GuideOctober 31, 2026·17 min read

Stripe Customer Portal: How to Let Customers Update a Failed Payment Method (2026 Guide)

Sinh Yang

Sinh Yang

Founder of Revova

A failed-payment email leading to a signed link on your own domain, then to a Stripe-hosted page where the customer saves a new card

The fastest way to let a customer fix a failed payment in Stripe is the customer portal: a Stripe-hosted page where they replace their card in under a minute, with no login screen to build. You can switch it on in the Dashboard, let Stripe's own emails link to it, or create a one-purpose portal session from your server that opens straight on "add a new payment method." The part most guides skip is what happens next: whether the next retry actually uses the new card.

A failed subscription payment is a customer problem you can only solve if they act. The decline is usually mundane (an expired card, a replaced card, a limit), the customer often has no idea, and every extra click between "your payment failed" and "your new card is saved" loses some of them. If you are searching for how the Stripe billing portal fits into that moment, this guide goes from the no-code setup to a deep link built with the API, then to the failure mode that catches experienced teams: a card updated in the portal that Stripe never uses. It builds on what we cover in how Smart Retries work and how to recover failed Stripe payments, and everything about Stripe behaviour here comes from Stripe's own documentation, read on 31 October 2026.

Key takeaways

  • The customer portal is a Stripe-hosted page for payment methods, invoices and subscriptions. You can launch it with a no-code login link or from the API.
  • For failed payments, create a session with flow_data.type = payment_method_update: a focused page that only adds a card and sets it as the customer default.
  • Stripe tries the subscription default payment method before the customer default. A card saved at the customer level can be ignored if the subscription has its own.
  • Session URLs live for minutes. Put a link to your own endpoint in emails and create the session when the customer clicks, after you have checked who they are.
5 min
How long a new, unused portal session stays valid
1 month
Before expiry that Stripe’s expiring-card email goes out
30 days
Life of an unused Dashboard payment-update link

What is the Stripe customer portal, and why does it matter for failed payments?

The customer portal is a Stripe-hosted, co-branded page where your customers manage their own account. Stripe's documentation lists what customers can do there: update billing information including tax IDs, update payment methods, update subscriptions, cancel immediately or at the end of the period, and pay, download or view current and past invoices. It localises itself to the customer's preferred language, takes your icon, logo and colours from your branding settings, and works with Checkout, Payment Links, Connect, Invoices and Billing.

For revenue recovery the important line is the first one: customers can update their payment method. Most involuntary churn starts with a card that no longer works. If the fix is one click away, a share of customers will do it the moment they read your email. If the fix means finding a login page, resetting a password and hunting through settings, many will not. We looked at how big that pile is in what involuntary churn is and how much of your churn it explains; the portal is the cheapest part of the fix because Stripe already built it.

A person holding a bank card in one hand while typing on a laptop keyboard, with a second card lying on the desk
The moment that matters: a customer with a card in hand and a laptop open. Every extra step before the card is saved is a chance to lose them. Illustrative photo, not a Revova customer. Photo: European Commission (Bogdan Hoyaux), CC BY 4.0, via Wikimedia Commons.

What are the ways to get a customer to the update-card page?

Stripe gives you five different doors to a payment-method update, and they are not interchangeable. They differ in how much code you write, how long the link lives, and whether the customer ends on a page that only asks for a card or on a full account dashboard.

RouteHow it worksLink life and limitsBest for
Stripe failed-payment emailTurn on Send emails when card payments fail; the email links to a Stripe-hosted page or the portalStripe controls the template and timing; hosted-page links stop working if the subscription ends as canceled, incomplete_expired or unpaidA zero-code baseline
Portal login linkActivate a shareable link; customers enter their email and Stripe sends a login linkNeeds an email that matches a Customer; ends on the full portal home pageFooters, help pages, a quick start
Portal session (API)Your server calls billing_portal/sessions for one customer and redirects to the returned URLUnused session expires after 5 minutes; you must authenticate the customer firstYour own emails and in-app banners
Portal flow: payment_method_updateA session with flow_data.type set; shows only the add-a-card step and hides the rest of the portalSame session limits; you choose where the customer lands afterwardsFailed-payment and expiring-card emails
Dashboard payment update linkOn a subscription, choose Share payment update link and email or copy itSingle use, cards only, expires after 30 days, subscription must be active, past_due or trialingOne-off help for a single customer

