Your customers need to update their payment method or check their invoice history, and you don't want them flooding support. Stripe's Billing Portal is a hosted page that handles all of this for you. It's the fastest way to reduce billing-related support tickets while keeping customers in control.
What Billing Portal Does
Billing Portal is a customizable, hosted UI that Stripe manages for you. Instead of building your own billing dashboard, you redirect authenticated customers to a session URL where they can self-serve.
Understand what customers can do in the portal
Once in the portal, customers can update their Payment method, view and download Invoices, manage Subscription settings (pause, resume, cancel), update Billing information like address and email, and check Upcoming charges. You control which features are enabled.
// Create a billing portal session for a customer
const session = await stripe.billingPortal.sessions.create({
customer: 'cus_1234567890',
return_url: 'https://yourapp.com/account',
});
console.log(session.url); // Redirect customer hereWhy it beats building it yourself
Stripe handles PCI compliance, payment validation, and invoice rendering. You avoid maintaining forms for payment method updates and storing sensitive card data. Plus, Stripe updates the portal automatically when they ship new features—you get them for free.
Setting Up Billing Portal Sessions
To send a customer to the portal, you create a session and redirect them to the session URL. This is server-side—never expose your API key to the browser.
Retrieve the customer ID from your database
When a user logs into your app, fetch their Stripe customer ID. You stored this when you created their subscription or first payment. This is the customer parameter needed for the session.
const userId = req.user.id; // Your app's user ID
const stripeCustomerId = await getCustomerIdFromDb(userId);
if (!stripeCustomerId) {
return res.status(404).json({ error: 'Customer not found' });
}Create the portal session
Call stripe.billingPortal.sessions.create() with the customer ID and a return_url. The return_url is where Stripe sends them back after they finish (your account settings page).
const session = await stripe.billingPortal.sessions.create({
customer: stripeCustomerId,
return_url: 'https://yourapp.com/settings/billing',
});
res.json({ url: session.url });Redirect the customer
On the frontend, receive the session.url and redirect the customer via window.location.href or a server redirect. Stripe's portal is now handling the request.
fetch('/api/billing-portal', { method: 'POST' })
.then(res => res.json())
.then(data => window.location.href = data.url);Customizing Portal Behavior
By default, Billing Portal enables all features. You can restrict what customers see using the features parameter.
Disable features you don't want customers to control
Use the features object to disable Invoice download, Subscription cancellation, or Payment method updates. For example, if you don't want customers canceling mid-contract, set subscription_cancel.enabled to false.
const session = await stripe.billingPortal.sessions.create({
customer: stripeCustomerId,
return_url: 'https://yourapp.com/settings',
features: {
invoice_history: { enabled: true },
subscription_update: { enabled: false },
subscription_cancel: { enabled: false },
payment_method_update: { enabled: true },
},
});Create a reusable portal configuration
Instead of passing features on every session, create a Billing Portal Configuration once in the Dashboard or API. Then reference it by ID on all sessions. This keeps your code cleaner and makes future changes easier.
const config = await stripe.billingPortal.configurations.create({
business_profile: {
headline: 'Manage your subscription',
},
features: {
invoice_history: { enabled: true },
subscription_cancel: { enabled: true },
},
});
const session = await stripe.billingPortal.sessions.create({
customer: stripeCustomerId,
configuration: config.id,
return_url: 'https://yourapp.com/settings',
});Common Pitfalls
- Creating sessions without storing the customer ID first. You can't generate a billing portal session for a customer you haven't created in Stripe.
- Exposing your Stripe API key in frontend code. Session creation must happen server-side—your frontend calls your backend, which calls Stripe.
- Forgetting that sessions expire after 24 hours. Customer links in emails fail if they wait too long. Generate fresh sessions on-demand.
- Enabling all features without thinking about your business rules. If you don't want customers canceling during a contract, explicitly disable
subscription_cancel.
Wrapping Up
Billing Portal shifts subscription management to your customers and frees your team from repetitive support tasks. You create sessions server-side, Stripe handles the rest. If you want to track billing metrics like churn and MRR across all your tools, Product Analyst can help.