This endpoint creates a Stripe Checkout session, redirecting the authenticated user to Stripe’s hosted checkout page to complete their subscription payment.
- Endpoint:
POST /api/checkout_sessions - File Location:
src/app/api/checkout_sessions/route.ts - Authentication: Required (uses Supabase session)
Parameters
The Stripe Price ID for the subscription plan. Get this from your Stripe Dashboard or environment variables.Format:
price_1234567890abcdefWhere to find:- Stripe Dashboard → Products → Select product → Copy Price ID
- Or use environment variables:
NEXT_PUBLIC_STRIPE_PRICE_ID_PRO_MONTHLY
Request/Response
Example Request (cURL)
Example Request (Fetch API)
Example Response (303 Redirect)
Example Response (401 Unauthorized)
Example Response (400 Bad Request)
price_id parameter.
Example Response (500 Server Error)
How It Works
1
Authenticate user
The endpoint checks for an active Supabase session using
supabase.auth.getUser().If no session exists, returns 401 Unauthorized.2
Validate price ID
Extracts
price_id from form data and validates it’s not empty or “undefined”.Invalid price IDs return 400 Bad Request.3
Create Checkout session
Calls Stripe API to create a Checkout session with:
- Line items: Subscription with selected price
- Mode:
"subscription"(recurring payment) - Success URL:
{SITE_URL}/success?session_id={CHECKOUT_SESSION_ID} - Cancel URL:
{SITE_URL}/pricing?canceled=true - Customer email: Pre-filled with user’s email
- Metadata: Includes
user_idanduser_emailfor webhook processing - Features:
- Automatic tax calculation
- Promotion code support
- Customer email pre-filled
4
Redirect to Stripe
Returns a
303 See Other redirect to the Stripe Checkout URL.User completes payment on Stripe’s secure hosted page.5
Handle return
After payment:
- Success: User redirected to
/success?session_id=cs_... - Cancel: User redirected to
/pricing?canceled=true
Implementation
The endpoint is implemented insrc/app/api/checkout_sessions/route.ts:
src/app/api/checkout_sessions/route.ts
Frontend Integration
Typically called from a pricing page or “Upgrade” button:src/components/marketing/pricing.tsx
Success Page Verification
After Stripe redirects to/success, verify the session:
src/app/success/page.tsx
Webhook Integration
This endpoint only starts the checkout flow—subscription activation and billing history are synced by/api/webhooks/stripe.
The webhook handler listens to these events:
| Event | Purpose |
|---|---|
customer.subscription.created | Upserts the initial record in user_subscriptions using metadata from the Checkout session |
customer.subscription.updated | Handles plan changes, cancellations, renewals, and billing-cycle changes |
customer.subscription.deleted | Marks subscriptions as canceled in the database |
invoice.payment_succeeded | Inserts a payment record into payment_history |
invoice.payment_failed | Logs failed payments so you can notify the customer |
user_id from metadata you set in checkout_sessions, webhooks must run even if the user never returns to /success.
See Stripe Webhook Handler for the full implementation details.
Testing
Test with Stripe Test Cards
Use Stripe’s test cards to simulate payments:Test Workflow
1
Ensure Stripe CLI is running
2
Start dev server
3
Create checkout session
- Sign in to your app
- Go to
/pricing - Click “Subscribe” on any plan
- You’ll be redirected to Stripe Checkout (test mode)
4
Complete test payment
- Fill form with test card
4242 4242 4242 4242 - Click “Subscribe”
- Stripe processes payment and redirects to
/success
5
Verify webhook received
Check your terminal running Stripe CLI. You should see one of the subscription or invoice events:For invoice flows, you can also trigger:
6
Check database
Verify
user_subscriptions and payment_history tables updated:Troubleshooting
401 Unauthorized error
401 Unauthorized error
400 Bad Request - 'Valid price ID is required'
400 Bad Request - 'Valid price ID is required'
Cause: Missing or invalid
price_id parameter.Fix:- Verify form includes
price_idfield - Check Price ID format:
price_...(notprod_...) - Ensure Price ID exists in Stripe Dashboard
- Test with hardcoded Price ID first
Redirect to Stripe fails
Redirect to Stripe fails
Cause: Stripe API error, network issue, or invalid configuration.Fix:
- Check
STRIPE_SECRET_KEYenvironment variable - Verify Stripe key matches mode (test vs live)
- Check browser console for errors
- Inspect server logs for Stripe API errors
Checkout works but subscription not created
Checkout works but subscription not created
Cause: Webhook not received or webhook handler error.Fix:
- Verify Stripe CLI running:
stripe listen --forward-to localhost:3000/api/webhooks/stripe - Check webhook endpoint:
POST /api/webhooks/stripe - Verify
STRIPE_WEBHOOK_SECRETenvironment variable - Check webhook handler logs for errors
- Test webhook:
stripe trigger checkout.session.completed
'origin' header missing
'origin' header missing
Cause: Request made without origin header (e.g., from Postman).Fix:
Use browser fetch or add origin header manually:
Security Considerations
Authentication Required
Authentication Required
The endpoint checks for an active Supabase session. Unauthenticated requests return
401.Why: Prevents anonymous users from creating checkout sessions and ensures proper user-subscription linking.Metadata Tracking
Metadata Tracking
User ID and email are stored in Stripe metadata:Purpose: Webhooks use this to update the correct
user_subscriptions record.Privacy: Email stored in Stripe only, not exposed publicly.Success URL Protection
Success URL Protection
The success page should verify the session ID with Stripe:Why: Prevents users from forging success URLs to fake payments.
No Sensitive Data Exposure
No Sensitive Data Exposure
The endpoint doesn’t return the Checkout session object (contains sensitive data). It only redirects to Stripe.Best practice: Stripe handles all payment details securely on their PCI-compliant infrastructure.
Related Documentation
Payments with Stripe
Complete Stripe integration guide including webhooks, plans, and testing.
Customer Portal
Allow users to manage their subscriptions, update payment methods, and view invoices.
Stripe Webhooks
Handle subscription lifecycle events and keep your database in sync.
Environment Variables
Set up Stripe API keys and webhook secrets for local and production environments.