If you only want something running today, the first row costs nothing. If you want control over the copy, the timing and the exact page the customer lands on, you will end up on rows three and four. The rest of this guide takes them in that order.

How do you set up the portal without writing code?

You can have a working update-card path in about ten minutes, and it is worth doing even if you plan to build something custom later, because it becomes your fallback. Follow Stripe's no-code route:

  1. Activate the link. On the customer portal configuration page of the Dashboard, click Activate link in the Ways to get started section.
  2. Check what customers may do. The Payment methods setting (let your customer update their payment method information) is on by default. Decide separately whether the portal may also cancel subscriptions, change plans or edit billing details, because each is a toggle.
  3. Brand it. Upload your icon or logo and set colours in Branding settings, and review your public business name, which the portal shows. A customer who lands on an unfamiliar page from an email is more likely to close it.
  4. Point Stripe's emails at it. In the subscriptions and emails settings, turn on failed-payment emails and expiring-card emails and choose where their link goes: a Stripe-hosted page or your own page. Stripe sends the expiring-card email one month before the card on file expires.
  5. Share the login link where customers look. Put it in your help centre, receipts and account footer. Customers enter their email address and Stripe emails them a login link.

Two details that quietly break the login link

The login link finds the customer by email address. If the address does not match an existing Customer record, no email arrives and the customer concludes your system is broken. If several customers share an address, Stripe picks the most recently created one that has an active subscription. You can prefill the address with a prefilled_email query parameter, which saves a step when you send the link yourself, but customers cannot change their email through this link.

The no-code route has one weakness for recovery work: the login link lands on the portal home page. A customer who clicked "update your card" now sees a menu of everything they could do, including cancel. Sometimes that is fine. For a failed payment you usually want the single-purpose version.

Create a portal session with a flow. Stripe defines a flow as a customizable deep link into the portal: the customer goes directly to the page for one action, the navigation to the rest of the portal is hidden, and you decide where they land afterwards (back to your site immediately, a hosted confirmation page or the portal home page). The flow type for our case is payment_method_update, which lets the customer add a new payment method and sets it as the customer's default.

There are two requirements from Stripe's integration guide. You need the customer's ID and a return_url (required unless the Dashboard configuration already has a default return URL), and you must authenticate the customer on your side before creating the session. The snippet below is a starting point in a Next.js route handler:

// app/api/billing/update-card/route.ts  (Next.js route handler, simplified)
import Stripe from 'stripe'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)

export async function POST(req: Request) {
  // 1. Authenticate: who is calling, and which Stripe customer is theirs?
  const user = await getSignedInUser(req)           // your own auth
  if (!user?.stripeCustomerId) return new Response('Unauthorized', { status: 401 })

  // 2. Create a single-purpose portal session for that customer only.
  const session = await stripe.billingPortal.sessions.create({
    customer: user.stripeCustomerId,                 // from your database, not the request
    return_url: 'https://example.com/account?card=updated',
    flow_data: { type: 'payment_method_update' },
  })

  // 3. Send the customer to Stripe's hosted page.
  return Response.redirect(session.url, 303)
}
A simplified route: replace getSignedInUser with your real authentication, keep the Stripe customer ID server-side, and add error handling.

Notice where the customer ID comes from: your database, keyed by the signed-in user, never from the request. The same rule is why a plain link such as /update-card?customer=cus_123 in an email is dangerous. Anyone who edits the ID in the address bar would open someone else's billing page. We come back to safe email links below.

Portal configurations for different customers

If you want different behaviour for different groups, such as a stricter portal for monthly customers and one that allows plan changes for annual customers, you can create several portal configurations with the API and select one when you create a session. Stripe keeps separate sets of configurations for live mode and each sandbox, so changing one never alters the other. That is handy for testing, and it is a reason to check that you configured the live portal before you launch.

Why does Stripe sometimes ignore the card the customer just added?

Because "the customer's default payment method" is only one of several places a payment method can live, and Stripe uses a fixed order. When Stripe retries a failed subscription payment it uses the first available method in this list:

PriorityPayment method used for a retryAPI attribute
1Subscription default payment methodsubscription.default_payment_method
2Subscription default payment sourcesubscription.default_source
3Customer default payment methodcustomer.invoice_settings.default_payment_method
4(Legacy) Customer default payment sourcecustomer.default_source

