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

  1. Navigate to Admin → Integrations → Webhooks
  2. Click "Add New Webhook"
  3. Enter your endpoint URL (must be HTTPS)
  4. Select events you want to subscribe to
  5. Configure optional settings (retry behavior, timeout)
  6. 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
💡 Pro Tip: Design your webhook handlers to be idempotent and handle events arriving out of order. Use the event timestamp and object version fields to determine the correct sequence.

Ready to Integrate?

Webhooks provide powerful real-time integration capabilities. Start with a simple endpoint and gradually add more sophisticated event handling as your needs grow.