Files
myeasycms-v2/docs/billing/overview.mdoc
Giancarlo Buomprisco 7ebff31475 Next.js Supabase V3 (#463)
Version 3 of the kit:
- Radix UI replaced with Base UI (using the Shadcn UI patterns)
- next-intl replaces react-i18next
- enhanceAction deprecated; usage moved to next-safe-action
- main layout now wrapped with [locale] path segment
- Teams only mode
- Layout updates
- Zod v4
- Next.js 16.2
- Typescript 6
- All other dependencies updated
- Removed deprecated Edge CSRF
- Dynamic Github Action runner
2026-03-24 13:40:38 +08:00

268 lines
8.6 KiB
Plaintext

---
status: "published"
label: "How Billing Works"
title: "Billing in Next.js Supabase Turbo"
description: "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."
order: 0
---
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