Now put the portal flow next to that table. The payment_method_update flow sets the new card as customer.invoice_settings.default_payment_method, which is priority 3. If the subscription was created with its own default_payment_method, which is common when a subscription is created through Checkout or from an API call that passes one, priority 1 still points at the old, failing card. Stripe's Smart Retries documentation spells out the consequence: if a subscription has a default payment method and you only update the customer-level one, Stripe continues to retry on the subscription's method. Its advice is to update the field where the previous payment failed.

From the customer's side, they did everything right: they got your email, updated the card and saw a success page. From yours, the next retry fails again for the same reason, the sequence carries on as if nothing happened, and the customer gets another "your payment failed" email after fixing it. That is the fastest way to turn a recoverable customer into an angry one.

Flow from a failed-payment email to your own link, to a Stripe portal session with the payment_method_update flow, to a customer.updated webhook, with a check that the subscription default payment method is not overriding the new card
The path from email to a card that is actually used. The last step, checking for a subscription-level override, is the one most setups skip.

How to check for an override

  1. When customer.updated arrives, read invoice_settings.default_payment_method to see the new card. Stripe's portal guide lists this event for exactly that purpose.
  2. Fetch the customer's active and past_due subscriptions and read each default_payment_method.
  3. If a subscription has one and it is not the new card, either clear it so the customer default applies, or set it to the new card. Stripe's portal documentation also notes that customers can remove an override themselves in the portal, so the value can change under you.
  4. Record the outcome. If you cannot tell whether the next retry will use the new card, the safe move is to tell the customer the truth: "Your new card is saved, and we will try it on the next scheduled attempt."

Account Updater and card network tokens add another layer, because a bank can refresh a card number without the customer doing anything. If you are curious how often that quietly fixes the problem, read Account Updater explained.

Does the failed invoice get paid as soon as the card is updated?

Do not assume so. Stripe's description of the hosted page that failed-payment emails link to says the customer can update the payment method for the relevant subscription and pay any outstanding invoices if applicable; otherwise the new method is used the next time a scheduled payment is attempted. The portal's payment-method flow, by contrast, is documented as setting the default and returning the customer to wherever you point it. Nothing in those pages promises an immediate charge of the open invoice.

If your goal is the customer's money today rather than at the next retry, you have two documented options. In the Dashboard, open the invoice details page and click Charge customer. Through the API, call the invoice pay endpoint, which attempts payment and emits either invoice.paid or invoice.payment_failed depending on the outcome. A recovery system that listens for the payment-method change can call that endpoint once, then fall back to the normal schedule. For the statuses an invoice moves through while this happens, see Stripe invoice statuses explained, and for the subscription side, past_due vs unpaid vs canceled.

One honest caveat on retry timing

Stripe does not retry some failures at all. Its documentation lists conditions such as no available payment method, a hard decline code from the issuer, India-issued cards and a disconnected Connect account. A new card fixes the first. A hard decline on the old card is also fixed by a new card, but only once there is one to try. Our guide to soft versus hard declines explains which failures need the customer and which only need time.

What can the portal not do?

The portal is not a general-purpose billing UI, and its documented limits matter most in the cases where you need it. Verify these against your own setup before you promise customers a one-click fix:

  • Some payment methods cannot be added by hand. Customers cannot manually enter a new bank account for ACH Direct Debit in the portal, and the table lists similar limits for PayTo and Vipps. If your base pays by bank debit, a failed mandate needs a different recovery path.
  • It cannot be embedded. You cannot display the portal inside an iframe, so it has to be a full-page redirect. In a single-page app, plan the return path.
  • Some subscriptions can be cancelled but not edited. Subscriptions with multiple products, usage-based billing, trials, sending invoices for collection, or unsupported payment methods can be cancelled in the portal but not updated.
  • Scheduled changes lock the subscription. Customers cannot update or cancel a subscription that has a scheduled update from a subscription schedule.
  • Trials end when edited. By default, a customer who modifies a trialing subscription ends the free trial and triggers an invoice for immediate payment unless you set the trial update behaviour to continue the trial.
  • Send-invoice subscriptions have no hosted update link. Stripe does not include the hosted-page link in emails for subscriptions set to manually send an invoice.

Unsupported or limited payment methods deserve a sentence in your support docs, not a surprise. Customers who bank by direct debit get a clear alternative ("reply to this email and we will send you a new mandate link") far more cheaply than a lost renewal.

