Price or plan taken from the client
What it looks like
// POST /api/checkout. Body comes from the browser.
const { amount, plan } = await req.json();
const session = await stripe.checkout.sessions.create({
mode: "payment",
line_items: [{ price_data: { currency: "usd", unit_amount: amount,
product_data: { name: plan } }, quantity: 1 }],
});What happens
A buyer can edit the request in their browser, pay one cent for the top plan, and your app records it as a full purchase.
Where it shows up
Descriptive, not a verdict: builders and stacks whose defaults make this reachable, or that a public incident names. Each hub states the tool's own security features.
Seen in public
Two days after announcing a SaaS built with Cursor and no hand-written code, its founder reported subscription bypasses, maxed-out API keys and unwanted database writes; he shut the app down three days later.
- 2025-03-15 Launch announcement
- 2025-03-17 Report of subscription bypass and API key abuse
- 2025-03-20 Shutdown announcement
Mar 2025 · Leo (Enrichlead founder) · all sources
Check it yourself
Uses your builder's dashboard and your browser. No tools to install.
- Open your app's pricing page in Chrome, press F12 to open developer tools (the browser's built-in inspector), and select the Network tab.
- Click a buy or subscribe button and find the request your app sends before Stripe's page opens. It is often named checkout, create-checkout-session or similar.
- Select it and read the Payload or Request tab.
- Bad result: the payload contains an amount, a price in cents or a plan name that the server appears to use as sent. A good result sends only a plan key or a Stripe price ID starting
price_, and the server chooses the amount.
What we check
We replay the checkout request with altered amounts, plan names and price IDs, complete the session in test mode, and read back which plan your database granted and what Stripe recorded.
Fix direction
Keep prices in Stripe as Price objects and let the server map a plan key from the browser to a fixed price_ ID, treating any amount from the client as untrusted. After payment, grant the plan from the verified webhook event's line items rather than from what the browser claimed.
Mechanism documented in: How Checkout works (verified 2026-09-21); Create a Checkout Session (verified 2026-09-21).
Covered by
Ready-to-charge check ($390, 48 hours) and Full audit ($1,900, 5 business days)
Both include a re-check after your fixes.
Related patterns
- Subscription state trusted from local storagePaymentsHigh
Anyone can type one line in the browser console to become a paying customer, and the server hands over the paid feature without checking.
- Stripe webhook accepted without signature verificationPaymentsCritical
Anyone who finds the webhook URL can post a made-up payment-succeeded event and get an order fulfilled or a plan activated for a user of their choosing, without paying.
- Authorization enforced only in the browserLogin and permissionsCritical
Anyone who calls the API directly, or edits the page in developer tools, gets the data and actions the screen was hiding.
- Record ids accepted without an ownership checkData accessCritical
Anyone who changes the id in a URL or request can read, edit or delete another customer's record.
Questions
price taken from client stripe checkout
Stripe lets the server create a Checkout Session either from a stored Price ID or from inline price_data. When the inline amount comes straight from the browser request, anyone can change it before sending. The check is to inspect the checkout request in developer tools and see whether an amount travels with it.
can users change the price in stripe checkout
Not on Stripe's hosted page, but they can change what your server asks Stripe to charge if your server copies the amount from the request. The founder of one SaaS built with Cursor reported subscription bypasses two days after launch (Leo (Enrichlead founder), 2025); replaying your own checkout request with a different amount is the quickest way to know where you stand.
how do i stop users editing the plan before checkout
Send only a plan key such as pro-monthly from the browser, look up the matching Stripe Price ID on the server, and ignore any amount or name in the request. Then activate the plan from the verified webhook, using the line items Stripe reports rather than the plan the browser said it bought.
Back to the catalogue, or run the self-check for your stack.