# Payments

Stripe and Polar feed one server-verified entitlement model.

## Enable providers deliberately [#enable-providers-deliberately]

All providers start off. Set `BILLING_PROVIDERS` to the required comma-separated names: `stripe`, `polar`. Each listed provider must have its complete valid configuration. `BILLING_ENVIRONMENT` distinguishes SANDBOX from PRODUCTION; Stripe key mode must agree.

Stripe and Polar sell on the web. If both web providers are enabled, `BILLING_CHECKOUT_PROVIDER` chooses new checkouts; existing customers retain their provider portal. Visibility uses `billing.web`.

## Entitlements and webhooks [#entitlements-and-webhooks]

Paid Access belongs to the User, not an Organization. The server's entitlement read model projects enabled-provider rows in the current environment. Leftover store rows (for example `provider = 'revenuecat'`) stay in the table and are ignored. Stripe and Polar apply signed, deduplicated events in transactions.

| Provider | Webhook path          | Server secrets                                              |
| -------- | --------------------- | ----------------------------------------------------------- |
| Stripe   | `/api/billing/stripe` | STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PRICE_IDS  |
| Polar    | `/api/billing/polar`  | POLAR_ACCESS_TOKEN, POLAR_WEBHOOK_SECRET, POLAR_PRODUCT_IDS |

Raw webhook verification precedes payload parsing. A replay is a no-op, and a failed write rolls back its dedupe record so the provider can retry.

## Extend and test [#extend-and-test]

Add plans to the leaf `billing-plans.ts`, map provider product IDs in env, and add name/description translations. Checkout return URLs come from the server, never a client-supplied redirect. Test purchases in sandbox and verify `billing.status`, cancellation and webhook replay before enabling production visibility.

Stripe and Polar currently rely on webhooks plus the grace window; no scheduled pull reconciles their subscriptions. `billing.sync` re-reads that projection. See [ADR-0004](/docs/adr/0004-payments-providers).

## Key files [#key-files]

- [`packages/api/src/billing/settings.ts`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/packages/api/src/billing/settings.ts)
- [`packages/api/src/billing/web-http.ts`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/packages/api/src/billing/web-http.ts)
- [`packages/api/src/billing/status.ts`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/packages/api/src/billing/status.ts)
- [`packages/api/src/lib/billing-plans.ts`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/packages/api/src/lib/billing-plans.ts)
- [`docs/billing.md`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/docs/billing.md)

