1
0 Comments

Hardcoded payment retries are an MVP trap. Here’s what we learned building a real dunning engine.

Hey everyone,

When you're first building a SaaS MVP, your billing logic is usually an afterthought. You set up a basic Stripe or Braintree integration, write a quick cron job to retry failed invoices every 3 days, and call it a day.

We did the exact same thing early on. But as your MRR grows, that static "wait 3 days and try again" logic turns into a massive, silent revenue leak.

Involuntary churn (when users drop off simply because a credit card failed, expired, or hit a transient bank timeout) is one of the most frustrating ways to lose revenue. The customer wants to pay you, and your code wants to take the money, but the financial middleware drops the ball.

After analyzing failure logs across dozens of edge cases while building Saaslogic, we realized that fixed retry loops are fundamentally broken. Here is the technical breakdown of what we learned and how to structure a higher-recovery billing state machine.

1. Categorize Your Webhook Failures (Stop Blasting the Gateway)

Hitting a payment gateway repeatedly with a blind loop can flag your merchant account as high-risk. You need to map your error codes into distinct logical buckets:

  • Soft Failures (Insufficient funds, temporary network timeouts): These need time-optimized retry windows (e.g., matching local payroll cycles or business hours).

  • Hard Failures (Expired cards, closed accounts): Do not retry these. You are burning API limits. Immediately route these users to a secure, single-use billing update token.

2. Turn Dunning into an Event-Driven State Machine

Relying entirely on email reminders is a losing battle, as they sit in spam or overflow folders. Instead, decouple your communication channels based on the state of the invoice:

  • Day 1 (Failure 1): Silent background retry during an optimized window based on past transaction success data.

  • Day 3 (Failure 2): Trigger a non-intrusive, secure in-app notification banner that displays only to the workspace admin.

  • Day 7 (Failure 3): Send a direct, plain-text email with a magic link that lets them update their card without needing to remember their account password.

3. Grace Periods > Instant Account Locking

Locking a user out of their dashboard the second a payment fails ruins user trust. We found that implementing a 7-day "read-only" or soft-lock grace period keeps the user engaged with the product while their operational team sorts out the corporate card issue.

If you want to see the complete guide on how subscription dunning automation works, why modern systems go beyond simple retry logic, and how to set up these workflows step-by-step, you can read the full breakdown on the Saaslogic Blog.

The Bottom Line

Every hour you spend writing, debugging, and maintaining custom database states for edge-case payment routing is an hour stolen from your core product roadmap.

If you're bootstrapping or scaling a SaaS right now, I'd love to hear how you handle failed payments:

  • Do you handle retries natively through your gateway's basic settings?

  • Have you built a custom state machine to handle localized bank failures?

If you want to see how we abstracted this entire problem so you can focus strictly on your core features, take a look at Saaslogic. Otherwise, I’d love to know: how are you currently tackling failed payments?

posted toAvatar for product Saaslogic
Saaslogic