Real-time transaction status notifications
Webhooks allow you to receive real-time notifications when a transaction status changes. When a transaction is completed (success or failure), we will send an HTTP POST request to your configured webhook URL.
Your webhook endpoint must return a 200 OK response within 10 seconds. Failed deliveries will be retried up to 3 times with a 30-second delay between attempts.
{
"status": "Success",
"message": "Payment completed successfully",
"customer_reference": "INV-123456789",
"internal_reference": "550e8400-e29b-41d4-a716-446655440000",
"provider_transaction_id": "987654321",
"msisdn": "+256777123456",
"amount": 50000,
"currency": "UGX",
"provider": "MTN",
"charge": 1500,
"completed_at": "2024-01-15 10:35:00"
}
Each webhook request includes a X-Webhook-Signature header.
X-Webhook-Signature: t=1705314900,v=5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
The signature is generated using HMAC-SHA256 with your webhook secret key:
string = webhook_url + timestamp + sorted_params sorted_params = status, customer_reference, internal_reference sorted alphabetically by key, with each key and value joined together directly
webhook_url: https://your-domain.com/webhook timestamp: 1781882665 status: Success customer_reference: INV-123 internal_reference: abc-456 Sorted params (alphabetical): customer_reference, internal_reference, status Final string: https://your-domain.com/webhook1781882665customer_referenceINV-123internal_referenceabc-456statusSuccess
const crypto = require('crypto'); function verifyWebhookSignature(payload, signatureHeader, webhookUrl, secret) { const [timestampPart, signaturePart] = signatureHeader.split(','); const timestamp = timestampPart.split('=')[1]; const receivedSignature = signaturePart.split('=')[1]; const params = { status: payload.status, customer_reference: payload.customer_reference, internal_reference: payload.internal_reference }; const sortedKeys = Object.keys(params).sort(); let stringToSign = webhookUrl + timestamp; for (const key of sortedKeys) { stringToSign += key + params[key]; } const expectedSignature = crypto .createHmac('sha256', secret) .update(stringToSign) .digest('hex'); return receivedSignature === expectedSignature; }