Skip to main content
Pricing, Metrics & Business Model

Failed Payments and Dunning for SaaS: Retries, Emails and Billing Edge Cases

By Obaid Tariq, Founder & Lead Engineer · Published

The short answer

Treat a failed renewal as a recoverable event, not a cancellation. Turn on your billing provider's automatic retries, send a short sequence of clear emails with a one-click link to update the card, keep the customer's access during a defined grace period, and decide in advance what happens when recovery fails. Most of the engineering work is in handling the webhooks correctly, so your app's idea of who has paid always matches the billing provider's.

How should I handle failed payments, payment retries, dunning and billing edge cases?

Let the billing provider retry automatically, tell the customer quickly and plainly, give them a hosted page to fix the card, and keep access for a grace period that matches the retry window. Make your application react to billing webhooks rather than polling, and write down what happens at the end of the window before the first customer ever fails a payment.

Why failed payments matter more than they look

A customer whose card fails did not decide to leave. Expired cards, a bank's fraud filter, insufficient funds on the renewal date or a card that now needs extra authentication all look the same in your revenue chart as a cancellation if nothing recovers them. This is usually called involuntary churn, and unlike voluntary churn it is mostly an engineering and operations problem, so it is one of the cheaper retention problems to fix. For the wider picture of retention, see our guide to SaaS metrics and churn.

Step 1: Configure automatic retries

Every mainstream subscription billing system can retry a failed renewal on a schedule. Using Stripe Billing as the example, Stripe recommends its Smart Retries feature, which chooses retry times using signals such as when a payment method is most likely to succeed; its documentation gives a recommended default of 8 tries within 2 weeks, with retry windows of 1 week, 2 weeks, 3 weeks, 1 month or 2 months. You can instead define a custom schedule of up to three retries at set day intervals.

Step 2: Write a dunning sequence people actually act on

Dunning is the sequence of messages that asks the customer to fix the payment. In our experience the wording matters less than three practical details: the email arrives from a recognisable sender, it says exactly what failed and what happens next, and it has one button to a hosted page where the card can be updated without logging in to anything else. A sequence we would start from:

An example dunning sequence for a monthly SaaS subscription with a two-week retry window
WhenMessagePurpose
Day 0 (first failure)"Your payment didn't go through" with the amount, the card's last four digits and an update linkMost fixes happen here; keep it short and neutral
Day 3Reminder, same link, mention that access continues for nowCatch people who missed the first email
Day 7Reminder that names the date access will changeCreate a clear, honest deadline
Final retry dayLast notice: what happens today and how to restore the accountNo surprises when access changes
After the windowAccount paused or downgraded; one-click restore linkMake coming back easy

For B2B accounts, send the messages to the billing contact and, where you have one, the account owner: the person who uses the product is often not the person whose card is on file. An in-app banner for admins is worth adding once you have more than a handful of customers.

Step 3: Decide what happens to access

Decide this before you launch, because it is a product decision as much as a technical one. Stripe, for example, lets you choose what a subscription does once retries are exhausted: cancel it, mark it unpaid, leave it past_due, or pause it where eligible. Whichever you pick, map each billing state to a clear level of access in your application:

  • Active: full access.
  • Past due (inside the retry window): full access plus a banner. Cutting access on day one punishes customers for their bank's mistake.
  • Unpaid or paused (after the window): read-only access or a locked workspace, with data kept. Deleting data because a card expired is rarely what anyone wants, and may conflict with what your terms promise.
  • Cancelled: follow your published data-retention policy, with an export option. See our guide to GDPR and SaaS legal basics for retention and deletion.

Step 4: Handle the webhooks properly

This is where most custom billing integrations go wrong. Your app should learn about payment state from the billing provider's webhooks and store it, rather than assuming a payment succeeded because the checkout page redirected. Stripe's webhook documentation sets out the rules that matter:

  • Verify the signature on every event using the endpoint's signing secret and the raw request body; otherwise anyone can post a fake "paid" event to your endpoint.
  • Expect duplicates. Stripe says an endpoint may receive the same event more than once; log processed event IDs and skip repeats.
  • Do not rely on order. Stripe does not guarantee events arrive in the order they were created, so fetch the current subscription state from the API when an event arrives rather than applying events as a sequence of changes.
  • Return a 2xx quickly and do the work in a background job. Stripe retries undelivered live-mode events for up to three days with exponential back-off, so a slow or failing endpoint causes repeated deliveries.
  • Listen to the events you need, typically invoice.payment_failed, invoice.paid and customer.subscription.updated / deleted, rather than every event type.

