# Billing in Next.js Supabase Turbo

> Complete guide to implementing billing in your Next.js Supabase SaaS. Configure subscriptions, one-off payments, metered usage, and per-seat pricing with Stripe, Lemon Squeezy, or Paddle.

*Canonical: https://makerkit.dev/docs/next-supabase-turbo/billing/overview*

---

Makerkit's billing system lets you accept payments through Stripe, Lemon Squeezy, or Paddle with a unified API. You define your pricing once in a schema, and the gateway routes requests to your chosen provider. Switching providers requires changing one environment variable.

## Quick Start

Set your billing provider:

```bash
NEXT_PUBLIC_BILLING_PROVIDER=stripe # or lemon-squeezy, paddle
```

Update the database configuration to match:

```sql
UPDATE public.config SET billing_provider = 'stripe';
```

Then [configure your billing schema](/docs/next-supabase-turbo/billing/billing-schema) with your products and pricing.

## Choose Your Provider

| Provider | Best For | Tax Handling | Multi-line Items |
|----------|----------|--------------|------------------|
| [Stripe](/docs/next-supabase-turbo/billing/stripe) | Maximum flexibility, global reach | You handle (or use Stripe Tax) | Yes |
| [Lemon Squeezy](/docs/next-supabase-turbo/billing/lemon-squeezy) | Simplicity, automatic tax compliance | Merchant of Record | No (1 per plan) |
| [Paddle](/docs/next-supabase-turbo/billing/paddle) | B2B SaaS, automatic tax compliance | Merchant of Record | No (flat + per-seat only) |

**Merchant of Record** means Lemon Squeezy and Paddle handle VAT, sales tax, and compliance globally. With Stripe, you're responsible for tax collection (though Stripe Tax can help).

## Supported Pricing Models

Makerkit supports four billing models out of the box:

### Flat Subscriptions

Fixed monthly or annual pricing. The most common SaaS model.

```tsx
{
  id: 'price_xxx',
  name: 'Pro Plan',
  cost: 29,
  type: 'flat',
}
```

