Draft preview. Not in the index, feed or sitemap.

SaaS billing beyond the happy path: failed payments, downgrades and refunds

When a subscription payment fails, your payment provider retries the card and emails the customer. What the customer can still do in your product meanwhile, what a downgrade does to data over the new limit, and how a refund is recorded are your decisions. Write them into the scope as a state table, let the provider's events set each account's status, and record every change to money as a new entry.

By The Spiral team Published 13 min read

SaaS billing and failed payments: the short answer

When a subscription payment fails, your payment provider retries the card and emails the customer. Stripe recommends 8 tries within 2 weeks as its default. What the customer can still do in your product during those retries, and after they run out, is your decision. So is what a downgrade does to data over the new limit, and how a refund is recorded.

This guide is for founders and managing directors at UK and EU companies commissioning a subscription product from a development partner. Most articles on SaaS billing and failed payments come from billing vendors and measure success in money recovered. You also need to know what your customer sees, what your support team can fix, and what the software records.

Our SaaS product development page promises billing states that survive refunds, downgrades and failed cards. The same page gives the reason. A demo becomes a product in everything off the happy path, from an expired card to a customer who changes plan mid month. Below are those states, the decision each needs, and how to test them.

A failed payment is not a cancellation

Renewals fail for ordinary reasons: an expired card, a card replaced after a fraud alert, a bank that wants the customer to confirm the payment. None of them means the customer wants to leave. Treat the failure as its own state and they can fix it in a minute. Treat it as a cancellation and you invite the mess OpenAI now warns ChatGPT subscribers about.

OpenAI's help centre explains how people end up paying twice for one subscription. When an Apple renewal fails, Apple doesn't cancel the subscription. It keeps trying to collect, while ChatGPT may show the subscription as inactive. A subscriber who signs up again on the web, then updates their card with Apple, can be charged by both.

The customer saw "inactive" where the truth was "retrying", and acted on what they saw. Your product needs a name for that in-between state, a screen that shows it, and one obvious way out: update the card.

The billing states your product needs

Stripe gives every subscription a status, such as active, past_due or unpaid, and our guide to what an MVP should include lists all eight. A status tells you where the money stands. It doesn't tell your product what the customer may do, so write a table that does, one row per state, before anyone designs a screen.

Here is the table we'd start from for a business product. Your rows will differ. What matters is that every cell has an answer.

Your product's stateStripe statusWhat the customer can doWhat you sendWho acts
TrialtrialingEverything in the planA reminder before the trial endsNobody yet
PayingactiveEverything in the planReceipts and invoicesNobody
Retryingpast_dueEverything, with a banner asking an admin to update the cardAn email after each failed attempt, with a link to update the cardThe customer's billing admin
Needs confirmationpast_due, with the bank asking for authenticationEverything, with a banner asking an admin to confirm the paymentAn email linking to a page where they confirm itOnly the customer can fix it
On holdunpaidSign in, read, export and pay; no new workA final notice before the hold startsThe customer; your team phones larger accounts
CancelledcanceledExport until a stated date, then data is deleted on scheduleConfirmation, with the export deadlineNobody
Never startedincomplete, then incomplete_expiredNothing; no paid account existsOne reminder, if anyNobody

Two rules sit behind the table. On hold means read-only, never deleted: a customer whose card failed on Friday should find their data on Monday. And cancelling starts a retention clock; it deletes nothing on the day.

How long a customer keeps access after a card fails

Keep full access while the provider retries, then move to read-only, then cancel. Apple and Google both offer a version of that sequence in their app stores.

  • Apple offers a billing grace period of 3, 16 or 28 days for monthly and yearly subscriptions. Subscribers keep their paid content while Apple retries. Without it, their paid service pauses until Apple collects.
  • Google Play switches a grace period on by default, with access kept. Account hold follows, where the app should block access. By default the hold lasts 60 days minus the grace period, and then the subscription is cancelled.
  • Stripe retries on the schedule you set. After the last retry it does one of three things, chosen in your settings: cancel the subscription, mark it unpaid, or leave it past_due (Stripe's retry settings). Its docs say to revoke access once a subscription is unpaid.

Choose those settings on purpose, and match your grace period to the retry window. If Stripe retries for two weeks but your product locks accounts after three days, customers lose access while their payment is still being fixed.

What "access" means is a product question. Say you sell booking software to physiotherapy clinics, priced per practitioner. Locking the diary at 9am on a Monday stops patients booking, and punishes the clinic's own customers for a card the office manager forgot to replace.

A banner for the admin and emails to the billing contact do less harm. So does a read-only hold once the retries fail. The clinic keeps working and has time to pay.

Dunning emails, and the failure a retry can't fix

Dunning is the sequence of reminders and retries after a failed payment. Stripe can send most of the emails for you. It can email after each failed payment with a link to update the card, a month before a saved card expires, and before each renewal.

Send them to someone who can act. In a business product, the person who signed up two years ago may have left. Store a billing contact on each customer account, let the account owner change it, and show billing banners only to people who can pay. Members who can't fix the card needn't see invoices at all, which our guide to user roles and permissions covers.

In the UK and EU, one kind of failure needs its own path. Strong Customer Authentication (SCA) lets a cardholder's bank ask them to confirm a payment. For UK e-commerce, the FCA's deadline was 14 March 2022.

Renewals charged while the customer is away can qualify for an exemption. Stripe warns that exemptions aren't guaranteed, and the bank may still ask.

When it asks, retrying the card changes nothing. Stripe lists authentication_required among the decline codes it can't retry until the customer acts. The email has to link to a page where they confirm the payment with their bank, and Stripe offers a hosted page for that. Turn it on, then test it with a card that asks for authentication.

Upgrades and downgrades: charge now, change later

Proration is the charge or credit for part of a billing period when a customer changes plan. Stripe works it out to the second by default. It doesn't refund a negative proration automatically, or bill a positive one straight away (Stripe's proration docs). You decide both.

