Stripe customer portal: self-serve billing without building it
Shows how to fix stripe customer portal: self-serve billing without building it. Use it when you hit this exact problem. Skip it when your error message or symptom looks different.
TL;DR
Building it yourself means rebuilding plan changes, payment method updates, and cancellation flows with all their edge cases. Configure what customers can do in the Dashboard's portal settings (or pass a configuration ID): allow plan changes, payment method updates, cancellation with reasons, invoice history.
When to use
You are seeing this: Every subscription product needs a "manage my subscription" page. Use this skill when you run into "Stripe customer portal: self-serve billing without building it".
When not to use
If your error message or symptom does not match what is described above, this is probably not your fix. Search for your exact error text instead of forcing this one to fit.
Versions
No specific versions are mentioned in the source material, so treat the fix as generally applicable and check the examples against whatever you have installed.
Stripe customer portal: the billing settings page you do not build
Every subscription product needs a "manage my subscription" page. Building it yourself means rebuilding plan changes, payment method updates, and cancellation flows with all their edge cases. The customer portal is Stripe's hosted version. Use it.
The basics
- Create a portal session:
POST /v1/billing_portal/sessionswith thecustomerID and areturn_urlpointing back to your app. The responseurlis a one-time link; redirect the customer to it. - Sessions are short-lived and single-customer. Create one per visit, server-side, for the logged-in customer. Never expose one customer's session URL to another.
- Configure what customers can do in the Dashboard's portal settings (or pass a
configurationID): allow plan changes, payment method updates, cancellation with reasons, invoice history.
Deep links
- Pass
flow_datawhen creating the session to drop the customer directly into a specific flow, for example subscription update or cancellation, instead of the portal homepage. - Use deep links from your app's UI where the intent is already known ("Change plan" goes straight to the plan-change flow).
Rules
- The portal handles the Stripe side; your app still needs to react to the resulting webhooks (
customer.subscription.updated,customer.subscription.deleted). The portal does not call your code. - Test the cancellation flow end to end. The most common bug is the app not handling
customer.subscription.deletedbecause "we tested upgrades, not cancels." - Do not build a custom billing settings page until the portal demonstrably cannot do what you need. It covers the 90 percent case.