Check out the Build Series. Part 1 | Part 2 | Part 3 | Part 4 | Part 5
Most builders wire up Stripe, test a happy-path checkout, and call the billing system done. The problems hide in the edges: the EU user whose card type Stripe rejects, the subscriber who downgrades and can no longer open a workspace they paid for, the Founding Member who clicks “Manage Subscription” and gets a portal error because they have no Stripe subscription at all.
I caught all three in DraftKit before they hit a real user. Here is exactly how.
When I was stress-testing DraftKit’s billing flow, I had already shipped Stripe Checkout for Pro subscriptions, tested a successful payment, and watched the subscription_started event fire cleanly. Everything looked fine.
Then I ran a test card flagged as “regional restriction” inside Stripe’s test suite. The checkout session failed with a generic error. No retry, no fallback, just a dead end. If this had been a real user in Germany or the Netherlands, they would have seen that error and bounced.
That was the first of three silent failures I found in 45 minutes of deliberate edge testing.
The second was worse: I tested a Pro user downgrading back to Free and found the access logic was checking their current subscription tier, not whether they had ever paid to open a specific workspace. Every completed workspace would lock on downgrade. Paying customers, locked out of their own work.
If you are building any kind of AI product with a paywall, your billing system has at least two of these gaps right now. The failure modes are not visible in the Stripe dashboard. They are in the edge states no one tests because no one planned to test them.
I run billing architecture audits for AI product builders. If you want me to walk through your access control and checkout flow before your next launch, email hello@elenacalvillo.com.
This Is an Industry Problem
Subscription businesses will lose $129 billion to failed payments in 2025, according to Recurly’s January 2024 estimate. That is not lost to bad product or bad support. It is lost to billing infrastructure that fails silently at edges the builder never thought to test.
The numbers are specific:
✅ Stripe’s own research shows 33% of customers who hit a payment failure do not retry it.
✅ 62% of users who encounter a payment error never return to the site.
✅ Paddle’s analysis of 2,000+ SaaS companies found involuntary churn accounts for 13-15% of total churn.
None of those users sent a support ticket. They just left.
The pattern is the same everywhere. Someone ships a payment flow, tests one successful checkout, and moves on. The edge states do not get tested because no one planned to test them. The first sign of a problem is a churned user you never hear from.
Why Your Workflow Misses This
Most PMs who build with AI tools treat billing as a Stripe plugin problem. You add the package, test a payment, see green, and ship. The assumption is that Stripe handles the edge cases.
It does not. Stripe gives you the tools. You have to design the states. Currency fallbacks, access revocation policies, and billing UI routing are product decisions, not payment processor features. If you have not written them down explicitly, they default to the worst possible outcome for your user.
This is particularly sharp if you have any non-standard user class: Founding Members, beta testers, manually grandfathered accounts. For DraftKit’s payment architecture, those edge cases meant three separate failure modes hiding behind a checkout that looked fine.
The Approach I Follow
✅ Test the failure path, not just the success path. For every billing event (checkout, upgrade, downgrade, cancel), I write a test case that forces a failure and verifies what happens next. I use Stripe’s test card suite to simulate regional restrictions, expired cards, and network errors before shipping anything.
✅ Define your access policy as a single function, not a conditional scattered across your codebase. I built has_workspace_access() as one Postgres function that is the only gatekeeper for workspace access. It answers one question: has this user ever paid to open this specific workspace? Not: are they on Pro right now? The distinction matters more than almost any other architectural decision in the billing system.
✅ Write down every billing UI state for every user type before writing a line of code. Before I built the Membership Hub, I listed every user type: Pro subscriber, free user, Founding Member (permanent Pro with no Stripe subscription), credit pack buyer, trial user. For each, I asked: what does the “Manage Subscription” button do? If the answer was “break,” that is a spec problem, not a bug.
✅ Treat currency failures as routing problems, not terminal errors. When a user’s card type or region causes Stripe to reject the currency on a checkout session, the right move is to retry with USD before surfacing an error. This is three lines of code. It removes a hard bounce for every user hitting a regional restriction.
✅ Audit your billing system from a downgraded account before you launch. Create a test Pro account, open several workspaces, manually downgrade to free, then open every workspace and try every button. Do this before every billing-related release. If anything breaks, you found a gap before a real user did.
Before I ship any change to DraftKit’s billing layer, I run through this checklist. The 45 minutes it takes is cheaper than one churned Pro subscriber.
If you want me to run this audit on your product’s billing architecture, email hello@elenacalvillo.com.



