The verdict in three sentences
A payment webhook without HMAC signature verification accepts any request: an attacker can fake a confirmation. Without an idempotency key, the 15 to 30 % of webhooks delivered twice create accounting double-counting. Three defenses — signature, idempotency, replay queue — turn a weak spot into a reliable channel.
The three threats and their defenses
A webhook is a public URL: the provider posts an event to it (payment confirmed, refunded, failed). The danger is threefold: forgery, duplication, disorder. Here is how each threat is neutralized.
| Threat | Symptom | Defense | 2026 setting |
|---|---|---|---|
| Forgery | Fake payment injected | HMAC signature | 5-min window |
| Duplicate | Order counted twice | Idempotency key | 24 h TTL |
| Out of order | "Refunded" before "paid" | State machine | Versioned status |
| Network replay | Provider-side timeout | Queue + ack | Retry 3-5x |
| Bloated payload | Denial of service | Size limit | 256 KB max |
The HMAC signature is the foundation: the provider signs the request body with a shared secret. Your server recomputes the signature and compares. If it does not match, you reject with a 401. The 5-minute timestamp window blocks replay of old payloads.
Behavior per provider in 2026
Each provider signs and re-delivers differently. Adapt verification accordingly.
| Provider | Signature header | Algo | Replay if no ack |
|---|---|---|---|
| Wave | Wave-Signature | HMAC-SHA256 | Yes, backoff |
| Paystack | x-paystack-signature | HMAC-SHA512 | Yes, ~72 h |
| Flutterwave | verif-hash | Secret hash | Yes, up to 24 h |
| CinetPay | Token + x-token | HMAC | Yes, few tries |
| Stripe | Stripe-Signature | HMAC-SHA256 | Yes, up to 3 days |
Golden rule: respond 200 as fast as possible (ack), then process asynchronously via a queue. Heavy synchronous processing causes provider-side timeouts and therefore replays… therefore duplicates. That is where the idempotency key comes in: each event carries a unique ID; if you already processed it in the last 24 h, you ignore it.
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
Fatou runs an online store in Dakar receiving 900 Wave webhooks a month. Before hardening, she saw ~20 % duplicates, i.e. ~180 events that, without idempotency, inflated her reported revenue. On an average basket of 18,000 FCFA, that meant up to 3,240,000 FCFA of phantom revenue potentially per month. With the idempotency key (24 h TTL) and HMAC signature, double-counting drops to 0 and no fake payment gets through.
FAQ
Is the HMAC signature enough to secure everything? It blocks forgery, but not duplicates or out-of-order events. You must combine it with idempotency and an order-status state machine to be complete.
What happens if my server is down when the webhook fires? Providers replay: Paystack up to ~72 h, Stripe up to 3 days. As long as you do not return a 200, the event comes back. Hence the importance of acking fast then processing in a queue.
How long should I keep the idempotency key? A 24 h TTL covers almost all replays. Beyond that, you can log without reprocessing. Storing in cache (Redis) or a DB with a unique index does the job.
How do I test a webhook locally? Use a tunnel (ngrok, Cloudflare Tunnel) and the provider's test tools that replay signed events. Verify duplicate handling by resending the same payload twice.
What budget to make my webhooks reliable? As a 2026 order of magnitude, 300,000 to 900,000 FCFA depending on the number of providers and queue complexity. That is small against the risk of false revenue or fraud.
Let's talk about your project. We secure your payment webhooks with signature, idempotency and a replay queue. 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.
