Skip to main content
Stripe is a great default if you want the most familiar developer payment workflow with full control over products, prices, and metadata. Let’s get you set up.

What you’re setting up

Four things, that’s it:
  1. A product or payment link in Stripe
  2. A checkout URL saved in your env vars
  3. A webhook endpoint in Stripe
  4. A way for the repo to understand what was purchased

Env vars

NEXT_PUBLIC_PAYMENT_PROVIDER=stripe
NEXT_PUBLIC_CHECKOUT_URL_TEMPLATE=https://...
STRIPE_SECRET_KEY=...
STRIPE_WEBHOOK_SECRET=...

Setup

1

Create your Stripe products

Head to your Stripe Dashboard and open Product catalog.
  1. Create a product for each thing you want to sell
  2. Add a price for each product
  3. Create a payment link or checkout flow
For your first setup, keep it simple — just create:
  • One Medium plan product
  • One Small credits product
2

Decide how Stripe describes the purchase

You have two options here. Pick whichever feels more natural to you.
3

Add your checkout URLs

Paste the Stripe payment link into the matching env var in .env.local:
NEXT_PUBLIC_CHECKOUT_URL_PLAN_MEDIUM=https://buy.stripe.com/...
NEXT_PUBLIC_CHECKOUT_URL_CREDITS_SMALL=https://buy.stripe.com/...
These are the URLs your app buttons will send users to.
4

Add the webhook in Stripe

Back in Stripe:
  1. Go to Developers -> Webhooks
  2. Click Add endpoint
  3. Set the URL to:
https://yourdomain.com/api/payments/stripe
  1. Enable at least these events:
    • checkout.session.completed
    • checkout.session.async_payment_succeeded
  2. Copy the signing secret into your env:
STRIPE_WEBHOOK_SECRET=whsec_...
5

Add your secret key

Grab your Stripe secret key and add it too:
STRIPE_SECRET_KEY=sk_...
The repo needs this to verify the webhook and inspect the checkout session.

How it works under the hood

Once Stripe sends a successful webhook, here’s what happens:
  1. The repo verifies the signature
  2. It loads the checkout session
  3. It resolves the purchase type from metadata or STRIPE_PRODUCT_MAP
  4. It stores the purchase in the database
  5. It updates the user’s credits or plan state

Verify your setup

Your Stripe setup is working if all of these are true:
  • The app opens the Stripe checkout page when you click a payment button
  • Stripe shows a successful webhook delivery in the dashboard
  • A row appears in your purchases table
  • A credits purchase increases the user’s credits
  • A plan purchase updates the user’s purchase state
STRIPE_SECRET_KEY needs your secret key (starts with sk_), not the publishable one (starts with pk_). The publishable key is for client-side Stripe.js only.
You probably added the checkout URL but forgot the webhook. Without a webhook, Stripe can’t tell your app the payment succeeded.
You haven’t set metadata on your product/session and you haven’t set STRIPE_PRODUCT_MAP. The repo needs at least one of these to know what was purchased.
Make sure you’re using the correct production domain in the webhook URL, not localhost.

Payments Overview

Go back to the shared payment architecture and basics.