For the complete documentation index, see llms.txt. Prefer markdown by appending.mdto documentation URLs or sendingAccept: text/markdown.
Subscriptions
Configure recurring plans with monthly and yearly variants, optional trials, and provider price IDs for Stripe, Lemon Squeezy, and Polar.
TurboStarter supports subscription billing (recurring payments) on the web across providers like Stripe, Lemon Squeezy, and Polar.
Subscriptions are configured in your billing config using:
- plans: what you sell (Free, Premium, Enterprise, etc.)
- variants: how you sell it (monthly, yearly, trials, etc.)
Configuration
Subscriptions are represented as variants with model: BillingModel.RECURRING.
The example below shows a standard flat recurring subscription. Per-seat and metered subscriptions use the same recurring model, but add extra fields such as type, meterId, or tier configuration. See Per-seat and Metered usage for those setups.
export const config = billingConfigSchema.parse({
plans: [
{
id: BillingPlan.PREMIUM,
name: "Premium",
description: "Become a power user and gain benefits",
badge: "Bestseller",
features: [
"Unlimited projects",
"Priority support",
"Advanced integrations",
"Team collaboration",
"Analytics dashboard",
],
variants: [
// Monthly
{
id: "price_monthly_or_variant_id",
cost: 1900,
currency: "usd",
type: BillingType.FLAT,
model: BillingModel.RECURRING,
interval: RecurringInterval.MONTH,
trialDays: 7,
},
// Yearly
{
id: "price_yearly_or_variant_id",
cost: 8900,
currency: "usd",
type: BillingType.FLAT,
model: BillingModel.RECURRING,
interval: RecurringInterval.YEAR,
trialDays: 7,
},
],
},
],
}) satisfies BillingConfig;Breaking down the fields:
id: Provider identifier for this recurring price/variant/product.cost: Amount in the smallest currency unit (e.g. cents). Used for UI; provider charges the real amount.currency: Currency code (defaults tousd).type: UsuallyBillingType.FLATfor a standard subscription. Other recurring billing types are available.model: Must beBillingModel.RECURRING.interval: Required for recurring variants (RecurringInterval.MONTH,RecurringInterval.YEAR, etc.).trialDays: Optional trial length in days.
Match IDs exactly
The variant.id value must match what your billing provider expects (Stripe price ID, Lemon Squeezy variant ID, Polar product ID, etc.). A mismatch is the #1 reason why a checkout can't be created.
Provider notes
- Stripe:
variant.idshould match a Stripe Price ID (price_...). Webhook events used for subscriptions includecustomer.subscription.*. See Stripe setup. - Lemon Squeezy:
variant.idshould match a Lemon Squeezy Variant ID. See Lemon Squeezy setup. - Polar:
variant.idshould match a Polar Product ID (Polar treats each “variant” as a separate product). Subscription events includesubscription.created/subscription.updated. See Polar setup.
How is this guide?
Last updated on