Swapping providers
The seam is the module boundary: the rest of the app only knows
BillingService.getEntitlement(userId) and the two redirect endpoints.
To use another provider, keep billing_subscriptions + the entitlement
shape and replace the Stripe specifics:
- Checkout/portal session creation in
billing.service.ts→ your provider's hosted equivalents. billing-webhook.service.tsandbilling-reversal.service.ts→ your provider's webhook verification and event names. Keep the guarded-upsert pattern: every provider redelivers and reorders webhooks.billing.config.tsenv surface + the boot validation.
Mind the merchant-of-record difference: with MoR providers (Paddle, Lemon Squeezy) the provider is the seller and handles VAT; with Stripe you are the merchant (Stripe Tax calculates, you register/file).