Skip to content
Money in Your Database

04.03 · Walkthrough

Idempotency, or You Will Charge Twice

Make a webhook handler safe to run twice on the same event, and explain why at-least-once delivery makes that a requirement rather than a precaution.

At-least-once webhook delivery means duplicate payment events are normal, not exceptional. A safe handler gives each event a stable key, lets the database enforce uniqueness atomically, and treats repeats as already handled. Without that, retries, crashes, or concurrent deliveries can create duplicate charges, shipments, or credits.

What this lesson answers

  • how to make payment webhooks idempotent
  • why webhooks can be delivered more than once
  • how unique constraints prevent duplicate payment processing

Notes

Webhook delivery is usually at-least-once, not exactly-once. That means the sender promises to keep trying until it believes you received the event, but it does not promise you will receive it only once. Network timeouts, retries, crashes after committing data, or a slow response can all make the same event arrive again. For payments, that turns a harmless retry into a serious bug if your handler creates another charge, ships another product, or credits an account twice.

Idempotency means running the same operation more than once has the same effect as running it once.

Common questions

Why can the same webhook arrive more than once?
Most webhook systems use at-least-once delivery. The sender retries until it believes your endpoint received the event. If your response is slow, lost, or happens after a crash, the sender may try again. That makes duplicate delivery a routine condition your handler must tolerate.
Is checking for an existing event before processing enough?
No. A read-before-write check can race. Two copies of the same event can both read that nothing exists, then both perform the side effect. The database needs to enforce a unique key atomically, usually through a unique constraint and an insert inside the same transaction boundary.
What should a webhook handler do with a duplicate event?
It should recognise the stable event or business key, avoid repeating the protected side effect, and return a successful response. The goal is for the retry path to converge on the same database state as the first successful handling, without another charge, shipment, or credit.