Billing System Setup Guide

This guide explains how to set up and configure the Stripe billing system for homepage.dev.

Overview

The billing system provides three subscription tiers:

  • Free: 5 items, 1 workspace (permanent, no credit card)
  • Pro: $15/month - 100 items, 3 workspaces, advanced features
  • Business: $79/month - Unlimited everything, all features

Architecture

  • Convex: Subscription data storage (source of truth)
  • Clerk: User metadata for fast feature access (cached)
  • Stripe: Payment processing and subscription management

Setup Steps

1. Create Stripe Account

  1. Sign up at https://stripe.com
  2. Get your API keys from the Stripe Dashboard
  3. Use test mode for development

2. Create Products in Stripe Dashboard

Pro Plan

  1. Navigate to ProductsAdd Product
  2. Name: Pro Plan
  3. Description: Perfect for professionals and small teams
  4. Create two prices:
    • Monthly: $15.00 USD (recurring monthly)
    • Annual: $144.00 USD (recurring annually)
  5. Copy the Price IDs for both

Business Plan

  1. Navigate to ProductsAdd Product
  2. Name: Business Plan
  3. Description: For teams and organizations
  4. Create two prices:
    • Monthly: $79.00 USD (recurring monthly)
    • Annual: $790.00 USD (recurring annually)
  5. Copy the Price IDs for both

3. Configure Environment Variables

Add these to your .env.local file:

# Stripe Keys (Test Mode for Development)
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxx

# Stripe Price IDs (from step 2)
NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PRICE_ID=price_xxxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_STRIPE_PRO_ANNUAL_PRICE_ID=price_xxxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PRICE_ID=price_xxxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_STRIPE_BUSINESS_ANNUAL_PRICE_ID=price_xxxxxxxxxxxxxxxxxxxxx

# App URL (for redirects)
NEXT_PUBLIC_APP_URL=http://localhost:3017

For Production, replace with live mode keys:

STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxxxxxxxxxxxxxxxxxxxx

4. Set Up Webhooks (Required for Subscription Updates)

Local Development (using Stripe CLI)

  1. Install Stripe CLI: https://stripe.com/docs/stripe-cli
  2. Login: stripe login
  3. Forward webhooks to local server:
    stripe listen --forward-to localhost:3017/api/webhooks/stripe
  4. Copy the webhook signing secret from the output:
    Ready! Your webhook signing secret is whsec_xxxxxxxxxxxxxxxxxxxxx
  5. Add to .env.local:
    STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxx

Production Deployment

  1. Deploy your application
  2. Navigate to Stripe Dashboard → DevelopersWebhooks
  3. Click Add Endpoint
  4. Enter your webhook URL: https://yourdomain.com/api/webhooks/stripe
  5. Select events to listen for:
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
    • invoice.payment_succeeded
    • invoice.payment_failed
  6. Copy the signing secret and add to production environment variables

5. Initialize Free Tier for Users

Set up Clerk webhook to auto-create subscriptions on user signup:

  1. Create webhook endpoint: /api/webhooks/clerk (TODO: implement)
  2. Listen for user.created event
  3. Call Convex subscriptions.create mutation with tier="free"

Option B: Migration Script

For existing users, run a one-time migration:

// convex/migrations/initializeSubscriptions.ts
// TODO: Create migration script to add free subscriptions for existing users

6. Test the System

Test Card Numbers (Stripe Test Mode)

  • Success: 4242 4242 4242 4242
  • Decline: 4000 0000 0000 0002
  • Requires Authentication: 4000 0025 0000 3155

Test Flow

  1. Sign in to your app
  2. Navigate to Profile → Billing
  3. Click "Upgrade to Pro"
  4. Enter test card details
  5. Complete checkout
  6. Verify:
    • Subscription updated in Convex
    • Clerk metadata updated
    • Features unlocked in app
  1. Navigate to Stripe Dashboard → SettingsTax
  2. Enable Stripe Tax
  3. Configure tax IDs for your business
  4. Stripe will automatically calculate and collect taxes

Feature Gating

Client-Side (React Components)

import { useHasFeature, useUsageLimit } from "@/lib/subscription/hooks"

function MyComponent() {
  const hasAPI = useHasFeature("apiAccess")
  const itemLimit = useUsageLimit("items")

  if (!hasAPI) {
    return <UpgradePrompt feature="API Access" />
  }

  return <div>API content here...</div>
}

Server-Side (Server Actions)

import { requireFeature, checkUsageLimit } from "@/lib/subscription/server"

export async function createItem() {
  // Check if user has feature
  await requireFeature("advancedAnalytics")

  // Check usage limits
  const currentItems = await getCurrentItemCount()
  await requireUnderLimit("items", currentItems)

  // Create item...
}

Monitoring & Analytics

Key Metrics Dashboard

Track these metrics in your admin panel:

  • MRR (Monthly Recurring Revenue)
  • Churn rate
  • Upgrade conversion rate (Free → Pro → Business)
  • Average Revenue Per User (ARPU)

Convex Query Example

import { api } from "@/convex/_generated/api"
import { useQuery } from "convex/react"

export function useMRR() {
  return useQuery(api.subscriptions.getStats)
}

Troubleshooting

Webhook Not Receiving Events

  1. Check Stripe CLI is running: stripe listen ...
  2. Verify webhook secret in .env.local
  3. Check webhook endpoint logs
  4. Test webhook manually in Stripe Dashboard

Subscription Not Updating

  1. Check Convex logs for errors
  2. Verify Clerk user has publicMetadata field
  3. Check webhook signature validation
  4. Review Stripe Dashboard → Events for webhook delivery status

TypeScript Errors

If you see Stripe API version errors:

  1. Check lib/subscription/stripe.ts - API version should match installed stripe package
  2. Update if needed: pnpm add stripe@latest

Security Checklist

  • Webhook signatures verified
  • STRIPE_SECRET_KEY stored securely (not in git)
  • Server actions protected with await auth()
  • Feature gates on both client AND server side
  • Stripe Customer Portal enabled for payment management
  • PCI compliance (never store card details)

Next Steps

  1. Test thoroughly in Stripe test mode
  2. Set up monitoring for failed payments
  3. Configure email notifications for subscription events
  4. Create upgrade prompts throughout your app
  5. Add usage indicators to show limits
  6. Deploy to production and test with real cards

Support

On this page