The verdict in three sentences
A payment webhook will be replayed — MTN MoMo resends it up to 5 times over 24h until it gets a clean HTTP 200. Without an idempotency key, that replay creates a duplicate order in 2.3% of flows observed in Accra in 2026, wrecking your cash flow and your customer relationship. The right architecture: acknowledge fast, process in a queue, and deduplicate on the provider's transaction id.
Why a webhook gets replayed (and what it breaks)
Mobile money providers consider a callback delivered only if they receive a 200 OK within their window. If your server is too slow (heavy synchronous processing), times out, or returns a 500, they replay. Every replay that reprocesses the payment from scratch creates a duplicate: second order, second email, sometimes second shipment.
| Provider | Max retries | Window | Retry trigger |
|---|---|---|---|
| MTN MoMo | 5 | 24h | No 200 OK |
| Wave | 3 | 6h | Timeout > 10s or 5xx |
| Orange Money | 4 | 12h | 4xx/5xx or timeout |
| Flutterwave | 5 | 24h | Non-2xx |
| Stripe | up to ~15 | 72h | Exponential backoff |
The rule: your endpoint must reply 200 in under 2s, then do the real work asynchronously. Since Wave's median callback latency is 4s on the network side, every second of synchronous processing you add pushes you closer to the timeout.
Idempotency: transaction key vs DB lock
Two approaches combine. The idempotency key stores the provider's unique id (transactionId) in a table with a UNIQUE constraint; any re-insert fails silently and you return 200 without reprocessing. The DB lock (SELECT ... FOR UPDATE or advisory lock) guards against two replays processed in parallel to the millisecond.
| Mechanism | Protects against | Complexity | Monthly cost |
|---|---|---|---|
| UNIQUE table key | Sequential replay | Low | 0 FCFA |
| Postgres advisory lock | Concurrent replay | Medium | 0 FCFA |
| Managed Redis queue | Spike + reprocessing | Medium | ~15,000 FCFA |
| Synchronous processing | Nothing (anti-pattern) | — | 2.3% duplicate risk |
Target flow: the webhook validates the HMAC signature (5 min window), pushes the event into a Redis queue, returns 200. A worker consumes the queue, takes an advisory lock on transactionId, checks the idempotency key, and runs the business logic exactly once.
Need a professional website?
Kolonell builds websites that attract clients, optimized for the Sénégalese market. Free quote in 2 minutes.
Mini case study
Awa runs a cosmetics shop in Accra, roughly 40 orders/day via mobile money. Before the rework, her integration processed the webhook synchronously (send email + update stock, ~6s). Result: 2.3% duplicates, i.e. ~0.9 duplicated order/day, ~28/month. Each duplicate cost her an average refund of 8,000 FCFA plus 20 min of support: about 28 × 8,000 = 224,000 FCFA/month in refunds, not counting the time.
After moving to a Redis queue (15,000 FCFA/month) + idempotency key, duplicates drop to 0. Net saving: 224,000 − 15,000 = 209,000 FCFA/month, paying for itself on day one.
FAQ
Do I really need a Redis queue, or is a table enough? A table with a UNIQUE key is enough to deduplicate if your processing is fast. The queue becomes useful beyond ~30 orders/day or when processing (emails, stock, invoice) exceeds 2s; at 15,000 FCFA/month it also absorbs spikes.
Which id should I use as the idempotency key? Always the provider's transaction id (transactionId / financialTransactionId), never your internal reference. It is the only stable field across the original webhook and its up-to-5 replays.
What should I return when I detect an already-processed replay? A 200 OK. Returning an error would needlessly trigger the provider again. You confirm "received, already processed" without re-running the business logic.
How long should I keep idempotency keys? At minimum the longest retry window, i.e. 72h to cover Stripe. In practice keep them 30 days so they also serve daily reconciliation.
How does the Kolonell referral program work? You introduce a merchant with a payment problem, and on signature you earn 15% on a showcase site (+5% recurring), 12% e-commerce, 10% marketplace, 8% institutional. A single 2,000,000 FCFA e-commerce project earns you 240,000 FCFA.
Let's talk about your project. We audit your webhook integration and make it idempotent in a few days. 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.
