Skip to main content

Billing

Billing providers and webhook setup.

Billing

SaaS Starter supports multiple billing providers switchable via a single environment variable.

Provider selection

# .env
BILLING_PROVIDER=stripe   # stripe | paddle

| Provider | Best for | Env var prefix | |---|---|---| | Stripe | Most flexibility, global cards, developer-friendly | STRIPE_ | | Paddle | Merchant of Record, EU VAT & tax handled automatically | PADDLE_ | | LemonSqueezy | Fast digital product checkout | LEMONSQUEEZY_ | | Polar | Merchant-of-record and subscriptions | POLAR_ |

Stripe setup

STRIPE_SECRET_KEY=your-stripe-secret-key
STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret
STRIPE_PUBLISHABLE_KEY=your-stripe-publishable-key

Run the Stripe setup script to sync products/prices:

pnpm run setup:stripe
# or interactively:
pnpm cli init

Paddle setup

PADDLE_API_KEY=your-paddle-api-key
PADDLE_WEBHOOK_SECRET=your-paddle-webhook-secret
PADDLE_CLIENT_TOKEN=your-paddle-client-token

Webhook handling

Provider webhook endpoints:

POST /api/webhooks/stripe
POST /api/webhooks/paddle
POST /api/webhooks/lemonsqueezy
POST /api/webhooks/polar

Events handled automatically:

  • subscription.created → activate plan, emit subscription:created event
  • subscription.updated → update plan/status
  • subscription.canceled → downgrade, emit subscription:canceled event
  • payment.succeeded → add AI credits (if configured)
  • payment.failed → notify user

Plan configuration

Plans are defined in packages/shared/src/plans.ts:

export const PLANS = {
  free: {
    limits: {
      aiTokensPerMonth: 50_000,
      teamMembers: 3,
      projects: 5,
    },
    features: ['aiChat', 'analytics'],
  },
  pro: { ... },
  business: { ... },
}

The CLI pnpm cli init walks you through billing provider selection and automatically runs the appropriate setup script.