A countertop card payment terminal whose screen reads that no merchant card was detected
A failed card payment looks like this from the other side of the counter. Online, your customer sees the decline and no explanation unless you give them one. Photo: VulcanSphere, CC BY 4.0, via Wikimedia Commons.

Link to your own address, not to a Stripe session URL, and verify the link before you create the session. Two facts from Stripe force this design. A session URL created when you send the email will have expired by the time most customers read it, since unused sessions last five minutes. And a session gives access to a customer's billing details, so creating one for whoever types a customer ID into a URL is an access-control hole.

The pattern that works is a signed link. At send time you build a URL on your domain that contains the customer reference and a signature computed with a secret only your server knows. On click, your endpoint recomputes the signature, rejects the request if it does not match, and only then creates the portal session and redirects. The customer ID cannot be swapped because the signature would no longer match. Revova's pre-dunning emails work this way: the link carries a token that binds the merchant and the customer together, and the endpoint creates a Stripe billing portal session on click and falls back to a safe page if the token is invalid. Its failed-payment emails link to the Stripe-hosted invoice page for the specific invoice instead, which pays that invoice directly. You can implement the same idea in a few dozen lines if you are building your own flow. Add an expiry to the token and rate-limit the endpoint.

✓ Pros

  • +Works for every customer with one click, no password
  • +Stripe handles card entry, 3D Secure and localisation
  • +No card data touches your servers, which keeps PCI scope small
  • +The deep link hides the rest of the portal, so the customer is not tempted to cancel

✕ Cons

  • –Session links expire in minutes, so email links must go through your endpoint
  • –Some payment methods cannot be added in the portal
  • –The customer default can be overridden by a subscription default
  • –A redirect away from your site means you need a clear return page and a webhook, not a guess

What should the email say?

Say what happened, what to do and how long it takes, in that order, with one button. Do not scare the customer and do not bury the action under reassurance. We have a full library in dunning email examples and templates and a setup walk-through in the dunning sequence setup guide; here are two variants written for a portal link.

Failed payment · day 1
Subject line
We couldn’t renew your subscription: update your card

Hi Sam,

Your card ending in 4242 was declined when we tried to renew your plan. Nothing is lost: your account is still active while we sort this out.

Updating your card takes under a minute: [Update payment method]

If you think this is a mistake, reply to this email and we will help.

Why it works: One problem, one button, one promise about time. The button opens the add-a-card page, not a login wall.
Expiring card · before it fails
Subject line
Your card ending 4242 expires next month

Hi Sam,

The card we have on file for your subscription expires next month. Add a new one now and your next renewal will go through without a hitch: [Update payment method]

Why it works: Sent before any charge fails, so the tone is a favour, not an alarm. It is the same link, so the click path is already tested.

The expiring-card version is the cheapest recovery there is, because the payment has not failed yet. If you have not set it up, our guide to pre-dunning card expiry emails walks through it, and what Stripe's built-in recovery covers and what it does not helps you decide which parts to leave to Stripe.

How do you know it worked?

Measure the loop, not the email. A click on the update button is not recovery; a saved card that is used for a successful payment is. A minimal set of numbers from Stripe events:

  • Link click to card saved. Compare portal sessions created against payment_method.attached events for the same customers within the same day. A big gap means the page is confusing, a method is unsupported or the session expired.
  • Card saved to invoice paid. For the customers who saved a card, how many then have invoice.paid within your retry window? If many do not, look for the override problem above.
  • Repeat failures after an update. A customer who updates a card and fails again within a few days usually points to a decline code the new card shares with the old one, or to a second subscription still using the old method.

To turn those into a churn figure you can track month over month, follow how to calculate churn rate. Missed or late webhooks distort all three measures, which is why we wrote why missed Stripe webhooks quietly cost revenue. Verify the signature on every event before you act on it.

A woman at a desk looking at a bank card while working on an open laptop, with a cup of coffee beside her
Many customers will act on the first email, at their desk, if the path is short. Illustrative photo, not a Revova customer. Photo: Shixart1985, CC BY 2.0, via Wikimedia Commons.

Where does Revova fit?

Stripe covers the page and the card entry. What a dedicated tool adds is the sequence around it: reason-aware emails after each failure, a stop the moment the invoice is paid, expiring-card warnings you control, and a look back at failed payments from before you turned any of this on. Revova connects to your Stripe account and links to either the hosted invoice page or a signed portal link depending on the email. To see how much is sitting in your history, read the historical recovery guide. Pricing is simple: Starter is $29/mo, Pro is $79/mo, with a 14-day free trial and no card.

