Prices sent from the browser to the checkout
What you'd see: Your create-checkout endpoint accepts amount, price or total in the request body.
§ 01 — What's actually happening
Anything the browser sends is user input, including numbers the user never typed. Editing a fetch call in DevTools takes seconds. If your server bills whatever arrives, the price on your pricing page is a suggestion.
Why an AI tool writes it this way
The cart total already exists in frontend state, so passing it to the server feels like reuse rather than duplication. Recalculating server-side looks wasteful until you notice the first order for $0.50.
What it costs you
Full-price products bought for pennies, with a real Stripe receipt and a real fulfilment email. It usually surfaces in reconciliation weeks later, after the goods have shipped.
§ 02 — Before & after
The customer decides what to pay
Illustrative code, written for this page — never a client's project.
AI-generated
- Amount taken from the request body
- Quantity and discounts also trusted
- A real receipt is issued, so it looks legitimate in your dashboard
Human-reviewed
- Server looks up the price from its own catalogue
- Client sends only what to buy, never how much it costs
- Coupons validated server-side against real, unexpired records
§ 03 — Check your own
How to tell in two minutes
You don't need us to run these. If any of them come back the wrong way, you have this problem.
- Open DevTools → Network, start a checkout, and look at the request payload. If you can see an amount, you have the bug.
- Confirm the server maps a plan identifier to a price it owns, rather than accepting
price_datafrom the client. - Check coupons and quantities too — a negative or huge quantity is the same class of bug.
- After payment, verify entitlement is granted by the webhook, not by the browser redirecting to
/success. That URL can simply be visited.
Found it in your project? Fixing this one properly usually takes an engineer under an hour. Finding the other nine takes longer — which is what the free health check is for.
Get my free health check →§ 04 — Related
Other things we find
Stripe webhook accepts unverified events
Your webhook route reads the request body and acts on it directly.
Read the fix → CRITICAL Database & dataSupabase Row Level Security is off, or has no policy
Your app works, and the Supabase dashboard shows "RLS disabled" or "No policies" next to your tables.
Read the fix → CRITICAL Exposed credentialsSecret keys shipped to the browser
You have an environment variable named something like NEXT_PUBLIC_OPENAI_API_KEY or VITE_STRIPE_SECRET_KEY.