GenerateSaaS

Billing & Payments

Sell subscriptions, one-time products, and credits through one provider-agnostic billing layer.

Billing lives in @repo/payments and is gated by config.payment in packages/config/src/index.ts, wired into auth through the Better Auth billing plugin. One provider-agnostic layer covers recurring subscriptions, one-time products, prepaid credits, and per-organization billing - so checkout, webhooks, and entitlement checks stay identical whether you run Stripe or Polar.

The flag

config.payment is a discriminated union: { enabled: false } or { enabled: true; provider; bannedCountries? }.

KeyTypeDefaultDescription
enabledbooleantrueMaster switch for the billing surface.
provider"stripe" | "polar""stripe"Selected at build time; inactive provider code is bundler-pruned.
bannedCountriesstring[]["BY","CU","IR","KP","RU","SY"]ISO codes blocked from checkout.

When enabled is false, createBillingPlugins() returns [], the provider barrel resolves to a no-op that throws on any charge, and checkout plus dashboard credits chrome disappear.

Disabling billing does not hide the marketing pricing page - those plan cards always render from pricingConfig. Remove or edit that page separately if you don't sell anything.

Capabilities

Each area maps to its own provider-agnostic module and dashboard view.

Webhooks & checkout guards

Webhooks reconcile state; authorizeReference runs server-side before any charge.

GuardBehavior
WebhooksKeep subscription, payment, and credit state in sync. The active provider owns one endpoint and one signing secret - register it or events never reconcile.
Banned countriesReject checkout when the CDN country header (cf-ipcountry, x-vercel-ip-country, cloudfront-viewer-country) or the user-profile country is in bannedCountries.
Lifetime plansBlock new subscription checkouts for any entity already on a lifetime plan.
Org roleWhen billingScope is organization, limit purchases to owner / admin members; otherwise the reference must equal the user's own ID.

Organization billing requires config.tenancy.multiTenant: true and config.tenancy.billingScope: "organization". Both default to user-scoped (billingScope: "user"), so the team owns nothing until you opt in.

Frequently asked questions

Stripe or Polar? Pick Stripe for the broadest ecosystem and lowest fees when you handle your own tax. Pick Polar when you want a merchant-of-record to take on VAT/sales-tax remittance for you.

How do I disable billing entirely? Set config.payment.enabled to false. Checkout, the active provider, and dashboard billing UI disappear; remember the marketing pricing page is separate content.

Where do entitlement checks happen? Server-side, through Billing(...) and the Better Auth billing plugin - read the active subscription, owned products, or credit balance rather than trusting client state.

Next steps

On this page