Installation
Detailed setup instructions for all services.
Prerequisites
- Node.js 20+ (24 recommended)
- npm or yarn
- A Supabase account (free tier works)
- A Stripe account (test mode)
- A Resend account (optional, for emails)
Environment Variables
Copy .env.example to .env.local and fill in the values:
cp .env.example .env.local
Required Variables
| Variable | Description |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | Your Supabase project URL -- must include https:// (e.g., https://abc123.supabase.co) |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Supabase anon/public key |
SUPABASE_SERVICE_ROLE_KEY | Supabase service role key (never expose to client) |
STRIPE_SECRET_KEY | Stripe secret key (starts with sk_) |
STRIPE_WEBHOOK_SECRET | Stripe webhook signing secret |
NEXT_PUBLIC_APP_URL | Your app URL -- must include https:// (e.g., http://localhost:3000 or https://yourdomain.com) |
Optional Variables
| Variable | Description |
|---|---|
RESEND_API_KEY | Resend API key for transactional emails |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Stripe publishable key |
Supabase Setup
- Go to supabase.com and create a new project
- Navigate to the SQL Editor
- Copy the contents of
supabase/schema.sqland run it - Go to Settings → API and copy your keys to
.env.local
The schema creates:
profilestable (extends auth.users)customerstable (Stripe customer mapping)subscriptionstable (subscription tracking)purchasestable (one-time payment tracking)- RLS policies (deny all to anon key)
Auth URL Configuration
In your Supabase dashboard, go to Authentication → URL Configuration and set:
- Site URL:
http://localhost:3000(or your production URL) - Redirect URLs: Add the following URLs:
http://localhost:3000/auth/callbackhttps://yourdomain.com/auth/callback(for production)
These are required for OAuth (Google login) and email verification links to work correctly. The /auth/callback route handles the code exchange after authentication.
Stripe Setup
1. Get your API keys
- Go to dashboard.stripe.com and create an account (or log in)
- Make sure you're in test mode (toggle in the top right)
- Go to Developers > API keys
- Copy the keys to
.env.local:
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
2. Create your products
- Go to Product catalog > + Add product
- Create your first product (e.g., "Starter") with a one-time price of $199
- Create your second product (e.g., "Pro") with a one-time price of $249
- After creating each product, click into the price and copy the Price ID (starts with
price_) - Paste them in
config.ts:
// config.ts > billing > plans
priceId: {
monthly: 'price_1Xxx...', // Your real price ID
yearly: 'price_1Xxx...', // Same ID for one-time payments
},
3. Set up the webhook
The webhook is how Stripe tells your app that a payment succeeded. Without this, purchases won't be recorded.
For local development
Install the Stripe CLI and run:
stripe listen --forward-to localhost:3000/api/webhooks/stripe
This prints a webhook signing secret (whsec_...). Copy it to .env.local:
STRIPE_WEBHOOK_SECRET=whsec_...
For production
-
Go to Developers > Webhooks > + Add endpoint
-
Set the endpoint URL to:
https://yourdomain.com/api/webhooks/stripe -
Click Select events and add these.
Required for the default one-time billing flow:
checkout.session.completed(records the purchase, sends the delivery email)charge.dispute.created(logs disputes for review)
Add these too if you switch to subscription mode (the webhook handler is already wired for them, see Switching to subscription mode):
customer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_succeededinvoice.payment_failed
-
Click Add endpoint
-
Click Reveal signing secret and copy the
whsec_...value -
Add it to your production environment variables as
STRIPE_WEBHOOK_SECRET
4. Go live
When you're ready to accept real payments:
- Toggle off test mode in your Stripe Dashboard
- Create the same products/prices in live mode
- Update your production
.envwith live keys (sk_live_...,pk_live_...) - Create a new webhook endpoint for production with the same events
- Update
STRIPE_WEBHOOK_SECRETwith the new live signing secret
Testing Payments
Use Stripe test mode with the test card: 4242 4242 4242 4242 (any future expiry, any CVC).
For a free test flow, create a 100% discount coupon in Stripe and apply it at checkout.
Running the App
npm run dev
Open http://localhost:3000 in your browser.