DEV Community

Syncoders
Syncoders

Posted on

Test mode passes, production double-charges: a Stripe webhook clip and a 4-step fix

A Stripe integration can pass every test-mode check and still go wrong with real customers. The reason is simple: in production Stripe may send the same event more than once, in any order, and it keeps retrying when your endpoint is slow or down. This short shows a basic handler and a hardened one getting the same traffic:

If you only change four things in your handler this week, make it these:

  1. Verify first, using the raw body. Check the Stripe-Signature header against the exact bytes Stripe sent. Parsing the JSON and re-serialising it breaks the signature.
  2. Remember every event you've handled. Insert event.id into a table with a unique key before doing any work. If the insert fails because the id already exists, it's a repeat delivery: return 200 and stop.
  3. Answer fast, work later. Save the event and return 200 within a second or two, then let a background job update the database and send emails. Slow handlers cause timeouts, timeouts cause retries, and retries pile up during a launch.
  4. Don't trust arrival order. When a subscription event arrives, re-read the subscription from Stripe (or compare the event's created time with what you stored) before overwriting the user's plan.

The full write-up, with diagrams and code for each failure, is here: 7 ways Stripe webhooks break in production. There's also a free Stripe webhooks go-live checklist.

Disclosure: the clip is for Webhook-Proof Stripe, a paid billing kit for Next.js and FastAPI from Syncoders Labs (us). Details are on its site.

Top comments (0)