Design a system to handle subscription renewals

Modeling a subscription state machine that decouples billing from access.
States include active, past_due, and cancelled; transitions are payment success, dunning exhaustion, and grace expiry; use idempotent webhooks.
What's really being asked
Whether you can model a distributed state machine for recurring billing. Interviewers care about separation of concerns between payment state, subscription state, and entitlement state. They want to see you understand asynchronous events, idempotency, and dunning as a first-class workflow rather than an afterthought.
The full answer
First, enumerate the core states. Trialing means no payment has been collected yet. Active means the latest invoice was paid or payment is not yet due. Past_due means a renewal payment failed but the account is still within the dunning window or grace period. Unpaid means dunning has exhausted and the grace period has lapsed but the subscription is not yet cancelled. Cancelled means the subscription has been terminated either by the user or by an automated rule. Incomplete and incomplete_expired map to Stripe's initial payment flow where a customer has 23 hours to complete a first payment. Second, map events to transitions. A successful invoice payment moves a subscription from incomplete to active or keeps it active. A failed charge on a renewal invoice moves active to past_due. A dunning retry success moves past_due back to active. Dunning exhaustion plus grace expiry moves past_due to unpaid or directly to cancelled depending on business rules. An explicit cancellation event moves active or past_due to cancelled. Third, separate concerns. The subscription status should not be the sole source of truth for feature access. Maintain an entitlements layer that reads subscription state plus payment state to decide whether to grant access during a grace period. Fourth, handle webhooks idempotently. Payment events can arrive out of order or duplicate, so process invoice paid events with idempotency keys and guard transitions with state preconditions.
The mistakes people make
Mapping every failed payment straight to cancelled ignores revenue recovery and violates real-world SaaS norms. Conflating invoice status with subscription status leads to race conditions, for example provisioning access based on an open invoice that later fails. Omitting the incomplete state for initial signups misses the 23-hour window where a customer may complete payment asynchronously. Designing a synchronous only flow assumes payment confirmation happens in the same request, which breaks for 3D Secure and other asynchronous methods.
What usually comes next
How would you handle a user who updates their payment method during dunning? How do you safely retry failed payments without violating card network rules? How would you backfill state if a webhook is missed? What happens to annual subscriptions versus monthly when prorating failures?
A concrete example
Consider a monthly subscription renewing on the first of the month. On day one, Stripe generates an open invoice and attempts a charge. The card is declined. The subscription moves to past_due. Your dunning schedule retries on day three, day seven, and day ten. During this window the customer retains access because your entitlements service checks both subscription status and a grace timestamp. On day ten the final retry fails. The grace period expires on day fourteen. On day fourteen a scheduled job transitions the subscription to unpaid and revokes entitlements. If the customer pays on day twelve, the invoice paid webhook transitions the subscription back to active and clears the grace timestamp.
Interview question
A subscription enters the past_due state after a renewal failure, yet the customer continues to have feature access during the grace period. Which architectural choice makes this possible?
- a.Webhook handlers retry failed payments automatically to keep the subscription active
- b.The billing system delays updating the invoice status until the grace period ends
- c.Access decisions are made by an entitlements layer that reads both subscription state and payment state independentlyCorrect
- d.The subscription state machine skips past_due and remains active until dunning exhausts
Why? this is the answer
An entitlements layer decouples feature access from the subscription state machine, allowing grace-period access while the subscription is legitimately past_due. Simply keeping the subscription active until dunning exhausts confuses payment failure tracking with access control and prevents accurate revenue recovery workflows.
Just read this? Test yourself on what you have been reading.
Read the original → docs.stripe.com
- #subscription billing
- #state machines
- #dunning
- #webhooks
- #payments
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Every open role lists the topics its interview covers, so you can prepare for the real thing rather than guessing.
See open roles