Step 3 of 4

Set Up Payments

Connect Stripe so your store can accept card payments — and so that paid orders actually reach your admin panel.

You'll need a Stripe account and your deployed store URL from Step 2 · Estimated time: 5–10 min

This step is optional. Cash on delivery and bank transfer work without any payment provider, so you can skip ahead to Step 4 and come back when you're ready to take cards.

1. Get Your Stripe Keys

Start in Test mode — you want to place a fake order end to end before real money is involved. There is a toggle in the top-right of the Stripe dashboard.

  1. Go to Stripe Dashboard → API keys
  2. Confirm the toggle says Test mode
  3. Copy both keys using the copy icon:
You'll seeSave it as
Publishable key (pk_test_…)NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
Secret key (sk_test_…)STRIPE_SECRET_KEY

Add both to .env.local, either by editing the file or by re-running npm run setup.

2. Register the Webhook

A webhook is how Stripe tells your store that a payment succeeded. Without it, customers are charged and no order is ever created. Get this right and everything else follows.

Recommended: let the script do it

This creates the endpoint, subscribes exactly the right events, and writes the signing secret into .env.local:

npm run stripe:setup

It's safe to run more than once — an existing endpoint with the same URL is reused rather than duplicated. It registers the webhook against your deployed store, which is why this step comes after Step 2 — Stripe cannot reach localhost. For local development use npm run stripe:listen instead.

Manual alternative

  1. Go to Stripe Dashboard → Webhooks
  2. Click Add destination
  3. Select exactly these four events — search for each by name:
EventWhat it does
payment_intent.succeededCreates the order when a payment clears
payment_intent.payment_failedRecords the failed attempt
charge.refundedMarks the order refunded
charge.refund.updatedTracks refund status changes
  1. Click Continue, choose Webhook endpoint, then Continue again
  2. Set the Endpoint URL to your store address followed by /api/stripe/webhook:
    https://yourstore.com/api/stripe/webhook
  3. Click Create destination
  4. Copy the Signing secret (whsec_…) and save it as STRIPE_WEBHOOK_SECRET

Followed an older version of this guide? Earlier revisions listed subscription events — checkout.session.completed, customer.subscription.* and invoice.*. Those are not the events this store listens for, and a webhook configured that way will never record an order. Open your endpoint in Stripe, replace the event list with the four above, or just run npm run stripe:setup to correct it.

Custom domain not connected yet? Use the Vercel URL from Step 2, e.g. your-project.vercel.app/api/stripe/webhook. If you add a domain later, update the endpoint URL here to match.

3. Testing Locally

Stripe can't reach localhost, so forward events to your machine while developing:

# One-time: install the Stripe CLI
brew install stripe/stripe-cli/stripe
stripe login

# Then, in a second terminal alongside npm run dev
npm run stripe:listen

The command prints its own whsec_… — use that one in .env.local while testing locally. It is different from your deployed endpoint's secret; each endpoint has its own.

4. Going Live

When test orders work end to end, switch over:

  1. Flip the Stripe dashboard to Live mode
  2. Copy the live keys (pk_live_… and sk_live_…) into your hosting environment variables
  3. Register the webhook again in live mode — test and live endpoints are separate, each with its own signing secret
  4. Redeploy, then run npm run post-deploy to confirm the live keys are in place

Checklist

  • Publishable and secret keys saved (test mode first)
  • Webhook endpoint created — via npm run stripe:setup or manually
  • Exactly the four events above are subscribed
  • Signing secret saved as STRIPE_WEBHOOK_SECRET
  • A test payment created a real order in the admin panel

Was this step clear?