Stripe Checkout Flow Setup
Date: 2025-01-04 Status: ✅ Code Complete - Ready for Testing
Overview
The complete checkout flow is now implemented, allowing users to subscribe to Personal, Pro, or Team tiers with 10-day free trials.
Files Created
1. Checkout API Route
File: app/api/stripe/checkout/route.ts
Creates Stripe Checkout sessions with:
- 10-day free trial for all paid tiers
- User ID linked via metadata
- Success/cancel redirects
- Promotion code support
2. Success Page
File: app/checkout/success/page.tsx
Shows confirmation after successful checkout with:
- Success message
- Trial information
- Next steps
- Quick links to dashboard and settings
3. Checkout Button Component
File: components/checkout-button.tsx
Reusable button component that:
- Requires authentication (redirects to sign-in if needed)
- Shows loading state
- Calls checkout API
- Redirects to Stripe Checkout
4. Updated Pricing Page
File: app/(marketing)/pricing/page.tsx
Now uses CheckoutButton component with:
- Real Stripe price IDs
- Monthly/yearly toggle support
- Trial information
Environment Variables Required
Add these to your .env.local file:
# Stripe Keys (already configured)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Public Price IDs (for client-side pricing page)
NEXT_PUBLIC_STRIPE_PERSONAL_MONTHLY_PRICE_ID=price_1SPjo4Aye5brjRffe1sdUwS9
NEXT_PUBLIC_STRIPE_PERSONAL_YEARLY_PRICE_ID=price_1SPjoGAye5brjRff3OxcvQo8
NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PRICE_ID=price_1SPjokAye5brjRffO0iaBf5S
NEXT_PUBLIC_STRIPE_PRO_YEARLY_PRICE_ID=price_1SPjovAye5brjRfftU1yWaqo
NEXT_PUBLIC_STRIPE_TEAM_MONTHLY_PRICE_ID=price_1SPjqmAye5brjRffVAyS9mhe
NEXT_PUBLIC_STRIPE_TEAM_YEARLY_PRICE_ID=price_1SPjqwAye5brjRffqf7Enyuv
# Base URL (optional - auto-detected if not set)
NEXT_PUBLIC_BASE_URL=http://localhost:3017
# Convex (already configured)
NEXT_PUBLIC_CONVEX_URL=https://your-project.convex.cloudImportant: The NEXT_PUBLIC_* prefix makes these variables accessible in client components.
How It Works
User Flow
- User visits pricing page (
/pricing) - User clicks "Start Free Trial"
- System checks authentication:
- Not signed in → Redirect to
/sign-in?redirect_url=/pricing - Signed in → Continue to step 4
- Not signed in → Redirect to
- Frontend calls
POST /api/stripe/checkout - API creates checkout session:
- Links userId in metadata
- Sets 10-day trial period
- Returns Stripe Checkout URL
- User redirected to Stripe Checkout
- User enters payment details (test mode: use test cards)
- Stripe creates subscription and sends webhook
- Webhook syncs to Convex (creates subscription record)
- User redirected to
/checkout/success
API Request Format
POST /api/stripe/checkout
Content-Type: application/json
{
"priceId": "price_1SPjo4Aye5brjRffe1sdUwS9",
"tier": "personal",
"interval": "monthly"
}API Response Format
{
"url": "https://checkout.stripe.com/c/pay/...",
"sessionId": "cs_test_..."
}Testing with Stripe Test Cards
Test Mode Setup
You're currently in test mode (correct for development). Use these test card numbers:
Successful Payment
| Card Number | Description |
|---|---|
4242 4242 4242 4242 | Visa - Always succeeds |
5555 5555 5555 4444 | Mastercard - Always succeeds |
3782 822463 10005 | American Express - Always succeeds |
For all test cards:
- Expiry: Any future date (e.g., 12/25)
- CVC: Any 3 digits (e.g., 123)
- ZIP: Any 5 digits (e.g., 12345)
Failed Payments (for testing error handling)
| Card Number | Description |
|---|---|
4000 0000 0000 0002 | Card declined |
4000 0000 0000 9995 | Insufficient funds |
4000 0000 0000 0069 | Expired card |
Testing Steps
- Add environment variables to
.env.local(see above) - Restart dev server:
pnpm dev - Ensure ngrok is running (for webhook testing)
- Ensure you're signed in to the app
- Go to pricing page:
http://localhost:3017/pricing - Click "Start Free Trial" on any tier
- Use test card:
4242 4242 4242 4242 - Complete checkout
- Verify:
- Redirected to
/checkout/success - Webhook received (check console logs)
- Subscription created in Convex database
- Redirected to
Verifying Subscription Creation
Check Stripe Dashboard
- Go to Stripe Dashboard → Test mode → Customers
- Find the customer (will have the user's email)
- Click on customer → See subscription with 10-day trial
Check Convex Database
- Go to Convex Dashboard → Data
- Open subscriptions table
- Find record with your
userId - Verify:
tier: "personal", "pro", or "team"status: "trialing"trialEndsAt: ~10 days from nowstripeSubscriptionId: starts with "sub_"
Check Console Logs
You should see in your Next.js console:
✅ Received webhook: customer.subscription.created
Subscription created: sub_1234567890 for user user_abc123Common Issues
"Missing environment variable" Error
Cause: Price ID environment variables not set
Fix:
- Add all
NEXT_PUBLIC_STRIPE_*_PRICE_IDvariables to.env.local - Restart dev server
"Unauthorized" Error
Cause: User not signed in
Expected: User should be redirected to /sign-in
If not redirecting: Check Clerk authentication setup
Webhook Not Received
Cause: ngrok tunnel not configured or webhook not created
Fix: See STRIPE_WEBHOOK_SETUP.md for webhook configuration
"No userId in metadata" Warning
Cause: userId not passed to checkout session
This should NOT happen with the current implementation - the API route includes userId in metadata. If you see this, check:
- User is authenticated when calling checkout API
- Clerk auth is working correctly
Trial Period Behavior
During Trial (Days 1-10)
- User has full access to tier features
- No payment charged
status: "trialing"- Can cancel anytime without being charged
After Trial Ends (Day 11)
- Stripe automatically charges the subscription
status: changes to "active"- Webhook:
invoice.payment_succeeded - User continues to have access
If Payment Fails After Trial
status: changes to "past_due"- Webhook:
invoice.payment_failed - Stripe retries payment (configurable in Stripe settings)
- Consider showing banner to update payment method
Next Steps
- ✅ Checkout flow implemented
- ⏳ Test end-to-end with test cards
- ⏳ Implement Customer Portal (manage subscription)
- ⏳ Add frontend usage indicators
- ⏳ Add upgrade prompts when limits hit
- ⏳ Test downgrade/upgrade scenarios
Security Notes
- ✅ Authentication required before checkout
- ✅ User ID passed securely via Clerk auth
- ✅ Stripe handles all payment processing
- ✅ Webhook signatures verified
- ✅ Price IDs are public (not sensitive)
Reference Documents:
docs/STRIPE_PRODUCT_SETUP.md- Product and price IDsdocs/STRIPE_WEBHOOK_SETUP.md- Webhook configurationdocs/PHASE_1_IMPLEMENTATION_COMPLETE.md- Backend tier enforcementdocs/TIER_MANAGEMENT_PLAN.md- Complete implementation roadmap