What you're doing and why it's painful
A Stripe subscription requires a Product and a Price before you can attach it to a Checkout session. The Product represents what you're selling ("Pro plan"), the Price defines the billing interval and amount ($29/month, $290/year). Without both set up correctly, you can't create a subscription or embed a Stripe Checkout for recurring billing.
The pain: the dashboard flow buries pricing configuration under the Product, trial periods are easy to misconfigure, and the difference between a one-time Price and a recurring Price is a single toggle — easy to get wrong and annoying to debug later.
Prerequisites
- A Stripe account
- Access to the Stripe Dashboard (or the Stripe CLI if you prefer)
Step 1 — Create a Product
- Log in to the Stripe Dashboard
- Go to Product catalog in the left sidebar
- Click + Add product
- Fill in:
- Name: what your users will see on invoices — e.g.
Pro Plan - Description (optional): shown in Stripe-hosted Checkout
- Image (optional): your product logo for Checkout
- Name: what your users will see on invoices — e.g.
- Leave Pricing for the next step
- Click Save product
Step 2 — Add a recurring Price
After saving the product, you're on the product detail page. Under Pricing, click Add a price.
- Set Pricing model to Standard pricing
- Set Price — e.g.
29.00 - Set Currency — e.g.
USD - Under Billing period, choose Monthly (or Yearly, Weekly, etc.)
- Make sure Recurring is selected (not One time)
- Click Save price
Stripe will generate a Price ID like price_1ABC.... Copy it — you'll need it in your code.
Step 3 — (Optional) Add an annual price
If you want to offer annual billing at a discount, add a second Price to the same Product:
- Click Add a price again on the same product
- Set the annual amount — e.g.
290.00for $290/year (saves ~17%) - Set Billing period to Yearly
- Save
Now you have two Price IDs — one monthly, one annual — both attached to the same Product.
Step 4 — Add a free trial (optional)
To add a trial period to a Price:
- Click Edit on the Price
- Scroll to Trial period days
- Enter the trial length — e.g.
14 - Save
Users who subscribe via this Price will get 14 days free before their card is charged.
Step 5 — Use the Price ID in Checkout
In your server code, pass the Price ID when creating a Checkout session:
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
payment_method_types: ['card'],
line_items: [
{
price: 'price_1ABC...', // your Price ID
quantity: 1,
},
],
success_url: 'https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://yourdomain.com/pricing',
});
// Redirect user to session.url
Set mode: 'subscription' — not 'payment'. Using the wrong mode will cause the Checkout session to fail.
Step 6 — Verify in test mode first
Before going live, test the full flow with Stripe's test card:
- Card:
4242 4242 4242 4242 - Expiry: any future date
- CVC: any 3 digits
The test subscription will appear in the Dashboard under Subscriptions with a test mode badge.
Common errors and gotchas
This price does not support subscriptions
You created a one-time Price instead of a recurring one. Go back to the Product, edit the Price, and make sure the billing period is set (Monthly, Yearly, etc.) — not left as one-time.
Checkout fails with mode: 'payment' and a recurring Price
Subscription Prices require mode: 'subscription'. Change the Checkout session mode.
Trial period not applying Trial periods are set on the Price, not the Checkout session. Make sure the trial is configured on the Price itself (step 4), not passed as a session parameter.
Different prices for different currencies Stripe supports multi-currency but each Price is locked to one currency. Create separate Prices for each currency you want to accept and present the appropriate one based on the user's location.