Billing
Plans & Limits
How to define billing plans and gate features based on plan limits.
Configuration
Plans are defined in config/billing.config.ts. This is the single source of truth for pricing, features, and limits. The pricing page reads plans from this config and renders them automatically.
Each plan includes an id, name, price, interval, Stripe/Lemon Squeezy IDs, a features list, and an optional limits object.
Helper functions
import { getPlanById, getPlanByPriceId, getPlanByVariantId, getPlanLimit }
from "@/config/billing.config";| Function | Description |
|---|---|
getPlanById("starter") | Find plan by its config ID |
getPlanByPriceId("price_...") | Find plan by Stripe price ID |
getPlanByVariantId("123456") | Find plan by Lemon Squeezy variant ID |
getPlanLimit("starter", "requestsPerDay", 0) | Get a limit value with fallback |
Feature gating
Add limits to your plan config:
{
id: "starter",
limits: {
requestsPerDay: 100,
canExport: false,
},
}Then gate features in your API routes:
import { requireLimit } from "@/lib/auth/subscription-guard";
const { authorized, response } = await requireLimit("requestsPerDay", currentCount);
if (!authorized) return response;Or check a boolean feature flag:
import { getPlanLimit } from "@/config/billing.config";
import { getSubscriptionStatus } from "@/lib/auth/subscription-guard";
const { plan } = await getSubscriptionStatus();
const canExport = getPlanLimit(plan ?? "free", "canExport", false);Use -1 for unlimited numeric limits.
Free users
Users without a subscription row in the database are implicitly on the free tier. The planId defaults to "free" when no subscription exists.