The verdict in three sentences
An order is paid only when a signed server webhook confirms it — never because the customer's browser showed a success page. The browser return can lie: network drop, closed tab, failed redirect, and you end up with a ghost order or a charged customer with no order. The 2026 rule: verify the signature, acknowledge within 5 s, retry on failure, and only mark "paid" on a confirmed event.
Why the browser is never authoritative
The payment flow has two channels: the redirect the customer sees, and the invisible webhook sent server to server. The first is fragile — it depends on the customer's network, phone and patience. The second is robust: the provider (Paystack, Wave) replays until your server answers 200.
If you validate the order on the redirect, two scenarios break everything. A customer who closes the tab after paying: money debited, order never created. A customer who reloads the success page without paying: order created, no money. The webhook solves both because it carries the real state on the provider side.
| Event to listen for | Meaning | Server action |
|---|---|---|
| charge.success | Debit confirmed (Paystack) | Mark order paid |
| checkout.session.completed | Wave payment succeeded | Trigger fulfilment |
| charge.failed | Payment declined | Show the cart again |
| refund.processed | Refund done | Deduct from revenue, notify |
| transfer.success | Vendor payout succeeded | Close the payout |
| chargeback.created | Dispute opened | Freeze the order |
Only one event matters to trigger fulfilment: the one that proves the debit. The others feed accounting and tracking.
The robust sequence: signature, acknowledge, retry
Three non-negotiable steps. First verify the HMAC signature of the header: it proves the call really comes from the provider and not an attacker faking a payment. Then answer 200 within 5 s — otherwise the provider considers delivery failed and replays. Finally handle business logic asynchronously so you never block the acknowledgement.
| Parameter | Recommended 2026 value | Consequence if ignored |
|---|---|---|
| Response timeout | Under 5 s | Provider replays the event |
| Expected response code | 200 only | 4xx/5xx = forced retry |
| Number of retries | Up to 5 over 24h | Event lost if server down |
| Signature verification | HMAC mandatory | Payment can be forged |
| Business processing | Asynchronous (queue) | Blocking, timeout, retry |
| Idempotency | transaction_id key | Double processing |
The trap: doing all the work (email, PDF, stock) before answering 200. If that work exceeds 5 s, the provider replays, and you process twice. Best practice: acknowledge first, push the task to a queue, process afterwards.
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
Modou launches a store in Lagos with Paystack. First week: on 500 orders, he counts 512 "paid" in his admin. Twelve too many. Auditing, he finds those 12 come from customers who reloaded the success page with no webhook behind it.
He switches to the "signed webhook only" rule. Result: the 12 ghosts disappear, and he even recovers 3 orders where the customer closed the tab but the charge.success webhook had arrived. Real total: 491 genuine orders collected, zero ghosts, zero charged customer without delivery. Weekly audit time drops from 2h to 15 min.
FAQ
What timeout for the acknowledgement? Answer 200 in under 5 s. Beyond that, most providers consider the call failed and replay it, which can trigger double processing if you have no idempotency.
How many times is a webhook replayed? Up to 5 attempts over 24h depending on the provider, with increasing delays. Your server must stay idempotent to absorb these replays without creating a duplicate.
How do you verify a webhook is authentic? Recompute the HMAC signature from the raw body and shared secret, then compare to the header. A mismatch = rejected request. Without this check, anyone can fake a payment.
What if my server was offline? The provider replays for 24h, but plan a catch-up polling that re-checks the status of pending orders every 15 min for lost cases.
Should you listen to every event? No: only the confirmed-debit event triggers fulfilment. The others (refund, dispute) feed accounting but must never validate an order.
Let's talk about your project. We integrate your Paystack or Wave webhooks with signature, retry and idempotency for zero ghost 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.
