6 min read

What Is Billing Portal in Stripe

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.

javascript
// 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 here
The session.url is where you send the customer.

Why 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.

Tip: Billing Portal only works with authenticated customers you create via the Stripe API. If you're using Hosted Checkout, you'll need a way to identify customers on return (usually via session email).

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.

javascript
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' });
}
You store the Stripe customer ID when onboarding a customer.

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).

javascript
const session = await stripe.billingPortal.sessions.create({
  customer: stripeCustomerId,
  return_url: 'https://yourapp.com/settings/billing',
});

res.json({ url: session.url });
Send session.url to the frontend to redirect the customer.

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.

javascript
fetch('/api/billing-portal', { method: 'POST' })
  .then(res => res.json())
  .then(data => window.location.href = data.url);
Redirect the customer to manage their subscription.
Watch out: Sessions expire after 24 hours. If a customer takes too long, they'll get an error. Create fresh sessions each time they click the link—they're free.

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.

javascript
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 },
  },
});
Configure which features are available in the portal.

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.

javascript
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',
});
Reuse a configuration ID instead of repeating features.
Tip: Test in your Stripe test environment first. Use test customer IDs and test mode API keys. The UI looks identical but no real transactions occur.

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.

Track these metrics automatically

Product Analyst connects to your stack and surfaces the insights that matter.

Try Product Analyst — Free