Stripe or Creem

Both rails are wired. What actually differs, and how to switch.

Both providers are already implemented. The webhook routes verify, deduplicate and translate either payload into one internal PaymentEvent; your features handle the event and never import a provider client. So this is a choice you can defer, and change later without stranding anyone: PAYMENT_PROVIDER decides who takes new checkouts, while an existing subscription keeps renewing wherever it started, read off the row.

What differs

StripeCreem
RolePayment processor — you are the merchant of recordMerchant of record — they sell, you get paid
Sales tax and VATYours to handle, or add Stripe TaxCollected and remitted for you
Plan changes in the appUpgrade now for the difference, downgrade at period endThrough the provider's own portal
A refund of an upgradeUndoes it — price back, top-up clawed back
Local webhook testbun run stripe:listen, through the Stripe CLIA signed payload posted by the /creem skill
Catalog idsstripePriceId, one per tier and intervalcreemProductId, one per tier and interval
The switchPAYMENT_PROVIDER=stripePAYMENT_PROVIDER=creem

The one asymmetry worth planning around is plan changes. In-app upgrades and downgrades are Stripe-only (src/features/billing/plan-change.ts); a Creem subscriber who wants a different tier goes through Creem's portal. Everything else — checkout, renewals, refunds, cancellation, the credit grants that hang off each event — works identically on both.

Switching

PAYMENT_PROVIDER is read at boot, so set it in .dev.vars (local) and .prod.vars (production) and restart. If it is unset the template falls back to whichever key exists, preferring Stripe.

Nothing can be bought until the active provider has its key, and every purchase button reads that rather than showing a checkout that would fail — the product ships before the payment rail does, and a merchant account takes weeks.

Run the /stripe or /creem skill to build the catalog, write the ids into src/config/plans.ts, and prove the webhook end to end before you trust it.