The verdict in three sentences
Payment providers replay every webhook 3 to 5 times over 24 hours to guarantee delivery; without protection this creates 0.3 to 0.8 % duplicate charges. The fix is two measures: a unique idempotency key in the database and a 24-hour deduplication window. Done right, you push duplicates below 0.02 % and recover the refund cost — roughly 1.5 % lost on every duplicated transaction.
Why webhooks replay
A webhook is an "at least once" delivery promise, never "exactly once." If your server responds slowly or returns a 5xx error, the provider retries. The M-Pesa Daraja STK callback times out in 30 seconds: if your processing exceeds that, Safaricom treats the call as failed and replays it — even if you already collected the money.
| Provider | Retry count | Retry window | Signature check |
|---|---|---|---|
| M-Pesa Daraja | 3 to 4 | ~24 h | IP allowlist |
| Paystack | 3 to 5 | 72 h | HMAC SHA512 |
| Flutterwave | 3 to 5 | 24 h | secret hash |
| Wave | 3 to 5 | 24 h | signature header |
| Stripe | up to ~3 days | 72 h | signing secret |
The golden rule: return 200 immediately, then process in the background. A fast acknowledgement cuts half of the pointless replays.
The anti-duplicate architecture
The core of the system is a database unique constraint. You store the idempotency key (provider reference or event ID) and reject any duplicate insert.
| Measure | No protection | With idempotency 2026 |
|---|---|---|
| Duplicate rate | 0.3 to 0.8 % | < 0.02 % |
| DB unique constraint | absent | mandatory |
| Dedup window | none | 24 h minimum |
| Signature verification | optional | systematic |
| Cost per duplicate | ~1.5 % fee + refund | 0 |
Two checks are non-negotiable in 2026: validate the signature (HMAC or secret hash) to reject fake webhooks, and make processing idempotent so 5 deliveries of the same event produce exactly one booked charge.
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
Wanjiru runs an airtime top-up platform in Nairobi handling around 4,500 payments a day. Without an idempotency key she suffered 0.5 % double charges — roughly 22 customers a day to refund. Each refund cost her the collection fee (1.5 %) plus an hour of support. After adding a unique constraint on the event ID and a 24-hour window, duplicates fell to fewer than 1 a day. Over 30 days she avoided about 630 refunds and won back community trust — no more "double debit" screenshots on social media.
FAQ
What is an idempotency key exactly? A unique identifier attached to an operation: if the same identifier returns, the system replays the already-computed result instead of redoing the action. For a payment, use the provider's event ID.
Why a 24-hour window and not longer? Because most providers stop replaying after 24 to 72 hours. A 24-hour window covers M-Pesa, Flutterwave and Wave; extend to 72 hours for Paystack and Stripe.
Do I really need to verify the signature? Yes. Without HMAC verification, anyone who knows your URL can fake a payment. The signature proves the event genuinely came from the provider.
What duplicate rate should I target in 2026? Below 0.02 %. Above 0.1 %, there is almost always a missing unique constraint or synchronous processing so slow it triggers replays.
Is the Daraja 30-second timeout a problem? Only if you process everything inside the webhook. Return 200 at once, queue the event, and the timeout never fires.
Let's talk about your project. We can audit your webhooks and harden idempotency before your next traffic spike. 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.