The simplest rule, and the one we'd suggest: upgrades take effect and are charged now; downgrades take effect when the paid period ends. The customer paid for the bigger plan this month, so they keep it until renewal, and nobody handles a partial refund.

Three edge cases catch most products:

  1. An upgrade whose payment fails. By default, Stripe applies the change even if the payment fails, and rolling it back is manual work. The customer gets the bigger plan unpaid. Stripe's pending updates apply the change only once the payment succeeds.
  2. A downgrade below current usage. The clinic moves from ten practitioner seats to six while eight people still sign in. Your scope should say whether to refuse the downgrade, let the owner pick who stays, or make two accounts read-only. Never delete data to make the numbers fit.
  3. A plan change while an invoice is unpaid. Stripe calculates prorations as if earlier invoices will be paid, so a customer can be credited for time they never paid for. The usual workarounds can charge them twice unless the old invoice is voided.

One detail for per-seat pricing: when code changes a subscription's price through Stripe's API, the quantity resets to 1 unless the code sends the seat count again. A test with a ten-seat customer catches it.

ChangeWhen it takes effectMoneyCheck in testing
UpgradeNowCharge the difference nowThe plan stays the same if the payment fails
DowngradeEnd of the paid periodNo refund; the new price applies at renewalWhat happens to usage over the new limit
Add seatsNowCharge for the rest of the periodThe new seat count reaches the invoice
Remove seatsEnd of the paid periodCredit or nothing, by written policyRemoved users' data stays
Monthly to annualNowCharge the annual price, minus unused time on the monthly planThe renewal date moves to today
CancelEnd of the paid periodNo refund unless your terms or the law require oneExport still works until the end date

Refunds and credits: add an entry, never edit the invoice

A paid invoice stays exactly as it was issued. A refund or correction goes in as a credit note that points back to it. Stripe's credit notes work this way: a credit note doesn't void and replace the original invoice. On a paid invoice, it can refund the card, add to the customer's credit balance, or record a refund made outside Stripe.

UK VAT rules expect the same shape. HMRC's VAT manual sets out the conditions for a valid credit note. Among them: it goes to the customer, and it corrects a genuine mistake or overcharge or reflects an agreed reduction in price.

Decide between refund and credit in writing, per case. Slack's fair billing policy is a public example. When a paying member hasn't used Slack for more than 28 days, Slack stops billing for them and credits the account for the unused time.

That credit can't be exchanged for money. The rule is written down, so customers can check it and a support agent can apply it without asking anyone.

Inside your product, four rules keep refunds honest:

  • Refunding is a permission, given to named roles.
  • Every refund and credit needs a reason from a short list, plus a note.
  • Refunds above an amount you set need a second person's approval.
  • The billing history shows the original invoice, the credit note and who issued it.

That is an audit trail for money. Our guide to why your business software needs an audit trail explains how to stop anyone editing it later, your own administrators included.

Let the provider's events set the status

Your product learns about payments through webhooks: messages your payment provider sends your server when something happens, such as a failed renewal. Stripe's webhook docs warn of three things the receiving code must handle:

  • Events can arrive in any order.
  • The same event can arrive more than once.
  • If your server doesn't answer, Stripe keeps retrying for up to three days in live mode.

So the code records each event's ID and ignores one it has already seen. Before it changes anything, it fetches the current subscription from Stripe. Your product's billing state becomes a copy of the provider's, refreshed from the source, not from whichever message arrived last.

Keep that copy read-only for your team. If a support agent edits "past due" to "active" in your database, the next event sets it back. Or no event comes, and the two systems disagree about who has paid.

To give a customer an extra week, act where the money lives: extend the retries, add a credit, or record a bank transfer as a payment. The provider sends an event, and your product follows it.

