One-time payments

Manage one-time payments with TurboStarter.

While not a typical SaaS billing model, TurboStarter supports one-time (one-off) payments.

One-time payments are useful when you want to sell products that aren't subscription-based, such as:

  • Lifetime access: products sold once, granting access forever.
  • Multiple purchases: one-off items/add-ons that can be bought multiple times.

Some of this will require custom code (e.g. fulfillment), but TurboStarter provides a solid foundation for handling checkout and syncing successful purchases into your app.

Configuration

One-time payments are represented as variants with model: BillingModel.ONE_TIME in your billing configuration.

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: [
        {
          /* 👇 This is the `priceId` from the provider (e.g. Stripe), `variantId` (e.g. Lemon Squeezy) or `productId` (e.g. Polar) */
          id: "price_1PpUagFQH4McJDTlHCzOmyT6",
          cost: 29900,
          currency: "usd",
          model: BillingModel.ONE_TIME,  
        },
      ],
    },
  ],
  ...
}) satisfies BillingConfig;

Let's break down the fields:

  • id: The unique identifier for the variant. This must match the identifier in the billing provider.
  • cost: The price amount in the smallest currency unit (e.g. cents). Displayed values are typically divided by 100.
  • currency: The currency code (defaults to usd).
  • model: The billing model for the variant. For one-time payments, it must be BillingModel.ONE_TIME.

Please remember that the cost is set for UI purposes. The billing provider handles the actual billing, so make sure the amount is correct in the provider.

Provider notes

  • Stripe: one-time purchases typically complete on checkout.session.completed. Your variant.id should match the Stripe Price ID (e.g. price_...). See Stripe setup.
  • Lemon Squeezy: one-time purchases typically emit order_created. Your variant.id should match the Lemon Squeezy Variant ID. See Lemon Squeezy setup.
  • Polar: one-time purchases typically emit order.created/order.updated. Your variant.id should match the Polar Product ID (Polar models each “variant” as a separate product). See Polar setup.

When a product is purchased, TurboStarter will create an order in the provider-agnostic order table - you can use this data to fulfill the order and grant access to the product.

How is this guide?

Last updated on

On this page

Ship your startup everywhere. In minutes.Get TurboStarter