ShipSafeFull checklist (23 checks)

Payments

Decide prices and access on the server

Create Stripe Checkout Sessions with server-side Price IDs and grant access from verified webhooks, not the success redirect.

Why it matters

Anything the browser sends can be edited in DevTools, including the amount. The success URL can also be opened without paying.

How to fix it

  1. Create the Checkout Session on the server using a Price ID you choose, not an amount from the request.
  2. Grant access when a verified webhook arrives (for example checkout.session.completed), not when the user lands on the success page.
  3. Store the Stripe customer and subscription IDs against your user.
const session = await stripe.checkout.sessions.create({
  mode: "subscription",
  line_items: [{ price: process.env.STRIPE_PRICE_PRO!, quantity: 1 }],
  client_reference_id: user.id,
  success_url: `${origin}/thanks`,
  cancel_url: `${origin}/pricing`,
});

How to verify

Related checks

Tick it off in the full checklist

Get new checks by email

New checks and real leak post-mortems, about once a month. No spam.