A complete payment flow implementation built with Next.js, demonstrating pricing plans, checkout processing, and subscription management through a hands-on learning approach.
# Install dependencies
npm install
# Set up database
npm run db:push
# Seed with demo plans
npm run seed
# Start development server
npm run dev
Visit http://localhost:3000 to see the app!
/src/app/* # Next.js App Router pages & API routes
/src/components/* # Reusable UI components
/src/lib/
├── db.ts # Prisma database client
├── analytics.ts # Event tracking utilities
/prisma/
├── schema.prisma # Database schema
/scripts/
├── seed.ts # Database seeding script
This project demonstrates a complete payment flow with three core components:
- Pricing Plans - Display available subscription tiers
- Checkout Flow - Process payments and create subscriptions
- Subscription Management - View and update subscription status
Plans Table: Represents the subscription tiers customers can purchase. Each plan has:
price
(in cents to avoid floating-point errors)interval
(monthly/yearly billing cycles)features
(JSON array of what's included)isActive
flag (for hiding deprecated plans without breaking existing subscriptions)
Subscriptions Table: Tracks customer subscriptions with:
status
enum (ACTIVE, TRIALING, CANCELED, etc.)currentPeriodStart/End
(for billing cycles)planId
reference (so plan changes don't affect existing customers)
Events Table: Analytics tracking for:
- User behavior (
pricing_viewed
,checkout_started
) - Business metrics (
checkout_completed
,subscription_canceled
) - Debugging and funnel analysis
- ✅ Plans loaded from SQLite database
- ✅ Clean pricing UI with feature lists
- ✅ Event tracking (
pricing_viewed
fires on page load) - ✅ Popular plan highlighting
- ✅ Responsive design
- Start the app:
npm run dev
- Visit homepage: http://localhost:3000
- See phase progress overview
- Click "View Pricing Plans"
- Pricing page: http://localhost:3000/pricing
- Should load 3 plans: Basic ($9.99), Pro ($29.99), Enterprise ($99.99)
- Pro plan highlighted as "Most Popular"
- Click any "Get Started" button → alert shows (Phase 2 coming next)
- Check event tracking: Look at your terminal console
- Should see:
📊 Event tracked: pricing_viewed
- Should see:
- Plans display correctly with prices and features
- "Most Popular" badge appears on Pro plan
- Console shows
pricing_viewed
event when visiting /pricing - Clicking plan buttons shows alert with plan ID
- Database not seeded: Run
npm run seed
if you see empty pricing page - Port conflicts: Change port with
npm run dev -- -p 3001
- Events not tracking: Check browser dev tools network tab for failed /api/analytics calls
What's Mocked in Phase 1:
- ✅ Real database (SQLite for local dev)
- ✅ Real event tracking system
- ❌ User authentication (using placeholder user IDs)
- ❌ Payment processing (buttons show alerts)
Real Production Requirements:
- Authentication: Next-Auth.js or Auth0 for user sessions
- Database: PostgreSQL on Vercel/Railway for production
- Analytics: Ship events to PostHog, Mixpanel, or custom analytics pipeline
- Monitoring: Error tracking with Sentry, performance with Vercel Analytics
- ✅ Complete checkout page with payment form
- ✅ Mock payment processor with realistic delays and test cards
- ✅ Subscription creation in database
- ✅ Success/failure handling with proper error messages
- ✅ Event tracking (
checkout_started
,checkout_completed
,checkout_failed
) - ✅ Professional payment form with validation
- Start checkout flow: Visit pricing → Click any "Get Started" button
- Checkout page: http://localhost:3000/checkout?planId=[plan-id]
- Shows order summary with plan details
- Displays professional payment form with Stripe integration notes
- Test payment scenarios:
- Success: Use card
4242424242424242
with any future expiry/CVC - Decline: Use card
4000000000000002
- Insufficient funds: Use card
4000000000000119
- Success: Use card
- Success flow: Completes → redirects to success page with subscription details
- Check event tracking: Terminal shows payment processing logs
- Checkout page loads with selected plan details
- Payment form validates required fields
- Test cards trigger appropriate success/failure responses
- Successful payments create subscriptions in database
- Success page displays subscription information
- Console shows checkout events (
checkout_started
,checkout_completed
)
- Missing planId: Checkout redirects to pricing if no plan selected
- Form validation: All fields required, card format validated
- Network delays: Mock processor simulates 1.5-2.5 second delays
What's Working in Phase 2:
- ✅ Complete checkout user experience
- ✅ Payment form validation and UX
- ✅ Database subscription creation
- ✅ Event tracking and logging
- ❌ Mock payment processor (not real payments)
- ❌ No webhook handling for payment updates
Real Stripe Integration Requirements:
1. Frontend (Stripe Elements):
// Replace PaymentForm with Stripe Elements
import { Elements, CardElement, useStripe, useElements } from '@stripe/react-stripe-js'
import { loadStripe } from '@stripe/stripe-js'
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!)
2. Backend Payment Intent Creation:
// Replace processPayment() with:
const paymentIntent = await stripe.paymentIntents.create({
amount: plan.price,
currency: 'usd',
customer: customer.id,
metadata: { planId, subscriptionId }
})
3. Webhook Handling:
- Set up
/api/webhooks/stripe
endpoint - Handle
payment_intent.succeeded
events - Update subscription status based on payment results
- Verify webhook signatures for security
4. Security & Compliance:
- Card details never touch your server (Stripe Elements handles this)
- PCI compliance handled by Stripe
- Webhook signature verification prevents fraud
- ✅ Complete subscription dashboard with current plan overview
- ✅ Real-time plan upgrade/downgrade functionality
- ✅ Subscription cancellation with period-end access
- ✅ Comprehensive billing history with transaction details
- ✅ Event tracking (
subscription_management_viewed
,subscription_plan_changed
,subscription_canceled
) - ✅ Professional management interface with status indicators
- Complete a subscription first: Go through pricing → checkout → success flow
- Access management: Visit http://localhost:3000/manage or click "Manage Subscription" on success page
- View subscription overview:
- Current plan details with features and pricing
- Billing cycle information and next renewal date
- Subscription status (ACTIVE, CANCELED, etc.)
- Test plan changes:
- Upgrade or downgrade between available plans
- See real-time price differences and feature updates
- Watch for confirmation and loading states
- Test cancellation:
- Cancel subscription with confirmation dialog
- Observe "access until period end" messaging
- See status change to CANCELED with reactivation option
- Review billing history:
- View chronological transaction history
- See payment events, plan changes, and cancellations
- Test export functionality (shows production alerts)
- Management page loads with current subscription details
- Plan upgrade/downgrade changes subscription in database
- Cancellation updates status and shows appropriate messaging
- Billing history displays realistic transaction data
- All actions show proper loading states and error handling
- Console shows management events (
subscription_management_viewed
, etc.)
- No subscription found: Complete checkout flow first to create subscription
- Plan changes fail: Ensure subscription is ACTIVE status for changes
- Cancellation restrictions: Only active subscriptions can be canceled
What's Working in Phase 3:
- ✅ Complete subscription management user experience
- ✅ Plan change logic with database updates
- ✅ Cancellation handling with proper messaging
- ✅ Billing history simulation with realistic data
- ✅ Event tracking for all management actions
- ❌ Mock subscription operations (not real billing)
- ❌ No webhook handling for external payment updates
Real Stripe Subscription Management Requirements:
1. Subscription Updates:
// Plan changes via Stripe API
const updatedSubscription = await stripe.subscriptions.update(subscriptionId, {
items: [{
id: subscriptionItem.id,
price: newPriceId,
}],
proration_behavior: 'create_prorations', // Handle billing adjustments
})
2. Cancellation Handling:
// Cancel at period end (recommended approach)
const canceledSubscription = await stripe.subscriptions.update(subscriptionId, {
cancel_at_period_end: true
})
// Immediate cancellation (less common)
const canceledSubscription = await stripe.subscriptions.cancel(subscriptionId)
3. Billing History Integration:
// Fetch invoices from Stripe
const invoices = await stripe.invoices.list({
customer: customerId,
limit: 10
})
// Get payment history
const payments = await stripe.paymentIntents.list({
customer: customerId
})
4. Webhook Integration:
- Handle
invoice.payment_succeeded
for successful billing - Handle
customer.subscription.updated
for plan changes - Handle
customer.subscription.deleted
for cancellations - Update local database based on Stripe events
5. Proration and Billing Logic:
- Stripe automatically handles prorations for mid-cycle plan changes
- Credit/charge differences are applied to next invoice
- Cancellations preserve access until period end by default
npm run dev # Start development server
npm run build # Build for production
npm run db:push # Push schema changes to database
npm run seed # Seed database with demo data
npm run lint # Run ESLint
- Next.js 15 App Router structure
- Prisma ORM with SQLite
- TypeScript interfaces for type safety
- API route design (
/api/plans
,/api/analytics
) - Client-side data fetching patterns
- Event-driven architecture basics
- SaaS pricing tier strategy
- Subscription lifecycle states
- Analytics event design
- Feature flag patterns (plan
isActive
)
- Responsive pricing table design
- Loading states and error handling
- Progressive disclosure (phase-by-phase features)
The complete payment flow learning project is now finished with all three phases implemented:
- Phase 1: Pricing plans with database integration and event tracking
- Phase 2: Complete checkout flow with mock payment processing
- Phase 3: Full subscription management dashboard with plan changes and billing history
Technical Implementation:
- Next.js 15 App Router with TypeScript for type safety
- Prisma ORM with SQLite for local development
- Mock payment processing that simulates real-world scenarios
- Comprehensive event tracking system for analytics
- Professional UI/UX patterns for SaaS applications
Business Understanding:
- SaaS pricing tier strategy and plan management
- Subscription lifecycle management (active → canceled → reactivation)
- Payment flow UX best practices
- Billing history and transaction tracking
- Customer subscription management workflows
Production Readiness:
- Clear separation between mock and production code
- Detailed Stripe integration documentation
- Webhook handling patterns for payment events
- Security considerations and PCI compliance notes
- Scalable database schema design
To take this to production:
- Replace mock payment processor with Stripe Elements and Payment Intents
- Add user authentication with NextAuth.js or similar
- Implement webhook handling for payment event processing
- Switch to PostgreSQL for production database
- Add email notifications for subscription changes
- Implement proper error handling and monitoring
- Add comprehensive testing suite
This project demonstrates a complete SaaS payment flow from first principles, with:
- Realistic payment scenarios including failures and edge cases
- Production-ready UI/UX patterns
- Comprehensive event tracking for business intelligence
- Clear architectural decisions with detailed explanations
- Step-by-step phase implementation for learning
Perfect for: Learning SaaS fundamentals, understanding payment flows, building subscription businesses, or as a foundation for production SaaS applications.