Stripe Customer Portal Setup
Date: 2025-01-04 Status: ✅ Code Complete - Ready for Testing
Overview
The Customer Portal is now implemented, allowing users to manage their subscriptions directly through Stripe's hosted portal. Users can change plans, update payment methods, cancel subscriptions, and view billing history.
Files Created
1. Customer Portal API Route
File: app/api/stripe/portal/route.ts
Creates Stripe Customer Portal sessions with:
- Authentication check
- Subscription lookup from Convex
- Return URL to settings page
- Error handling
2. Manage Subscription Button Component
File: components/manage-subscription-button.tsx
Reusable button that:
- Calls portal API
- Shows loading state
- Redirects to Stripe portal
- Handles errors gracefully
- Includes external link icon
3. Subscription Status Card Component
File: components/subscription-status-card.tsx
Comprehensive subscription display showing:
- Current tier and status
- Trial information (days remaining)
- Plan limits (pages, tabs, features)
- Status badges (Active, Trial, Past Due, Canceled)
- Manage subscription button
- Upgrade link for free tier users
4. Settings Page
File: app/settings/page.tsx
Simple settings page with:
- Subscription status card
- Clean layout
- Placeholder for future settings sections
What Users Can Do in Customer Portal
Subscription Management
- ✅ Upgrade/Downgrade Plans - Switch between Personal, Pro, Team
- ✅ Change Billing Cycle - Switch between monthly and yearly
- ✅ Cancel Subscription - Cancel at period end or immediately
- ✅ Reactivate Canceled - Resume canceled subscriptions
Payment Methods
- ✅ Update Card - Change payment method
- ✅ Add Multiple Cards - Store multiple payment methods
- ✅ Set Default - Choose default payment method
Billing History
- ✅ View Invoices - See all past invoices
- ✅ Download PDFs - Download invoice PDFs
- ✅ View Upcoming - See upcoming invoice amounts
How It Works
User Flow
- User goes to
/settings - Subscription Status Card displays:
- Current tier
- Status (Active, Trial, Past Due, etc.)
- Days remaining in trial (if applicable)
- Plan limits
- User clicks "Manage Subscription"
- API creates portal session:
- Looks up subscription in Convex
- Gets Stripe customer ID
- Creates portal session with return URL
- User redirected to Stripe Customer Portal
- User makes changes (upgrade, cancel, update card, etc.)
- Stripe sends webhooks (already configured)
- Webhooks update Convex (already implemented)
- User clicks "Return"
- Redirected back to
/settings?portal=true
API Request Format
POST /api/stripe/portal
Content-Type: application/json
{} // Empty body - user ID from authenticationAPI Response Format
{
"url": "https://billing.stripe.com/p/session/..."
}Stripe Portal Configuration
The Customer Portal is automatically configured with Stripe's default settings. To customize:
- Go to Stripe Dashboard → Settings → Customer Portal
- Configure portal settings:
- Subscription cancellation: Allow customers to cancel
- Plan changes: Allow customers to change plans
- Payment method updates: Allow customers to update cards
- Invoice history: Show invoice history
- Promo codes: Allow promo code redemption
Recommended Portal Settings
✅ Allow customers to cancel subscriptions
✅ Allow customers to switch plans
✅ Allow customers to update payment methods
✅ Show invoice history
✅ Allow promo code redemption during plan changes
✅ Require confirmation before cancelingTesting the Customer Portal
Prerequisites
- Have an active subscription (created via checkout flow)
- Be signed in to the app
- Environment variables set (from previous setups)
Test Steps
- Complete a checkout (use test card 4242 4242 4242 4242)
- Go to
/settingsin your app - Verify Subscription Status Card shows:
- Correct tier (Personal, Pro, or Team)
- Status badge (Active or Trial)
- Trial days remaining (if in trial)
- Plan limits
- Click "Manage Subscription"
- Verify redirect to Stripe Customer Portal
- In portal, try:
- Viewing invoice history
- Updating payment method (use another test card)
- Changing plan (upgrade/downgrade)
- Canceling subscription
- Click "Return to [your app]"
- Verify redirect back to
/settings - Check subscription status updated (may take a moment for webhook)
Test Scenarios
Scenario 1: Upgrade Plan
- Subscribe to Personal tier
- Go to settings → Manage Subscription
- In portal, upgrade to Pro tier
- Return to app
- Verify status card shows Pro tier
Scenario 2: Update Payment Method
- Have an active subscription
- Go to settings → Manage Subscription
- In portal, click "Update payment method"
- Add new test card: 5555 5555 5555 4444
- Set as default
- Return to app
Scenario 3: Cancel Subscription
- Have an active subscription
- Go to settings → Manage Subscription
- In portal, click "Cancel subscription"
- Choose "Cancel at end of period"
- Return to app
- Verify status card shows cancellation notice
Scenario 4: Reactivate Canceled
- Have a canceled subscription (pending end of period)
- Go to settings → Manage Subscription
- In portal, click "Renew subscription"
- Return to app
- Verify status card shows active again
Status Badge Indicators
The Subscription Status Card displays different badges based on subscription status:
| Status | Badge | Meaning |
|---|---|---|
active | 🟢 Active | Subscription is active and paid |
trialing | 🔵 Trial Active | Currently in free trial period |
past_due | 🟡 Payment Due | Payment failed, needs attention |
canceled | ⚪ Canceled | Subscription is canceled |
incomplete | ⚪ Incomplete | Payment incomplete |
Common Issues
"No subscription found" Error
Cause: User hasn't subscribed yet
Expected Behavior: Portal button should only show for subscribed users
Fix: The Subscription Status Card hides the manage button for free tier users and shows "View Plans" instead
"No Stripe customer ID found" Error
Cause: Subscription record missing Stripe customer ID
Should not happen with current implementation - checkout flow includes customer ID
If it happens: Check webhook logs to ensure customer.subscription.created was processed correctly
Portal Shows Wrong Information
Cause: Webhook not processed or Convex data out of sync
Fix:
- Check webhook logs for errors
- Verify webhook was received (check ngrok or Stripe dashboard)
- Check Convex database for subscription record
- Manually trigger webhook test from Stripe dashboard
Return URL Not Working
Cause: Base URL not configured correctly
Fix: Set NEXT_PUBLIC_BASE_URL in .env.local:
NEXT_PUBLIC_BASE_URL=http://localhost:3017Or for production:
NEXT_PUBLIC_BASE_URL=https://homepage.devSecurity Notes
- ✅ Authentication required (Clerk auth)
- ✅ User can only access their own subscription
- ✅ Stripe handles all payment processing
- ✅ Customer ID verified from Convex database
- ✅ Portal sessions expire after use
- ✅ Return URL validated by Stripe
Integration with Existing Features
Phase 1: Backend Tier Enforcement
- ✅ Limits enforced when creating pages/tabs
- ✅ Users blocked from exceeding limits
- ✅ Clear error messages with upgrade suggestions
Phase 2: Webhooks
- ✅ Subscription changes sync automatically
- ✅ Plan upgrades/downgrades reflected immediately
- ✅ Cancellations update status in Convex
Customer Portal (This Feature)
- ✅ Users can self-serve subscription management
- ✅ No support tickets needed for common tasks
- ✅ Seamless experience staying in Stripe's UI
Next Steps
After Customer Portal is tested:
-
⏳ Frontend Usage Indicators
- Show usage vs limits on dashboard
- Display progress bars
- Add usage badges
-
⏳ Upgrade Prompts
- Modal when hitting limits
- Inline upgrade CTAs
- Trial countdown reminders
-
⏳ Email Notifications
- Trial ending reminders
- Payment failed notices
- Subscription change confirmations
-
⏳ Switch to Production
- Use live Stripe keys
- Update webhook to production URL
- Test with real cards (small amounts)
Reference Documents:
docs/STRIPE_PRODUCT_SETUP.md- Product and price IDsdocs/STRIPE_WEBHOOK_SETUP.md- Webhook configurationdocs/STRIPE_CHECKOUT_SETUP.md- Checkout flowdocs/PHASE_1_IMPLEMENTATION_COMPLETE.md- Backend tier enforcementdocs/TIER_MANAGEMENT_PLAN.md- Complete implementation roadmap