Collecting Mobile Money Payments
Mobile money is the dominant payment method across much of Africa. This guide covers collecting funds via M-Pesa, MTN MoMo, Airtel Money, and Telebirr, then confirming payment via polling or webhooks.
🚀 Quick Start
curl -X POST https://api.afriroute.ai/api/v1/payments/mobile-money/collect \
-H "Authorization: Bearer $AFRIROUTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "mpesa",
"phone": "+254712345678",
"amount": 1500,
"currency": "KES",
"reference": "ORDER-A1029",
"callback_url": "https://example.com/webhooks/payments"
}'
📡 Providers
| Provider | Code | Markets | Flow |
|---|---|---|---|
| M-Pesa | mpesa | 🇰🇪 🇹🇿 | STK push prompt |
| MTN MoMo | mtn | 🇬🇭 🇺🇬 🇨🇮 + | Approve prompt |
| Airtel Money | airtel | 🇺🇬 🇹🇿 🇿🇲 + | USSD/approve |
| Telebirr | telebirr | 🇪🇹 | App/USSD approve |
All collections are asynchronous: the customer approves a prompt on their phone, so the result arrives after your API call returns.
🔄 The Collection Flow
- Call
collect— triggers a prompt on the customer's phone. - Customer enters their PIN to approve.
- You receive the final result via webhook (or polling).
const res = await fetch('https://api.afriroute.ai/api/v1/payments/mobile-money/collect', {
method: 'POST',
headers: { 'Authorization': 'Bearer $AFRIROUTE_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
provider: 'mtn', phone: '+233244000000', amount: 50, currency: 'GHS',
reference: 'INV-7781', callback_url: 'https://example.com/webhooks/payments'
})
});
const { transaction_id } = await res.json(); // status: pending
🔔 Webhooks (Preferred)
Let the platform tell you when the customer approves or declines.
app.post('/webhooks/payments', (req, res) => {
const { transaction_id, status, reference } = req.body;
if (status === 'success') fulfillOrder(reference);
if (status === 'failed') notifyCustomer(reference);
res.sendStatus(200);
});
⏳ Status Polling (Fallback)
If you can't receive webhooks, poll the transaction with backoff until it settles.
import requests, time
def poll(txn_id, timeout=120):
deadline = time.time() + timeout
while time.time() < deadline:
r = requests.get(f'https://api.afriroute.ai/api/v1/payments/{txn_id}',
headers={'Authorization': 'Bearer $AFRIROUTE_API_KEY'})
status = r.json()['status']
if status in ('success', 'failed'):
return status
time.sleep(5)
return 'timeout'
💡 Best Practices
- Prefer webhooks; use polling only as a fallback.
- Treat the API response as
pending— never fulfill before confirmation. - Use a unique
referenceper order for reconciliation and idempotency. - Set a timeout (~2 min) for unapproved prompts and let the user retry.
- Validate the phone number's provider matches the chosen
provider. - Verify webhook signatures before trusting them.
📚 Related Resources
Last Updated: May 2026