Already using the portal? Add the part Stripe does not do: a sequence that stops when the card works. Starter is $29/mo, Pro is $79/mo, with a 14-day free trial and no card.
$29–79/mo · free trialStart free →

A checklist before you ship

  1. The customer portal is activated and configured in live mode, not only in a sandbox.
  2. The Payment methods feature is on, and the portal is branded.
  3. Stripe's failed-payment and expiring-card emails are on, or replaced by your own with a tested link.
  4. Email links go to your endpoint, which authenticates the customer or verifies a signature before creating a session.
  5. The endpoint uses the payment_method_update flow and a sensible return_url.
  6. Your webhook handler processes customer.updated and payment_method.attached, verifies signatures and checks for a subscription-level override.
  7. Payment methods the portal cannot add (such as ACH) have a documented alternative.
  8. You track link clicks, cards saved and invoices paid, and you know the baseline before you change anything.

Frequently asked questions

How do I let a customer update their card in Stripe after a failed payment?

The shortest route is the Stripe customer portal. Activate it once in the Dashboard, then point your failed-payment emails at it. Stripe can do that for you: its failed-payment and expiring-card emails can link to a Stripe-hosted page where the customer updates their payment method. If you send your own emails, create a portal session through the API with the payment_method_update flow, which opens a single-purpose page for adding a new card and sets it as the customer’s default payment method.

What is the Stripe billing portal and is it free?

The Stripe customer portal (often searched as the billing portal) is a Stripe-hosted page where customers manage payment methods, billing details, invoices and subscriptions without you building any of it. Stripe’s documentation describes how to configure and use it but does not attach a separate fee to the portal in the pages we read, so check the current Stripe Billing pricing page for your plan before you rely on that. This guide covers how it works, not what it costs.

Why did Stripe keep charging the old card after the customer updated it in the portal?

Most often because the subscription has its own default payment method that overrides the customer-level default. When Stripe retries a failed payment it tries the subscription default first, then the customer default. The portal’s payment_method_update flow sets the customer’s invoice_settings.default_payment_method, so a subscription-level default can keep winning. Stripe’s documentation says to update the field where the previous payment failed. Check subscription.default_payment_method and clear or replace it.

How long does a customer portal link last?

A portal session URL is short-lived. Stripe documents that a new session expires after 5 minutes if it is not used, and a session that is in use expires within 1 hour of the most recent activity. That is why a link in an email should point at your own endpoint, which creates a fresh session when the customer clicks, instead of carrying a session URL created at send time.

Can customers add a bank account in the Stripe portal?

Not for every method. Stripe’s supported-payment-methods table says customers cannot manually enter a new bank account for ACH Direct Debit in the portal, and lists similar limits for PayTo and Vipps. Cards, including Apple Pay and Google Pay, have no such limitation. If a large share of your customers pay by bank debit, plan another way to collect a replacement mandate.

What is the difference between the portal login link and a portal session?

The login link is a permanent, shareable URL you activate in the Dashboard. Customers enter their email address and Stripe emails them a link that opens their session, so it needs no code. A portal session is created by your server through the API for a specific customer, returns a temporary URL and can be deep-linked into a single action such as updating a payment method. Use the login link for a quick start and sessions for failed-payment flows.

Do I need to authenticate the customer before creating a portal session?

Yes. Stripe’s integration guide tells you to authenticate customers on your site before creating sessions for them, because a session gives access to that customer’s billing details. In an email link, that means a signed or logged-in step in your own code, never a bare customer ID in the URL that anyone could change to another customer’s ID.

Which webhooks tell me a customer updated their payment method?

Stripe lists payment_method.attached for a newly added method and customer.updated for a change of the default, where you check invoice_settings.default_payment_method. If your subscriptions override the customer default, also check the subscription’s default_payment_method when customer.updated arrives. Use these events to update your own records and to stop recovery emails for that customer.

Make the update-card link do more than open a page

Revova emails customers when a payment fails, links them to a one-click fix and stops the moment the invoice is paid. Starter is $29/mo, Pro is $79/mo, and the 14-day free trial needs no card.

Start my 14-day free trial →

No credit card · Free Lost Revenue scan · 30-day money-back guarantee

Found this useful? Tell Google you'd like to see more from us.