Payment Confirmations
A payment isn't truly complete until the customer knows it succeeded. Automated payment confirmations — sent the instant a mobile money or card transaction settles — reduce disputes, build trust, and cut "did my payment go through?" support tickets. This guide shows how to send receipts over SMS, WhatsApp, and email using AfriRoute, driven by payment webhooks.
Scenario: Mobile Money Checkout Receipts
An e-commerce merchant in Kenya accepts M-Pesa. The moment a payment completes, the customer should receive an SMS receipt and, if they have WhatsApp, a richer confirmation with the order details.
The Flow
payment.completed webhook → send SMS receipt → send WhatsApp receipt (optional) → email receipt
Step 1: Listen for the Payment Webhook
AfriRoute Payments fires payment.completed when funds settle. Verify the signature, then send the receipt:
const crypto = require('crypto');
app.post('/webhooks/payment', express.json(), async (req, res) => {
// Verify HMAC signature
const sig = req.headers['x-afriroute-signature'];
const expected = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(JSON.stringify(req.body)).digest('hex');
if (sig !== expected) return res.status(401).send('bad signature');
const { event, data } = req.body;
if (event === 'payment.completed') {
await sendReceipt(data);
}
res.sendStatus(200);
});
Step 2: Send the SMS Receipt
async function sendReceipt(payment) {
const msg = `Payment received! ${payment.amount} ${payment.currency} for order ` +
`#${payment.metadata.order_id}. Ref: ${payment.provider_reference}. Thank you!`;
await fetch('https://api.afriroute.ai/api/v1/sms/send', {
method: 'POST',
headers: { 'Authorization': 'Bearer $AFRIROUTE_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
to: payment.customer.phone, // +254712345678
from: 'DukaShop', // registered transactional sender ID
message: msg,
category: 'transactional'
})
});
}
Step 3: Add a WhatsApp Receipt (Optional)
For a richer confirmation with line items and a receipt link:
def whatsapp_receipt(payment):
requests.post('https://api.afriroute.ai/api/v1/whatsapp/send',
headers={'Authorization': 'Bearer $AFRIROUTE_API_KEY'},
json={
'to': payment['customer']['phone'],
'template': 'payment_receipt',
'parameters': [
payment['amount'], payment['currency'],
payment['metadata']['order_id'],
f"https://dukashop.co.ke/receipts/{payment['payment_id']}"
]
})
Step 4: Idempotency
Webhooks can be redelivered. Use the payment ID as an idempotency key so a customer never gets two receipts:
if already_sent(payment['payment_id']):
return
mark_sent(payment['payment_id'])
send_receipt(payment)
Handling Failed Payments
Also confirm failures so customers can retry promptly:
if (event === 'payment.failed') {
await sendSMS(data.customer.phone,
`Payment for order #${data.metadata.order_id} didn't go through (${data.failure_reason}). ` +
`Please try again: https://dukashop.co.ke/pay/${data.metadata.order_id}`);
}
Compliance Notes
- Payment receipts are transactional — no marketing opt-in required, but store PII lawfully per Data Protection.
- Use a registered transactional sender ID for best delivery — see Sender ID Registration.
- Never include full card numbers or PINs; reference the provider transaction ID only.
Best Practices
- Send instantly on the
payment.completedwebhook — speed builds trust. - Always verify the webhook signature before acting.
- Be idempotent — one receipt per payment, even on webhook retries.
- Confirm failures too, with a retry link.
- Include a reference the customer and your support team can both look up.
Related
Last Updated: May 2026