Webhooks
AWDPay sends real-time HTTP POST notifications to your callbackUrl whenever a deposit status changes.
Event Types
| Event | Trigger |
|---|---|
deposit.pending | Transaction initiated |
deposit.completed | Payment successful |
deposit.failed | Payment failed |
deposit.expired | Transaction expired |
Webhook Payload
{
"event": "deposit.completed",
"reference": "DEP_TEST_1763646304918_1D05265E",
"status": "completed",
"amount": 10000.0,
"currency": "XOF",
"fees": 104.0,
"gatewayName": "wave-senegal",
"customerEmail": "customer@example.com",
"customerPhone": "+221701234567",
"customerName": "Awa Diop",
"createdAt": "2025-11-20T14:45:04",
"completedAt": "2025-11-20T14:47:32",
"metadata": {
"order_id": "ORDER_123",
"description": "Premium subscription"
},
"signature": "f8a9b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8"
}
Endpoint Requirements
Your webhook endpoint must:
- Respond with HTTP 200 within 10 seconds
- Validate signature (see below)
- Be idempotent: handle duplicate notifications gracefully
Example: Node.js/Express
const express = require('express');
const crypto = require('crypto');
const app = express();
app.post('/webhooks/deposit', express.json(), (req, res) => {
const payload = req.body;
const receivedSignature = payload.signature;
// Validate signature
const expectedSignature = crypto
.createHmac('sha256', process.env.AWDPAY_WEBHOOK_SECRET)
.update(JSON.stringify({
reference: payload.reference,
status: payload.status,
amount: payload.amount
}))
.digest('hex');
if (receivedSignature !== expectedSignature) {
return res.status(401).send('Invalid signature');
}
// Process event
console.log(`Deposit ${payload.reference} is ${payload.status}`);
// Update your database
updateOrderStatus(payload.metadata.order_id, payload.status);
// Respond immediately
res.status(200).send('OK');
});
app.listen(3000);
Example: Python/Flask
import hmac
import hashlib
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhooks/deposit', methods=['POST'])
def webhook():
payload = request.json
received_signature = payload.get('signature')
# Validate signature
signature_data = f"{payload['reference']}{payload['status']}{payload['amount']}"
expected_signature = hmac.new(
WEBHOOK_SECRET.encode(),
signature_data.encode(),
hashlib.sha256
).hexdigest()
if received_signature != expected_signature:
return 'Invalid signature', 401
# Process event
print(f"Deposit {payload['reference']} is {payload['status']}")
update_order_status(payload['metadata']['order_id'], payload['status'])
return 'OK', 200
if __name__ == '__main__':
app.run(port=3000)
Signature Verification
AWDPay signs each webhook with HMAC-SHA256 using your webhook secret (available in Dashboard).
Signature Algorithm
signature = HMAC_SHA256(
webhook_secret,
reference + status + amount
)
Security
Always verify signatures before processing webhooks to prevent spoofed requests.
Retry Policy
- Failed webhooks (non-200 response or timeout) are retried with exponential backoff:
- 1st retry: 5 minutes
- 2nd retry: 30 minutes
- 3rd retry: 2 hours
- 4th retry: 6 hours
- After 4 failed attempts, notifications stop. Check Dashboard → Webhooks for logs.
Testing in Sandbox
Use tools like ngrok or localhost.run to expose your local server:
ngrok http 3000
# Use the generated URL (e.g., https://abc123.ngrok.io/webhooks/deposit) as callbackUrl
Testing Tip
In sandbox, manually trigger webhook resends from the Dashboard for testing purposes.