Webhooks
Webhooks
Webhooks are HTTP callbacks Aurax Pay sends to your server when a payment completes, fails, or changes state. Use them together with polling for reliable payment confirmation.
Register your endpoint via the APIs & Secrets page in your dashboard, or call
POST /merchant/webhooks with your JWT token. The signing secret is shown only once in the response โ copy it immediately and store as AURAXPAY_WEBHOOK_SECRET.Register an endpoint
POST /merchant/webhooks
curl -X POST https://api.auraxpay.net/merchant/webhooks \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -d '{ "url": "https://yourserver.com/webhooks/aurax", "events": ["payment.completed", "payment.failed", "payment.pending"] }'
Response -- secret shown once
{
"success": true,
"endpoint": {
"id": "wh_xxxxxxxxxxxxxxxx",
"url": "https://yourserver.com/webhooks/aurax",
"events": ["payment.completed", "payment.failed", "payment.pending"],
"isActive": true,
"secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
"message": "Save this secret now -- it will never be shown again."
}Webhooks and polling
Webhook delivery is best-effort. Always implement polling as a fallback โ payments typically complete within 30 seconds:
| Method | Speed | Use for |
|---|---|---|
| Webhook | Varies, within 30s | Primary โ background fulfillment |
| Polling | 30s max | Fallback โ user-facing status updates |
Polling fallback -- Node.js
"token-keyword">async "token-keyword">function waitForPayment(reference, timeoutMs = 60000) { "token-keyword">const start = Date.now() while (Date.now() - start < timeoutMs) { "token-keyword">const res = "token-keyword">await fetch("token-string">'https://api.auraxpay.net/v1/payments/' + reference, { headers: { "token-string">'x-api-key': process.env.AURAXPAY_API_KEY }, }) "token-keyword">const { transaction } = "token-keyword">await res.json() "token-keyword">if (transaction.status === "token-string">'COMPLETED') "token-keyword">return transaction "token-keyword">if (transaction.status === "token-string">'FAILED') "token-keyword">throw "token-keyword">new Error("token-string">'Payment failed') "token-keyword">await "token-keyword">new Promise(r => setTimeout(r, 3000)) // poll every 3s } "token-keyword">throw "token-keyword">new Error("token-string">'Payment timed out') }
How delivery works
1. Customer approves USSD prompt on their phone
2. Mobile network confirms payment to Aurax Pay
3. Aurax Pay sends a POST to your registered URL with
X-Aurax-Signature4. Your server verifies the signature
5. Your server responds with 200 OK within 10s
Delivery and retries
Aurax Pay expects a 2xx response within 10 seconds. If your endpoint fails or times out, we retry with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 12 hours |
Webhook headers
Every webhook request includes these headers:
| Header | Description |
|---|---|
| X-Aurax-Signature | HMAC-SHA256 signature of the raw request body. Use this to verify authenticity. |
| X-Aurax-Event | The event type. E.g. payment.completed |
| X-Aurax-Delivery | Unique ID for this delivery attempt. Useful for deduplication. |
| Content-Type | Always application/json |
Respond quickly, process async
Return a 200 response immediately, then process the webhook in the background. If your handler exceeds 10 seconds, Aurax Pay considers the delivery failed and retries.
Node.js / Express โ recommended pattern
app.post("token-string">'/webhooks/aurax', express.raw({ type: "token-string">'application/json' }), "token-keyword">async (req, res) => { // 1. Verify signature FIRST โ reject anything unsigned "token-keyword">const signature = req.headers["token-string">'x-aurax-signature'] "token-keyword">const isValid = verifyAuraxSignature(req.body, signature, process.env.AURAX_WEBHOOK_SECRET) "token-keyword">if (!isValid) "token-keyword">return res.status(400).json({ error: "token-string">'Invalid signature' }) // 2. Respond 200 immediately res.status(200).json({ received: true }) // 3. Process async โ outside the request cycle "token-keyword">const event = JSON.parse(req.body) "token-keyword">await processWebhookEvent(event) }) "token-keyword">async "token-keyword">function processWebhookEvent(event) { "token-keyword">switch (event.event) { "token-keyword">case "token-string">'payment.completed': "token-keyword">await fulfillOrder(event.transaction.metadata.orderId) "token-keyword">break "token-keyword">case "token-string">'payment.failed': "token-keyword">await notifyCustomer(event.transaction) "token-keyword">break "token-keyword">case "token-string">'payout.completed': "token-keyword">await markPayoutSent(event.transaction.id) "token-keyword">break } }