Skip to main content

Webhooks

AWDPay sends real-time HTTP POST notifications to your callbackUrl whenever a deposit status changes.

Event Types

EventTrigger
deposit.pendingTransaction initiated
deposit.completedPayment successful
deposit.failedPayment failed
deposit.expiredTransaction 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:

  1. Respond with HTTP 200 within 10 seconds
  2. Validate signature (see below)
  3. 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.

Next Steps