[Learn more about configuring flat subscriptions →](/docs/next-supabase-turbo/billing/billing-schema#flat-subscriptions)

### Per-Seat Billing

Charge based on team size. Makerkit automatically updates seat counts when members join or leave.

```tsx
{
  id: 'price_xxx',
  name: 'Team',
  cost: 0,
  type: 'per_seat',
  tiers: [
    { upTo: 3, cost: 0 },      // First 3 seats free
    { upTo: 10, cost: 12 },    // $12/seat up to 10
    { upTo: 'unlimited', cost: 10 },
  ]
}
```

[Configure per-seat billing →](/docs/next-supabase-turbo/billing/per-seat-billing)

### Metered Usage

Charge based on consumption (API calls, storage, tokens). Report usage through the billing API.

```tsx
{
  id: 'price_xxx',
  name: 'API Requests',
  cost: 0,
  type: 'metered',
  unit: 'requests',
  tiers: [
    { upTo: 1000, cost: 0 },
    { upTo: 'unlimited', cost: 0.001 },
  ]
}
```

[Set up metered billing →](/docs/next-supabase-turbo/billing/metered-usage)

### One-Off Payments

Lifetime deals, add-ons, or credits. Stored in the `orders` table instead of `subscriptions`.

```tsx
{
  paymentType: 'one-time',
  lineItems: [{
    id: 'price_xxx',
    name: 'Lifetime Access',
    cost: 299,
    type: 'flat',
  }]
}
```

[Configure one-off payments →](/docs/next-supabase-turbo/billing/one-off-payments)

### Credit-Based Billing

For AI SaaS and token-based systems. Combine subscriptions with a credits table for consumption tracking.

[Implement credit-based billing →](/docs/next-supabase-turbo/billing/credit-based-billing)

## Architecture Overview

The billing system uses a provider-agnostic architecture:

```
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Your App       │────▶│  Gateway        │────▶│  Provider       │
│  (billing.config)     │  (routes requests)    │  (Stripe/LS/Paddle)
└─────────────────┘     └─────────────────┘     └─────────────────┘
                                                        │
                                                        ▼
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Database       │◀────│  Webhook Handler│◀────│  Webhook        │
│  (subscriptions)      │  (processes events)   │  (payment events)
└─────────────────┘     └─────────────────┘     └─────────────────┘
```

**Package structure:**

- `@kit/billing` (core): Schema validation, interfaces, types
- `@kit/billing-gateway`: Provider routing, unified API
- `@kit/stripe`: Stripe-specific implementation
- `@kit/lemon-squeezy`: Lemon Squeezy-specific implementation
- `@kit/paddle`: Paddle-specific implementation (plugin)

This abstraction means your application code stays the same regardless of provider. The billing schema defines what you sell, and each provider package handles the API specifics.

## Database Schema

Billing data is stored in four main tables:

| Table | Purpose |
|-------|---------|
| `billing_customers` | Links accounts to provider customer IDs |
| `subscriptions` | Active and historical subscription records |
| `subscription_items` | Line items within subscriptions (for per-seat, metered) |
| `orders` | One-off payment records |
| `order_items` | Items within one-off orders |

All tables have Row Level Security (RLS) enabled. Users can only read their own billing data.

## Configuration Files

### billing.config.ts

Your pricing schema lives at `apps/web/config/billing.config.ts`:

```tsx
import { createBillingSchema } from '@kit/billing';

export default createBillingSchema({
  provider: process.env.NEXT_PUBLIC_BILLING_PROVIDER,
  products: [
    {
      id: 'starter',
      name: 'Starter',
      description: 'For individuals',
      currency: 'USD',
      plans: [/* ... */],
    },
  ],
});
```

[Full billing schema documentation →](/docs/next-supabase-turbo/billing/billing-schema)

### Environment Variables

Each provider requires specific environment variables:

**Stripe:**
```bash
STRIPE_SECRET_KEY=sk_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_...
```

**Lemon Squeezy:**
```bash
LEMON_SQUEEZY_SECRET_KEY=...
LEMON_SQUEEZY_SIGNING_SECRET=...
LEMON_SQUEEZY_STORE_ID=...
```

**Paddle:**
```bash
PADDLE_API_KEY=...
PADDLE_WEBHOOK_SECRET_KEY=...
NEXT_PUBLIC_PADDLE_CLIENT_TOKEN=...
```

## Common Tasks

### Check if an account has a subscription

```tsx
import { createAccountsApi } from '@kit/accounts/api';

const api = createAccountsApi(supabaseClient);
const subscription = await api.getSubscription(accountId);

if (subscription?.status === 'active') {
  // User has active subscription
}
```

### Create a checkout session

```tsx
import { createBillingGatewayService } from '@kit/billing-gateway';

const service = createBillingGatewayService(provider);
const { checkoutToken } = await service.createCheckoutSession({
  accountId,
  plan,
  returnUrl: `${origin}/billing/return`,
  customerEmail: user.email,
});
```

### Handle billing webhooks

Webhooks are processed at `/api/billing/webhook`. Extend the handler for custom logic:

```tsx
await service.handleWebhookEvent(request, {
  onCheckoutSessionCompleted: async (subscription) => {
    // Send welcome email, provision resources, etc.
  },
  onSubscriptionDeleted: async (subscriptionId) => {
    // Clean up, send cancellation email, etc.
  },
});
```

[Full webhook documentation →](/docs/next-supabase-turbo/billing/billing-webhooks)

## Next Steps

1. **[Configure your billing schema](/docs/next-supabase-turbo/billing/billing-schema)** to define your products and pricing
2. **Set up your payment provider:** [Stripe](/docs/next-supabase-turbo/billing/stripe), [Lemon Squeezy](/docs/next-supabase-turbo/billing/lemon-squeezy), or [Paddle](/docs/next-supabase-turbo/billing/paddle)
3. **[Handle webhooks](/docs/next-supabase-turbo/billing/billing-webhooks)** for payment events
4. **[Use the billing API](/docs/next-supabase-turbo/billing/billing-api)** to manage subscriptions programmatically

For advanced use cases:
- [Per-seat billing](/docs/next-supabase-turbo/billing/per-seat-billing) for team-based pricing
- [Metered usage](/docs/next-supabase-turbo/billing/metered-usage) for consumption-based billing
- [Credit-based billing](/docs/next-supabase-turbo/billing/credit-based-billing) for AI/token systems
- [Custom integrations](/docs/next-supabase-turbo/billing/custom-integration) for other payment providers