A missed event fails quietly. A customer who never paid keeps their access, and nothing on screen looks wrong. Run a daily job that compares every account's state with the provider's and reports any difference to a person.

If a job suspends accounts from a list of overdue customers, have it re-read each account just before acting. The payment may have landed a minute earlier.

UK and EU rules that touch subscription billing

Four sets of rules shape billing for UK and EU companies, besides the VAT rules on credit notes above. This is a map, not legal advice, so check the detail with your adviser.

  • Strong Customer Authentication, covered above, means some renewals fail for a reason only the customer can fix.
  • New UK rules for consumer subscriptions come into force in January 2027, under the Digital Markets, Competition and Consumers Act 2024. The government names clearer up-front information, regular reminders and an easier way to cancel. It adds a new 14-day cooling-off period after a trial or long contract renews.
  • The CMA has already acted on customers who keep paying for a subscription they don't use. In 2022 Microsoft agreed with the CMA to contact long-inactive Xbox members still paying, remind them how to stop, and in the end stop taking their payments.
  • In Germany, a business selling consumer subscriptions online must put a cancellation button on its website. Section 312k of the Civil Code says it must read "Verträge hier kündigen" (cancel contracts here) or something equally clear.

The UK's 2027 rules cover a contract between a trader and a consumer, and the German button applies to consumer contracts too. A product sold only to businesses sits outside both. If you also sell to consumers, your state table needs a reminder schedule and a way to cancel online. It also needs a refund path for the new cooling-off period.

Test the unhappy paths before launch

Every state in your table needs a test that forces it. Stripe's sandbox has test clocks that move a subscription's time forward, so you can watch a renewal fail without waiting a month. Its test cards decline on purpose or ask for authentication.

Run these checks in the shared staging environment before anyone calls billing finished:

  1. Renew with a card that declines. The account moves to retrying, only admins see the banner, and the email's link works.
  2. Renew with a card whose bank asks for confirmation. The email links to a page where the payment can be confirmed.
  3. Update the card halfway through the retries. Access continues, and the customer is charged once.
  4. Let the retries run out. The account goes read-only, and the export still works.
  5. Upgrade with a card that declines. The plan stays the same.
  6. Downgrade below current usage. The rule in your scope happens, and no data is lost.
  7. Refund part of an invoice. A credit note appears, with a reason and the name of whoever issued it.
  8. Switch off the webhook endpoint for an hour, then back on. Nothing is lost, and the daily comparison finds no difference.
  9. Send the same event twice. The account changes once.

We work from written scopes and a shared staging environment. A list like this one belongs in the scope, under what "done" means.

When you don't need to build most of this

You may not need much billing code of your own yet. Three options cover a lot of it.

  • For the first few customers, an invoice and a bank transfer work, as our MVP guide explains. Store a status on each account anyway.
  • Stripe's hosted customer portal lets customers update cards, change plans, cancel and download invoices without screens of your own. It has limits: customers can cancel but not change a subscription paid by invoice, one with several products, or one with usage-based pricing.
  • A merchant of record such as Paddle becomes the legal seller to your customers. It takes on sales tax, refunds and chargebacks, and Paddle's docs say you don't register for VAT or sales tax in the countries where you operate. In return you give up some control of checkout and pay a fee on each sale, set out on its pricing page.

No provider can take the product's side of billing off your hands. That means what each state lets a customer do, what a downgrade does to their data, and who on your team may give money back.

Start with the state table

This week, copy the state table above and fill it in for your product. For each state, write what the customer can do, what they're sent and who acts. Add a line for each kind of plan change, saying when it takes effect and what happens to anything over the new limit.

Open the payment provider account in your company's name, as you would the domain. Then send the table with your brief. A builder who reads it can quote the billing you need, not just the checkout page.

Questions

Our business customers pay by invoice and bank transfer, not by card. Does this still apply?

Mostly, with one trap. Stripe marks a new subscription paid by invoice as active, even while its first invoice is unpaid. So for these customers your product has to check invoice due dates as well as the status. Decide how many days overdue moves an account to retrying and then to on hold, and who at your company phones the customer before that happens.

What should happen when a customer disputes a payment with their bank?

A dispute, or chargeback, is a customer asking their bank to reverse a payment. Treat it as its own state with a person deciding what happens next, because it can mean fraud, a misunderstanding or an unhappy customer. Record it on the account, send the payment provider your evidence, and decide by written policy whether access continues meanwhile.

Who should own the Stripe or Paddle account?

Your company, from the first day. The account holds your customers' payment records and receives your money, so it belongs in your company's name, like your domain and hosting. Give developers their own logins with the narrowest role that lets them work, and remove their access when they leave.

How we wrote this: drafted with AI, then checked and edited by the people who build our projects. Figures come from our own work or from the linked sources.

Tell us what is slow

Describe the project, or the part of the business that still runs by hand. You will hear back within one business day, from the people who would build it.

info@thespiral.studio