The verdict in three sentences
In Nairobi, a non-idempotent M-Pesa Daraja callback is the top cause of ghost orders: Safaricom replays the callback up to 3 times and a shaky network double-credits around 1.2% of orders. Median callback latency is 8 seconds, but it climbs to 90 seconds at peak, so the webhook alone is never reliable. The right architecture: queue + PENDING/CONFIRMED status + nightly reconciliation via status query, never a dependency on the callback alone.
Why the webhook alone fails in Nairobi
The Daraja callback is a useful signal, not absolute truth. It can arrive twice, late, or never if your server is briefly unreachable. Each behaviour creates its own class of bug.
| Webhook behaviour | Frequency (order of magnitude) | Bug if unhandled |
|---|---|---|
| Callback replayed 2-3 times | ~15% of transactions | Double-credited order |
| Latency > 30 seconds | ~9% at peak | Customer retries / repays |
| Callback never received | ~2% | Paid but stuck order |
| Reversed order (fail then success) | ~1% | Wrong final status |
| Missing/invalid signature | rare | Fake payment accepted |
The fix is one word: idempotence. Each callback is processed once, keyed on the M-Pesa transactionId. A second identical callback is recognized and ignored.
The anti-ghost architecture in 2026
Never trust a single signal. Combine three layers: fast receipt, async processing, and a proactive status pull from Safaricom.
| Layer | Role | Key rule |
|---|---|---|
| Webhook receiver | Acknowledge in < 200 ms | Return 200, then process apart |
| Queue | Decouples receipt and logic | Retry with exponential backoff |
| Transactions table | Source of truth | Unique key on transactionId |
| Status query (pull) | Checks real state | After 2 min, then nightly |
| Nightly reconciliation | Catches lost callbacks | Compare PENDING vs M-Pesa status |
Status moves from PENDING to CONFIRMED only when Safaricom confirms, whether by webhook or status query. The 2% of never-received callbacks are caught by nightly reconciliation.
Mini case study
Wanjiru runs an online grocery in Nairobi, 900 orders/month, average basket KES 1,800. Without idempotence, 1.2% of orders are double-credited: about 11 orders/month, i.e. KES 19,800 credited in error that she must refund or lose in stock. She adds a unique key on transactionId and nightly reconciliation: double-credits fall to zero, and the 2% of paid-but-stuck orders (about 18/month, KES 32,400) finally clear automatically. Cost of the fix: 2 dev days.
Need a professional website?
Kolonell builds websites that attract clients, optimized for the Sénégalese market. Free quote in 2 minutes.
FAQ
Why does M-Pesa Daraja resend the same callback?
Safaricom treats a callback as undelivered until it receives a fast 200 from your server. On a shaky Nairobi network it replays up to 3 times, hence the need for idempotent processing keyed on the transactionId.
How do I guarantee idempotence in practice?
Add a unique constraint on transactionId in your transactions table. A second identical callback triggers a constraint violation you catch to cleanly ignore the duplicate.
Do I really need nightly reconciliation if webhooks work?
Yes. About 2% of callbacks never arrive at peak. Nightly reconciliation compares your PENDING orders against the real M-Pesa status and clears those that were actually paid.
What latency should I expect on the Daraja callback?
Median around 8 seconds, with spikes to 90 seconds at busy hours. Never show a failure to the customer before querying the status, or they will repay.
How do I stop a customer repaying an already-settled order?
Show a clear PENDING status with polling, and disable the pay button until the status resolves. Check the M-Pesa status before any payment retry.
Let's talk about your project. We audit and fix your M-Pesa Daraja integration to eliminate ghost payments in Nairobi. WhatsApp +221 77 596 93 33.
Mohamed Bah
Fondateur, Kolonell
Passionate about digital and entrepreneurship in Africa, Mohamed has been helping Sénégalese businesses with their digital transformation since 2020. Founder of Kolonell, he believes every SME deserves a professional and accessible online présence.

