82 82. Step 8: Set Up Stripe
As of Oct 22, 2026 · Official docs: Stripe · Stripe sandboxes · Stripe API keys · Stripe subscriptions · Stripe customer portal
Stripe will handle subscriptions and billing: the app’s monthly “Premium” plan. While you develop, you work in a Stripe sandbox (Stripe’s test environment; older pages call it “test mode”). Stripe is free to use in a sandbox.
As you go, copy each value into the Task App Setup Worksheet (see Classes 10–12: Start here). Its Commands sheet builds the exact lines you paste in this chapter, with your own keys in them.
82.1 Create Stripe Account
- Go to https://dashboard.stripe.com.
- Sign up for a new account or log in.
82.2 The Stripe Sandbox
A new Stripe account starts in a sandbox. In a sandbox, real cards are never charged and no real money moves. Instead, you pay with test card numbers (see Step 12) to make sure that your code works. Do every Stripe step in this chapter in the same sandbox: keys, products and webhooks from one sandbox don’t work in another.
82.3 Get API Keys
The keys are on the dashboard’s Home page, and on the API keys page. A sandbox has two. Copy both into the worksheet:
The publishable key (starts with
pk_test_) is meant for code that runs in a browser. This app never calls Stripe from the browser, so it doesn’t use this key; you won’t paste it anywhere.The secret key (starts with
sk_test_) can do anything with your Stripe account. It must stay on servers. Store it as a Supabase secret, so the Edge Functions can use it: copy the worksheet’sSTRIPE_SECRET_KEYline from the Commands sheet, paste it into VS Code’s terminal (in thetask-appfolder), and pressEnter. The line looks like this:npx supabase secrets set STRIPE_SECRET_KEY=sk_test_...Type these lines yourself rather than giving the keys to Claude, so the secret keys never go into Claude’s chat.
If you have separate development and production projects, repeat this setup for each one (we do not in this tutorial).
82.3.1 Create a Product
In the Stripe Dashboard, go to Product catalog (it may be under More).
Click + Add product.
Fill in:
- Name:
TaskMaster Premium - Optional description and image.
- Name:
Under the price, choose:
- Recurring
- Billing period: Monthly
- Amount: e.g.,
$10
Click Add product to save the product and its price.
Open the product, click the price, and copy its Price ID (it starts with
price_) into the worksheet.Store the Price ID as a Supabase secret named
STRIPE_PRICE_ID. Thecreate-stripe-sessionEdge Function reads it from there when it creates a checkout page. Copy the worksheet’sSTRIPE_PRICE_IDline, paste it into VS Code’s terminal (in thetask-appfolder), and pressEnter:npx supabase secrets set STRIPE_PRICE_ID=price_...
.env.local
Earlier versions of this page put the Price ID (and the publishable key) in .env.local as NEXT_PUBLIC_STRIPE_PRICE_ID. The app’s code never reads those variables, so checkout fails. The Price ID must be the Supabase secret STRIPE_PRICE_ID.
82.3.2 Get Webhook Signing Secret
You’ll use a webhook to let Stripe notify your app about subscription changes. A webhook is a web address that Stripe calls to tell your app that something happened, such as “this customer paid”. Stripe calls the setup an event destination.
The address is your stripe-webhook Edge Function. The worksheet builds it for you from your Supabase project ref (the row “Stripe webhook URL”):
https://YOUR-PROJECT-REF.supabase.co/functions/v1/stripe-webhook
You deploy that function in Step 11. You can create the webhook now, with the real address, or wait until the function is deployed, as Classes 10–12 do ((H) Deploy the Edge Functions and the Stripe webhook). If you create it now, Stripe can’t deliver events to it until the function is deployed; that’s fine, because you don’t try a payment until Step 12.
Open the Webhooks tab in Stripe’s Workbench (in the same sandbox) and click Create an event destination.
Choose:
- Events from: Your account.
- Payload style: Snapshot. Stripe recommends “Thin”, but this app’s code only understands Snapshot events.
Select these events:
checkout.session.completedcustomer.subscription.deleted
(The code handles only these two. The tutorial repository’s README also lists
customer.subscription.updated; adding it does no harm.)Click Continue and choose Webhook endpoint as the destination type.
Click Continue and enter the Endpoint URL: your Stripe webhook URL from the worksheet.
Create the destination. On its settings page, find the signing secret (it starts with
whsec_), click Reveal secret, and copy it into the worksheet.Store it as a Supabase secret: copy the worksheet’s
STRIPE_WEBHOOK_SECRETline, paste it into VS Code’s terminal (in thetask-appfolder), and pressEnter. The line looks like this:npx supabase secrets set STRIPE_WEBHOOK_SECRET=whsec_...
For each event, Stripe’s Workbench shows whether it was delivered. The app’s stripe-webhook function answers “OK” even when it rejects an event (for example, because the signing secret in STRIPE_WEBHOOK_SECRET doesn’t match this destination’s secret). So Stripe may show an event as delivered even though the app ignored it. If an upgrade doesn’t show up in the app, look at the stripe-webhook logs in the Supabase dashboard under Edge Functions: the function writes the reason there.
82.3.3 Store the Secret Key in Supabase Vault
When someone signs up for the app, the database itself creates a Stripe customer for them. (Migration 7_init_stripe_integration.sql, which you run in Step 9, sets this up.) To talk to Stripe, the database needs your Stripe secret key. It reads the key from Supabase Vault, an encrypted store for secrets inside your database, under the name stripe.
In the Supabase dashboard, open SQL Editor.
Paste the Vault line from the worksheet’s Commands sheet, which has your own secret key in place of
sk_test_..., and click Run:select vault.create_secret('sk_test_...', 'stripe');This is SQL, the language of databases, not a terminal command. It calls the function
create_secretin Supabase’svault, with two values: your secret key and the name to store it under. (The tutorial repository’s README does the same with aninsert into vault.secrets ...command.)
Do this before anyone signs up for the app. Without the key in Vault, creating a new user’s profile fails, so sign-up fails.
82.3.4 Set Up the Customer Portal
The app’s subscription button sends premium users to Stripe’s customer portal, a Stripe page where they can update their payment method or cancel. In a sandbox, the portal doesn’t work until you save its settings once.
- Open the customer portal settings in the Stripe Dashboard (in the same sandbox).
- Look over the options (letting customers cancel is on by default). For class demos, set cancellations to happen immediately.
- Click Save.
By default, a cancelled subscription stays active until the end of the billing period. Only then does Stripe send customer.subscription.deleted, and the app moves the user back to the free plan. With cancellations set to happen immediately, a cancelled subscription switches the app back to the free plan right away, so you can see the whole cycle in one sitting.
Practice Questions
Easy
Question 1 · True or false
While you work in the Stripe sandbox, real credit cards are charged.
Question 2 · Short answer
In your own words, what is a webhook, and what does task-app use one for?
Question 3 · Multiple choice
Where does the page store the Price ID of your subscription price?
- In
.env.local, asNEXT_PUBLIC_STRIPE_PRICE_ID - As a Supabase secret named
STRIPE_PRICE_ID - In the Google Cloud Console
- Nowhere: Stripe picks the price automatically
Question 4 · Matching
Match each Stripe value with its description.
| Value | Description | |
|---|---|---|
| 1. Publishable key | A. Looks like price_12345...; identifies the price a customer subscribes to (for example, $10 per month) |
|
| 2. Secret key | B. Looks like whsec_...; lets your code check that a webhook message really came from Stripe |
|
| 3. Price ID | C. Safe to use in code that runs in the browser | |
| 4. Webhook signing secret | D. Can do anything with your Stripe account through Stripe’s API; must stay on a server |
Medium
Question 5 · Write the command
Stripe shows you the signing secret whsec_9f8e7d. Write the command that stores it as a Supabase secret named STRIPE_WEBHOOK_SECRET.
Question 6 · Multiple choice
You want a “Pro Plan” that costs $10 every month. Which price settings match?
- One-off, $10
- Recurring, billing period Monthly, $10
- Recurring, billing period Yearly, $10
- One-off, $120
Question 7 · Matching
Match each webhook event with when Stripe sends it.
| Event | When Stripe sends it | |
|---|---|---|
1. checkout.session.completed |
A. A customer’s subscription ends (for example, it is cancelled) | |
2. customer.subscription.updated |
B. A customer finishes paying on Stripe’s checkout page | |
3. customer.subscription.deleted |
C. Something about a subscription changes (for example, its plan or status) |
Question 8 · Write the path
Sam’s Supabase project ref is abcdefghijklmnop. What endpoint URL does he enter for the webhook’s event destination in Stripe?
Question 9 · Multiple choice
When you create the event destination, Stripe offers two payload styles and recommends “Thin”. Which one do you choose for this app, and why?
- Thin, because Stripe recommends it
- Snapshot, because the app’s webhook code only understands Snapshot events
- Either one; the app handles both
- Neither; the payload style only matters in live mode
Question 10 · Short answer
When a user clicks to upgrade to the Pro Plan, how does Stripe know what to charge them? What role does the Price ID play?
Hard
Question 11 · Scenario
While testing, Maria completes Stripe’s checkout page successfully, but task-app still shows her on the free plan. In Stripe’s Workbench, the checkout.session.completed event shows as delivered. Explain what could be going on and where she should look.
Question 12 · Short answer
Why does Stripe give each webhook a signing secret? What could happen if the app accepted webhook messages without checking it?
Question 13 · Long answer
The page mentions repeating this setup for each environment if you have separate development and production setups. Suppose task-app goes live and starts charging real customers. What has to be set up again, and why?
Question 14 · Scenario
You finished Steps 8 to 12, but every new sign-up fails with a database error. You notice that you skipped the section “Store the Secret Key in Supabase Vault”. Why does a missing Stripe key break sign-up, of all things, and how do you fix it?
Question 15 · Short answer
A premium user clicks the app’s subscription button and gets an error instead of Stripe’s customer portal. Checkout worked fine earlier. What did you most likely skip?
Question 16 · Scenario
During a class demo, Sam cancels his Premium subscription in Stripe’s customer portal, but task-app still shows him on the Premium plan. Nothing is broken. What explains it, and what portal setting changes this?