DEV Community

SaaSFactory
SaaSFactory

Posted on

7 things that break Stripe integrations in production (checklist)

Why this matters

I've reviewed enough Stripe integrations to notice the same handful of mistakes over and over. None of them are exotic — they're the kind of thing that passes code review and works fine in test mode, then causes a real incident (double charges, unverified webhooks, a leaked secret key) once the integration goes live.

Here are 7 concrete checks, with the actual pattern to grep for.

1. Verify the webhook signature

Your webhook handler must call constructEvent / construct_event using the raw request body and the Stripe-Signature header. If you're doing json.loads(request.data) or JSON.parse(req.body) directly on a webhook route, anyone who finds the URL can forge events.

2. Never parse the event before verifying it

Related to #1: don't build any logic on the parsed JSON body until constructEvent has succeeded. If verification fails, reject the request — don't fall back to trusting the payload.

3. No secret key hardcoded in source

Grep for sk_live_, sk_test_, rk_live_ in your repo. It happens more than you'd think, usually in a "quick test" that gets committed by accident. Rotate immediately if you find one in history, not just in the current tree.

4. Use an idempotency key on creation calls

PaymentIntent.create, Charge.create, Refund.create, Subscription.create — any of these retried by a flaky network can create a duplicate charge or refund if you don't pass idempotency_key. This is the single most common cause of "why did the customer get charged twice" tickets.

5. Don't use the legacy Charges/Sources API

If you see Charge.create or Source.create in a codebase started in the last few years, it's worth checking whether it should be PaymentIntents/Checkout instead — the legacy API lacks SCA/3DS support that most markets now require.

6. Never take the amount from client input

amount = request.body.amount on the server is a straight path to a customer paying whatever they want. The amount must come from your own price/catalog lookup, server-side, always.

7. Deduplicate webhook events

Stripe can and will deliver the same event more than once. Before acting on event.id, check whether you've already processed it (a small "seen events" table or unique constraint is enough). Without this, a single retried webhook can trigger your fulfillment logic twice.


I turned this into a 2-page PDF + a CSV with the exact regex pattern for each rule (so you can run it as a quick grep pass over your own repo). $9, instant download, no login required: https://buy.stripe.com/14A14m48RgWC9ij7tu7IY03

Happy to discuss edge cases or additions to the list in the comments.

Top comments (0)