Webhook

BackendTier 2 · build and shipPhase 4 · Take money

A reverse API call: instead of you asking a service repeatedly, it calls you when something happens.

Sometimes your system needs to know the instant something happens on another service, a payment cleared, a delivery moved, but constantly polling to ask is slow and wasteful. Asking are we there yet a thousand times is a poor design. A webhook exists so the other service calls you when the event happens, turning a constant poll into a single push.

A payment might take seconds to confirm. Rather than your app asking "done yet?" over and over, you give the gateway a URL, and it calls that URL the moment the payment settles. That inbound call is a webhook.

Webhooks power anything event-driven from a third party: payment confirmed, delivery status changed, subscription renewed. The other system pushes news to you instead of you polling for it.

A junior PM, stuck

A customer paid but her order sat on "pending" for about 40 seconds before flipping to confirmed. The dev said "the webhook came late" and pasted a payload I cannot read. I need to explain to support why paid orders can lag, and whether it is a bug.

A webhook is a call coming into TiffinBox from the payment gateway, the reverse of us asking them. Instead of our app polling "is it paid yet," we give the gateway a URL and it calls that URL the moment the payment settles. Reading one takes five minutes. Here is the actual inbound call for her order, with the header that tells you it is a retry, and our reply. That retry is your 40 seconds.

The gateway's inbound webhook to /api/webhooks/payment, and TiffinBox's acknowledgment
The gateway calls into TiffinBox
1.1
:
: 256410
: 2
{
"event": "payment.succeeded",
"order_id": "o_5512",
"amount": 649,
"currency": "BDT"
}
TiffinBox acknowledges it
200
{ "received": true }

Click a step to see the lines it points at.

Reading a late webhook as a payment failure. "Pending for 40 seconds" usually means a retry, shown by X-Webhook-Attempt; the money is fine, the news just arrived on the second try.
Designing the flow to confirm on the pay tap. The order is only truly paid when the webhook lands, so the UI has to handle the gap between tapping pay and the confirmation arriving.
Skipping the signature check. Without verifying X-Signature, any stranger can POST a fake payment.succeeded and mark an unpaid order confirmed.
Assuming each webhook arrives once. They can fire twice or out of order, so the handler must be idempotent; a replayed payment.succeeded on o_5512 must not confirm or bill it a second time.

Tell support: "She was not double-charged and nothing broke. The payment webhook came in on its second attempt, so the order sat pending for those seconds and then confirmed. We confirm on the webhook, not the pay tap, so a short lag on a retry is expected." You read the payload, found the retry marker, and explained the gap.

"How do we know when the payment actually clears?" is answered by a webhook; designing what happens when it arrives (and if it's late) is product work.
Delayed webhooks cause "I paid but the order still says pending" bugs; the flow needs to handle that gap.

"We update the order when the payment webhook lands, not when they click pay."

Appears in Phase 4, Take money.