A webhook endpoint is a public URL that anyone on the internet can POST to. Treat every incoming webhook as untrusted input until its signature is verified — this applies to payment webhooks (building on Payment System Basics@markdowners/payment-system-basics) and equally to any other provider's webhooks.
Signature verification
Always verify the provider's signature (HMAC or equivalent) on every incoming webhook before processing it, using the raw, unparsed request body — most signature schemes sign the exact byte sequence sent, and re-serializing a parsed JSON body before verifying will produce a different signature and false rejections (or worse, false acceptances if you skip verification instead of fixing this).
Never trust a webhook based on the request coming from an expected IP range alone, and never trust an unsigned webhook just because the payload "looks right" — treat signature verification as non-optional, not a nice-to-have.
Idempotency
Every webhook event has an ID; store processed event IDs and skip reprocessing on a duplicate delivery — providers explicitly document that the same event may be delivered more than once, and your handler must be safe to run twice with the same event.
Design the handler's side effects (updating an order, sending an email) to be idempotent themselves as a second layer of defense, not just relying on the dedup check.
Fast acknowledgment
Respond 200 quickly (parse, verify, enqueue for processing) rather than doing slow work (sending emails, calling other APIs) synchronously inside the webhook handler — most providers retry on timeout, and a slow handler causes duplicate deliveries and, eventually, the provider disabling your endpoint for being unreliable.
If heavier processing is required, verify and acknowledge first, then hand off to a background job/queue.
Ordering and consistency
Never assume webhooks arrive in the same order events occurred — a "payment succeeded" event can arrive before or after a related "order updated" event depending on provider retry timing. Design handlers to check current state before applying an update, not to assume a specific sequence.
Use the event's own timestamp/version, not arrival order, when a handler needs to determine which of two related events is authoritative.
Failure handling
Log every webhook receipt (event type, ID, verification result) even on failure, so a missed or misprocessed event is diagnosable after the fact rather than silently lost.
Return a non-2xx status only when you genuinely want the provider to retry — returning an error for a permanently unprocessable event (e.g. a malformed payload you can never fix by retrying) just wastes retries; log it and return 200 instead, then alert separately.
Local development
Use the provider's CLI/forwarding tool to receive real webhook events locally — don't rely on manually simulating payloads, which drifts from the real shape over time. See Stripe Integration@markdowners/stripe-integration for provider-specific detail.
Comments (0)
Sign in to comment. Sign in
No comments yet. Be the first to add one.