Subscriptions

Learn how to manage subscriptions in your application.

For the complete documentation index, see llms.txt. Prefer markdown by appending .md to documentation URLs or sending Accept: text/markdown.

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.

index.ts
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 to usd).
  • type: Usually BillingType.FLAT for a standard subscription. Other recurring billing types are available.
  • model: Must be BillingModel.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.id should match a Stripe Price ID (price_...). Webhook events used for subscriptions include customer.subscription.*. See Stripe setup.
  • Lemon Squeezy: variant.id should match a Lemon Squeezy Variant ID. See Lemon Squeezy setup.
  • Polar: variant.id should match a Polar Product ID (Polar treats each “variant” as a separate product). Subscription events include subscription.created / subscription.updated. See Polar setup.

How is this guide?

Last updated on

On this page

Ship your startup everywhere. In minutes.Try TurboStarter