Webhooks Integration
Learn about webhook functionality in LocallyGrown. Current webhook support includes payment processing and email delivery integrations.
Webhook Overview
LocallyGrown uses webhooks internally for payment processing and email delivery services. These webhooks handle events from Stripe (payments) and Mailgun (email delivery) to keep the system synchronized.
💳 Payment Processing
Stripe webhooks handle payment confirmations, failures, and refund notifications to keep payment status synchronized.
📧 Email Delivery
Mailgun webhooks track email delivery status, bounces, and engagement metrics for better communication reliability.
🔒 Secure Processing
All webhook endpoints are secured and validate requests to ensure authentic delivery.
🔧 System Integration
Internal webhooks keep LocallyGrown synchronized with external service providers.
Available Webhook Events
📦 Order Events
- order.created - New order placed by customer
- order.updated - Order details modified
- order.completed - Order marked as fulfilled
- order.cancelled - Order cancelled by customer or admin
- order.payment_processed - Payment successfully charged
👤 User Events
- user.created - New user account registered
- user.updated - User profile information changed
- user.deleted - User account removed
- user.role_changed - User permissions modified
🛒 Product Events
- product.created - New product added by grower
- product.updated - Product details or availability changed
- product.deleted - Product removed from catalog
- inventory.updated - Product quantity changed
💳 Payment Events
- payment.succeeded - Payment processed successfully
- payment.failed - Payment attempt declined
- payment.refunded - Refund issued to customer
- balance.updated - User account balance changed
Setting Up Webhooks
Configuration Requirements
- HTTPS endpoint - Your webhook URL must use SSL/TLS encryption
- Public accessibility - Endpoint must be reachable from the internet
- Quick response - Respond within 10 seconds to avoid timeouts
- Idempotency handling - Handle duplicate delivery gracefully
Adding Webhook Endpoints
- Navigate to Admin → Integrations → Webhooks
- Click "Add New Webhook"
- Enter your endpoint URL (must be HTTPS)
- Select events you want to subscribe to
- Configure optional settings (retry behavior, timeout)
- Save and test the webhook connection
Example Webhook Configuration
{
"url": "https://your-app.com/webhooks/locallygrown",
"events": [
"order.created",
"order.completed",
"payment.succeeded"
],
"active": true,
"secret": "your-webhook-secret-key"
}
Webhook Payload Structure
Common Payload Format
All webhooks follow a consistent JSON structure:
{
"id": "evt_1234567890",
"type": "order.created",
"created": 1640995200,
"data": {
"object": {
// Event-specific data
}
},
"market_id": "market_123",
"api_version": "2024-01-01"
}
Order Event Example
{
"id": "evt_order_created_123",
"type": "order.created",
"created": 1640995200,
"data": {
"object": {
"id": "order_456",
"customer_id": "user_789",
"status": "pending",
"total": 4250,
"currency": "usd",
"items": [
{
"product_id": "prod_123",
"name": "Organic Tomatoes",
"quantity": 2,
"price": 1200,
"grower_id": "grower_456"
}
],
"pickup_location": "Downtown Farmers Market",
"pickup_date": "2024-01-15",
"created_at": "2024-01-10T10:30:00Z"
}
},
"market_id": "market_123",
"api_version": "2024-01-01"
}
Webhook Security
Signature Verification
All webhooks include a signature header for verification:
X-LocallyGrown-Signature: sha256=<signature>
Verifying Webhook Signatures
Node.js Example
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return signature === `sha256=${expectedSignature}`;
}
// Express.js middleware
app.post('/webhook', (req, res) => {
const signature = req.headers['x-locallygrown-signature'];
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
process.env.WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Process webhook
res.status(200).send('OK');
});
Python Example
import hmac
import hashlib
def verify_webhook_signature(payload, signature, secret):
expected_signature = hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
return signature == f'sha256={expected_signature}'
# Flask example
@app.route('/webhook', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-LocallyGrown-Signature')
payload = request.get_data(as_text=True)
if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401
# Process webhook
return 'OK', 200
Handling Webhook Events
Best Practices
- Verify signatures - Always validate webhook authenticity
- Handle idempotency - Use event IDs to prevent duplicate processing
- Respond quickly - Return 200 status within 10 seconds
- Process asynchronously - Queue heavy processing for later
- Log events - Keep records for debugging and audit purposes
Error Handling
- Return appropriate status codes - 200 for success, 4xx/5xx for errors
- Implement retry logic - Handle temporary failures gracefully
- Monitor webhook health - Track delivery success rates
- Handle missing events - Use API polling as backup when needed
Complete Webhook Handler Example
// Express.js webhook handler
app.post('/webhooks/locallygrown', express.json(), async (req, res) => {
try {
// Verify signature
const signature = req.headers['x-locallygrown-signature'];
if (!verifySignature(req.body, signature)) {
return res.status(401).send('Invalid signature');
}
const { type, data, id } = req.body;
// Check for duplicate events
if (await isEventProcessed(id)) {
return res.status(200).send('Event already processed');
}
// Handle different event types
switch (type) {
case 'order.created':
await handleOrderCreated(data.object);
break;
case 'payment.succeeded':
await handlePaymentSucceeded(data.object);
break;
default:
console.log(`Unhandled event type: ${type}`);
}
// Mark event as processed
await markEventProcessed(id);
res.status(200).send('OK');
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).send('Internal server error');
}
});
Testing Webhooks
Development Tools
- ngrok - Expose local development server to internet
- webhook.site - Receive and inspect webhook payloads
- RequestBin - Capture and analyze HTTP requests
- Postman - Test webhook endpoints manually
Testing Checklist
- Signature verification - Ensure your endpoint validates signatures
- Response timing - Verify responses within timeout limits
- Error handling - Test various error scenarios
- Idempotency - Send duplicate events to test handling
- Event filtering - Confirm only subscribed events are received
Development Setup Tips
- Use ngrok to tunnel webhooks to localhost during development
- Set up separate webhook endpoints for staging and production
- Log all incoming webhooks for debugging
- Test with actual LocallyGrown test events
- Implement health check endpoints for monitoring
Monitoring and Troubleshooting
Webhook Delivery Status
- Delivery attempts - Monitor success and failure rates
- Response times - Track endpoint performance
- Error patterns - Identify common failure causes
- Retry behavior - Understand automatic retry logic
Common Issues and Solutions
Webhooks not being delivered
- Verify endpoint URL is accessible via HTTPS
- Check firewall and security group settings
- Ensure endpoint responds with 200 status code
- Validate webhook configuration in admin panel
Duplicate events received
- Implement idempotency using event IDs
- Check for retry logic causing duplicates
- Ensure endpoint responds quickly (under 10 seconds)
- Use database constraints to prevent duplicate processing
Signature verification failing
- Verify webhook secret key is correct
- Check payload encoding (UTF-8 expected)
- Ensure complete request body is used for signature
- Test signature generation locally
Rate Limits and Reliability
Delivery Guarantees
- At-least-once delivery - Events may be delivered multiple times
- Retry mechanism - Failed deliveries retried with exponential backoff
- Maximum retries - Up to 5 retry attempts over 24 hours
- Event ordering - Events may arrive out of order
Rate Limits
- Webhook deliveries - Up to 100 per minute per endpoint
- Event types - No limit on number of event types
- Payload size - Maximum 1MB per webhook
- Timeout - 10 second response timeout