The verdict in three sentences
A payment webhook is never guaranteed unique or on time: between 3 and 8 % arrive duplicated or several minutes late, depending on the operator and network state. Without an idempotency key, every duplicate triggers a double order, a double shipment or a double credit. Four mechanisms — idempotency key, queue, HMAC signature and reconciliation polling — remove phantom orders and make checkout 99.9 % reliable.
Why webhooks duplicate or get lost
An operator that doesn't receive your HTTP 200 fast enough re-sends the webhook. If your server processed the event but replied too slowly, you receive the same payment two, three, sometimes five times. Here are the 2026 ballpark figures.
| Operator | Estimated duplicate rate | Average callback delay | Retry window |
|---|---|---|---|
| Wave | 2-4 % | 1-3 s | 5 attempts / 24h |
| Orange Money | 4-7 % | 3-15 s | 4-6 attempts / 24h |
| MTN MoMo | 3-6 % | 2-10 s | 5 attempts / 24h |
| Moov Money | 4-8 % | 5-20 s | 4 attempts / 12h |
| Stripe | 1-2 % | < 1 s | 3 days of retries |
The classic trap: processing the webhook synchronously. If sending an email or calling another service takes 6 seconds, the operator has already re-sent the event — and you have two orders.
The four mechanisms to put in place
The golden rule: reply 200 immediately, process afterwards. Here are the four building blocks and the problem each solves.
| Mechanism | Problem solved | Effect on phantom orders |
|---|---|---|
| Idempotency key (unique transaction ID) | Replayed duplicates | -95 % |
| Queue + immediate 200 response | Retries caused by slowness | -80 % of retries |
| Verified HMAC signature | Fake webhooks / injection | Blocks 100 % of fakes |
| Reconciliation polling every 15 min | Webhook never received | Recovers 100 % of lost ones |
Concretely: for each webhook, read the operator's transaction ID, check whether it's already in the database; if it exists, reply 200 and do nothing else. Otherwise record it, reply 200, and push processing into a queue. In parallel, a reconciliation job polls the operator API every 15 minutes to catch payments whose webhook never arrived.
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
Ibrahim runs an appliance shop in Abidjan: 1,200 orders a month. Before idempotency, about 5 % of duplicates created 60 phantom orders a month. Of those duplicates, around ten actually went out as double shipments, at 40,000 FCFA of goods lost each, i.e. 400,000 FCFA/month in losses, not counting support time.
After adding the idempotency key and the queue, phantom orders drop to 0. The reconciliation job also recovers 12 payments a month whose webhook had been lost, about 480,000 FCFA of sales that used to be stuck as "payment pending." Project cost: a one-time development of 400,000-700,000 FCFA, paid back in under two months.
FAQ
What is an idempotency key concretely? It's the unique transaction ID returned by the operator, stored in the database with a uniqueness constraint. If the same ID comes back, the duplicate is ignored. This is the mechanism that removes 95 % of phantom orders.
Why reply 200 before processing everything? Because the operator re-sends the webhook if it doesn't get a reply within seconds. By replying 200 immediately and processing in a queue, you remove up to 80 % of pointless retries.
Is the HMAC signature really necessary? Yes: without it, anyone who knows your URL can send you a fake "payment confirmed." HMAC verification blocks 100 % of these fake webhooks and costs only a few lines of code.
What if a webhook never arrives? A reconciliation polling job queries the operator API every 15 minutes and catches missing payments. On 1,200 orders it typically recovers around ten payments a month that would have stayed stuck.
How long should I keep the webhook history? At least 90 days, to trace any dispute and replay processing if needed. A timestamped log of received webhooks is your best evidence when a payment is contested.
Let's talk about your project. We make your Wave, Orange Money and Stripe webhooks idempotent, signed and reconciled — zero phantom orders. 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.