Billing edge cases to test before launch

Billing edge cases and how to handle them
Edge caseWhat goes wrongHow to handle it
Card needs authentication (SCA / 3D Secure)The renewal cannot complete without the customer presentEmail a link to a hosted page where they can authenticate; do not keep retrying silently
Customer updates the card in the wrong placeRetries keep using the old cardStripe retries the subscription's default payment method first, so update the payment method on the subscription, not only on the customer
Plan change mid-cycleUnexpected prorated charge failsShow the proration amount before confirming; treat a failed proration like any other failed invoice
Seat added on a failing accountMore usage on an unpaid accountBlock upgrades while past due, or require payment first
Refund or disputeAccess stays on after the money has goneListen for refund and dispute events and flag the account for review
Free trial ends without a cardAccount converts to nothingDecide whether trials need a card up front; send a reminder before the trial ends
Annual renewalA large charge fails after a year of silenceSend a renewal notice in advance and check local consumer-law rules on renewal reminders
Taxes and invoicesB2B buyers need a VAT/GST number on the invoiceCollect tax IDs at checkout and use the provider's tax tools or a tax adviser

Measure it

Track three numbers each month: the share of renewal invoices that fail on the first attempt, the share of those recovered by retries and emails, and the revenue lost after the window closes. If recovery is low, look first at the emails (are they reaching the billing contact?) and the hosted update page (does it work on mobile?) before tuning retry timing.

When to build more than this

For an early SaaS, the billing provider's built-in retries, emails and hosted pages are usually enough, and building your own dunning engine is a distraction; see what not to build in a SaaS. Custom work starts to pay off when you invoice large B2B accounts by bank transfer, sell through resellers, need usage-based billing reconciled against your own metering, or run several billing providers in different regions.

Frequently asked questions

What is dunning in SaaS?

Dunning is the process of recovering a failed subscription payment: automatic retries of the charge, a sequence of emails or in-app notices asking the customer to update their payment details, and a defined change to the account if the payment is not recovered by the end of the retry window.

How many times should a failed subscription payment be retried?

There is no universal number. Stripe's documentation gives a recommended default for its Smart Retries feature of 8 tries within 2 weeks, and lets you choose windows from 1 week to 2 months. Hard declines such as a lost or stolen card should not be retried at all without a new payment method.

Should I cut off access as soon as a payment fails?

Usually not. Most failures are not a decision to leave, so keep access during the retry window and show a banner. Change access at the end of the window, preferably to read-only or paused rather than deleting data.

Do I need to build my own dunning system?

Not at the start. Billing providers such as Stripe include automatic retries, customer emails and hosted pages for updating payment details. Your job is to connect their webhooks to your application's access rules correctly and test the edge cases.

Sources

  1. Stripe Docs: Automate payment retries (Smart Retries) — Smart Retries; recommended default of 8 tries within 2 weeks; retry windows; custom schedules of up to three retries; non-retryable hard decline codes; payment-method retry order; end-of-retry subscription states (cancel, unpaid, past due, pause). Accessed 19 October 2026.
  2. Stripe Docs: Receive Stripe events in your webhook endpoint — Signature verification with the raw body; duplicate events; no ordering guarantee; returning 2xx quickly; automatic retries of undelivered live-mode events for up to three days. Accessed 19 October 2026.

Recommendations without a source are AI Dev Planner’s own engineering opinion. Platform features and prices change; check the linked pages for the current position. Found an error? See our corrections policy.

Need billing that doesn't leak revenue?

We build Stripe subscription billing into SaaS products, including webhooks, access rules and the edge cases above. Tell us how you charge and we will tell you what to build and what to leave to Stripe.