---
name: add-stripe-checkout
description: Adds Stripe Checkout for one-off payments or subscriptions with a server route that creates the session and a verified webhook that fulfils the order. Use when the user wants to take payments, charge cards, sell a product, add a pricing page, subscriptions, or a buy button.
license: CC-BY-4.0
compatibility: Requires a Stripe account (test mode is fine)
metadata:
  category: backend
  tags: "stripe,payments,nextjs,webhooks"
  author: johnny-bravo
  version: "1.0"
  updated: "2026-10-30"
  status: published
---

# Add Stripe Checkout

## When to use
- Selling something without building a custom card form.

## Steps
1. Create the Product and Price in the Stripe dashboard (test mode); copy the `price_...` ID.
2. `npm i stripe`; set `STRIPE_SECRET_KEY` and `STRIPE_WEBHOOK_SECRET` server-side only.
3. Server route `POST /api/checkout`: `stripe.checkout.sessions.create({ mode: "payment" | "subscription", line_items: [{ price, quantity: 1 }], success_url, cancel_url, client_reference_id: userId })`, return `session.url`, redirect the browser to it.
4. Webhook route `POST /api/stripe/webhook`: read the **raw** body, `stripe.webhooks.constructEvent(raw, sig, secret)`, handle `checkout.session.completed` (and `customer.subscription.*` for subs). Fulfil here, not on the success page.
5. Make fulfilment idempotent: store `event.id` or `session.id` and skip duplicates.
6. Test locally: `stripe listen --forward-to localhost:3000/api/stripe/webhook` and `stripe trigger checkout.session.completed`. Card `4242 4242 4242 4242`.

## Gotchas
- Parsing the body as JSON before verifying breaks the signature check.
- Prices come from Stripe, never from the client request.
- The success page can be reached without paying; never grant access there.

## Example prompt
"Add a $29 buy button that unlocks the pro features after payment."
