Customer billing portal
A self-serve page where customers change plan, swap the card on file, cancel, and grab invoices without emailing you.
See it
What it is
A billing portal is the one page a paying customer can go to and fix their own money problems: swap the card on file, switch plan, update the billing address and tax ID, download past invoices, cancel. Every billing vendor ships a hosted one (Stripe calls it the Customer Portal, Paddle and Chargebee have equivalents), so the usual build is one backend endpoint that mints a short-lived portal session for the logged-in customer and redirects them to it. What the portal is allowed to do is configured in the vendor dashboard, not in your code, which surprises people the first time.
Reach for it the moment your support inbox fills with 'my card changed' and 'can you resend the receipt'. It is also quiet churn defense: a customer whose card expired will happily update it at 2am if there is a page to do it on, and will otherwise just lapse.
Two gotchas. The portal session URL is tied to one customer and expires fast, so generate it per click and never email, cache, or log it. And decide deliberately whether portal cancellation is immediate or cancel at period end, because the default is not always what your refund policy assumes. Plan changes made in the portal fire webhooks, so your entitlements need to listen rather than trusting whatever your database said at signup.
Ask AI for it
Add a self-serve billing portal to this app. Create a POST /billing/portal endpoint that resolves the Stripe customer id from the server session (never from the request body), creates a billing portal session with return_url pointing at /settings/billing, and redirects. Configure the portal to allow payment method update, invoice history download, address and tax ID edits, plan switching between my listed prices with proration, and cancellation set to cancel at period end with a reason survey. On the settings page show current plan, next renewal date, last four of the card, and a 'Manage billing' button. Handle the customer-with-no-subscription case with an upgrade prompt instead of an error, and sync plan changes back through the subscription webhooks rather than assuming the redirect succeeded.