Leave Localhost logoLeave LocalhostDocs
Billing

Billing Overview

Leave Localhost includes a complete billing system that supports three payment providers out of the box: Polar, Stripe, and Lemon Squeezy. Billing is optional — the app works without any provider configured.

Leave Localhost includes a complete billing system that supports three payment providers out of the box: Polar, Stripe, and Lemon Squeezy. Billing is optional — the app works without any provider configured.

Architecture

┌─────────────────────────────────────────────────┐
│  Frontend (apps/app)                            │
│  Checkout → Plan selector → Customer portal     │
└──────────────┬──────────────────────────────────┘
               │ Convex actions (createCheckout, createCustomerPortal)

┌─────────────────────────────────────────────────┐
│  Billing Layer (packages/backend/convex)         │
│  ┌─────────────┐ ┌──────────────┐ ┌───────────┐ │
│  │ Plan Catalog │ │ Adapter      │ │ Grants    │ │
│  │ (catalog.ts) │ │ (adapter.ts) │ │ (grants.ts│ │
│  └─────────────┘ └──────────────┘ └───────────┘ │
│  ┌─────────────────────────────────────────────┐ │
│  │ Provider Mapping (env vars → plan keys)     │ │
│  └─────────────────────────────────────────────┘ │
└──────────────┬──────────────────────────────────┘
               │ Webhooks (http.ts routes)

┌─────────────────────────────────────────────────┐
│  Payment Provider (Polar / Stripe / LemonSqueezy)│
└─────────────────────────────────────────────────┘

Key Concepts

Plan Catalog

The plan catalog at billing/catalog.ts defines every available plan: Free, Pro Monthly, Pro Yearly, and Pro Lifetime. Each plan maps to a set of capabilities.

Provider Adapter

The BillingProviderAdapter interface abstracts checkout, customer portal, and cancellation behind three methods. Each provider implements this interface in its own subdirectory.

Grants

When a webhook event arrives, it is converted into billing grants — rows in the billing_grants table that grant specific capabilities to an organization. The rest of the app checks grants, never provider-specific subscription state.

Provider Selection

Billing is opt-in and ships disabled. Every provider integration is compiled into the deployment, so activating one is a single runtime choice:

  1. Set BILLING_PROVIDER to polar, stripe, or lemon_squeezy.
  2. Fill that provider's variables and register its webhook URL (see the per-provider guides).

Leaving BILLING_PROVIDER blank keeps billing in "unconfigured" mode: users see the Free plan, and checkout/portal/cancellation fail closed. BILLING_PROVIDER is the sole selector — switching providers is just changing the env var and redeploying; no source is mutated and no install/remove step exists.

Default Plans

Plan KeyDisplay NameIntervalCapabilities
freeFree(none)
pro_monthlyProMonthfeature.pro, workspace.members.invite, workspace.members.limit.10, billing.portal
pro_yearlyProYearSame as Pro Monthly
pro_lifetimePro LifetimeOne-timeSame as Pro Monthly

Billing Flow

  1. User clicks "Upgrade" in the app.
  2. Frontend calls createCheckout action with a planKey.
  3. The billing adapter creates a checkout session with the provider and returns a redirect URL.
  4. User completes payment on the provider's hosted checkout page.
  5. Provider sends a webhook event to /webhooks/<provider>.
  6. The webhook handler creates/updates grants in the billing_grants table.
  7. Capabilities are now active — the app reacts in real time.

Files

FilePurpose
billing.tsPublic queries and actions (listPlans, createCheckout, etc.)
billing/catalog.tsPlan definitions and capability mappings
billing/config.tsProvider selection from BILLING_PROVIDER
billing/integration.tsBillingProviderIntegration contract (adapter + webhook routes)
billing/providers.tsStatic registry of all provider integrations
billing/adapter.tsProvider adapter interface; resolves the active provider's adapter (fails closed)
billing/grants.tsGrant sync, creation, revocation
billing/providerMapping.tsMaps provider product/price IDs to plan keys
billing/subscriptions.tsSubscription state selection logic
billing/validators.tsBilling-specific Convex validators
billing/cleanup.tsOrganization deletion billing cleanup
billing/stripe/Stripe adapter, webhook handler, queries, mutations
billing/polar/Polar adapter, webhook handler, queries, mutations
billing/lemonSqueezy/Lemon Squeezy adapter, webhook handler, queries, mutations

Next Reads

On this page