Stripe Webhook Handler Spec Builder

Document which Stripe events to handle and the logic for each

Builds a Stripe webhook handler specification with signature-verification order, an idempotency strategy keyed on event.id, per-event handler actions and payload fields, and the 200/400/5xx response contract.

Why must I verify the signature before parsing the body?

Stripe signs the exact raw request bytes. If your framework JSON-parses and re-serializes the body first, the bytes change and constructEvent fails. Always read the raw body, verify the Stripe-Signature header, then parse.

Specify a Stripe webhook handler that survives production

Stripe sends events to your endpoint to tell you when a payment succeeded, a subscription renewed, or an invoice failed. Getting that handler right means three things that are easy to forget: verifying the signature, deduplicating retried events, and returning the correct status code. This builder turns your chosen events into a precise specification covering all three.

How it works

The generated spec fixes the processing order, which matters because each step depends on the previous one. First you read the raw body and call stripe.webhooks.constructEvent(rawBody, signature, endpointSecret). This both authenticates the request (only Stripe knows the signing secret) and protects against replay via the timestamp in the signature. A failure here returns 400 and stops.

Next comes idempotency. Because Stripe may deliver the same event more than once, the handler checks event.id against a uniquely-keyed store before acting and skips anything already seen. Only then does it dispatch on event.type, run your per-event action, record the ID as processed, and return 200. For each common event the spec also lists the object type and the payload fields you will actually read, like amount_total on a checkout.session or attempt_count on a failed invoice.

Response contract and reliability

The status code is the entire contract with Stripe’s retry engine. A 200 means “handled — stop.” A 400 means “rejected — never retry,” which is correct for a bad signature. A 5xx or timeout means “try again,” and Stripe will retry with exponential backoff for up to three days. Because of retries and the lack of ordering guarantees, every handler should be idempotent and tolerant of out-of-order delivery. Keep handlers fast: return 200 immediately and push slow fulfilment work onto a background queue.