Skip to content
Optimal Lab

System design · interactive

Receiving webhooks safely

A payment provider calls your endpoint when a payment succeeds. Webhooks are retried until you answer 200, so the same event can arrive twice. And your URL is public, so anyone can call it. Turn on the two defences.

  1. 0sproviderPOST evt_123 payment.succeeded (signed)
  2. 0syouVerify, credit the order, reply 200
  3. 10sproviderNo 200 within 10 s (you were slow), so it counts as failed and retries.
  4. 1mproviderretry 1: POST evt_123 again
  5. 1myouCredit the order AGAIN

Order credited 2× (should be once)

You processed it but answered too slowly. The provider retried, and you credited the order twice.

Scenario

Verify signature

Dedupe by event id

Step 2 of 4

  1. 1

    Verify the signature

    The provider signs the raw body with a shared secret (HMAC). Recompute it, compare in constant time, and check the timestamp.

    HMAC-SHA256(secret, timestamp + body)

  2. 2

    Reply 200 quickly, work later

    Store the event, return 200, and process it from a queue. Slow handlers cause timeouts and retries.

    Most providers time out in 5–30 s.

  3. 3

    Deduplicate by event id

    At-least-once delivery means duplicates. Record processed ids in the same transaction as the effect.

    Events can also arrive out of order.

  4. 4

    Reconcile

    If you were down longer than the retry window, fetch missed events from the provider's API.

    Webhooks are a hint; the API is the truth.

In production

Tools and technologies

Every payment and SaaS platform sends webhooks.

Not tied to one language — use these names when you search docs or design a system.