Webhook Configuration & Security
Portal Configuration (Mandatory)
- Required: Configure your Webhook URL and Webhook Secret in the Merchant Portal (Integration → Webhooks)
- These settings serve as the default for all transactions
- The webhook secret is used to generate HMAC SHA256 signatures for webhook verification
- You must configure a webhook URL to receive payment notifications
Request-Level Override
- You can optionally provide a callback_url in individual API requests
- If callback_url is provided, it overrides the portal-configured webhook URL for that specific transaction
- If callback_url is not provided, the portal-configured webhook URL is used
- The webhook secret from the portal is always used for signature generation (if configured)
Security
- Validate incoming webhook requests by checking the source IP and/or using a shared secret
- Verify HMAC SHA256 signatures using the X-Webhook-Signature header (if webhook secret is configured)
- Handle duplicate webhook deliveries idempotently using reference or trace_number
Webhook Signature Verification
Important: Always verify webhook signatures to ensure the request is authentic and prevent replay attacks.
Steps to verify
- Extract the X-Webhook-Signature and X-Webhook-Timestamp headers from the incoming request
- Get the raw request body (JSON string) and sort the keys alphabetically
- Create a message string: {timestamp}.{sorted_payload_json}
- Compute HMAC SHA256: HMAC_SHA256(message, webhook_secret)
- Compare the computed signature with the X-Webhook-Signature header using constant-time comparison
- Validate the timestamp to prevent replay attacks (recommended: 5-minute tolerance)
Code Examples
Request
curl -X POST "https://your-server.com/webhook" \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: your_signature" \
-H "X-Webhook-Timestamp: 1234567890" \
-d '{
"event": "payment.completed",
"reference": "ABC123",
"amount": 1000,
"status": "SUCCESS"
}'Security Best Practices
- Always use constant-time comparison functions (hmac.compare_digest in Python, crypto.timingSafeEqual in Node.js) to prevent timing attacks
- Validate the timestamp to prevent replay attacks (recommended: reject requests older than 5 minutes)
- Store your webhook secret securely (environment variables, secret management service)
- Never log or expose the webhook secret
- Return 401 Unauthorized if signature verification fails