Solution guide · Custom Software

Stripe Subscription Billing and Dunning: What It Really Costs to Build

The short answer

Adding Stripe subscription billing with working dunning to an app you already have typically runs $15,000 to $60,000 over 4 to 8 weeks. Built as part of a first release it is $50,000 to $130,000 in 10 to 16 weeks. A full billing platform with usage metering, multi entity tax, migrations and finance tooling is $150,000 to $350,000 over 6 to 12 months. The payment integration is the cheap part. Dunning, entitlements and webhook correctness are where the money goes.

What this becomes once real cards start failing

The demo version is forty lines. Create a Checkout Session, listen for checkout.session.completed, set is_paid to true. It works. It keeps working for roughly ninety days, which is about how long it takes for the first cohort of cards to expire.

The failure we get called in to fix most often: a customer's card expires on the 1st. Stripe fires invoice.payment_failed. The team wired that event to suspend the account, which felt responsible. Stripe's Smart Retries then attempt the charge again on day 3 and it succeeds, because the issuer reissued the card and the card network account updater pushed the new number into Stripe automatically. Stripe fires invoice.payment_succeeded. Nobody listened for that event, because in the original build the only path to "paid" was checkout.session.completed, and a renewal never emits one. The customer is now paying full price and locked out of the product. They do not file a ticket. They open a dispute, which costs $15 plus the transaction, and they leave. The team finds out three weeks later when finance cannot reconcile the month.

None of that is a Stripe bug, it is all documented behaviour. It happens because subscription billing is a distributed state machine running between Stripe's ledger and your database, and the naive build treats it as an event handler.

Webhooks are at least once and out of order. Your handler is neither.

Two facts from Stripe's own documentation that almost no quote accounts for.

Delivery is at least once. Stripe retries failed webhook deliveries with exponential backoff for up to three days in live mode. If your endpoint returns a 500, or is slow enough that Stripe times out the attempt, you receive that event again. If the first attempt did the work and then died on the response, you do the work twice. That is how customers get double credits, duplicate seats, and two invoices for one month.

Ordering is not guaranteed. Stripe states plainly that events may not arrive in the order they were generated. A customer.subscription.updated carrying the new price can land before the one carrying the old price. Apply them in arrival order and you write stale state permanently, because no third event arrives to correct you.

A competent build does four specific things. It keeps a processed_events table with a unique constraint on the Stripe event ID, and inserts that row inside the same database transaction as the state change, so "we handled this" and "the handling" commit or roll back together. It verifies the signature with the endpoint secret and a timestamp tolerance, so a replayed payload is rejected. It returns a 2xx immediately and does the real work off a queue, because the handler's job is acknowledgement rather than business logic. And it never treats the webhook payload as truth for ordering: either re-fetch the object from the API when processing, or store the event created timestamp per object and discard anything older than what is already applied. Every outbound write to Stripe carries a deterministic idempotency key, which Stripe honours for 24 hours, so a retried job does not create a second subscription.

Dunning is a state machine, not a retry loop

Stripe already retries. Smart Retries makes up to four attempts across a window you configure in Billing settings. The retrying is free. The decisions around it are what you are paying for.

Decline codes split into two families and you must treat them differently. insufficient_funds and try_again_later are soft: retry them. stolen_card, lost_card and pickup_card are hard: never retry them, because hammering an issuer with a known bad card damages your acceptance rate across your whole account, not just that customer.

past_due, unpaid and canceled are three different endings. Which one a failed subscription lands in is a setting in your Stripe Billing configuration. Most teams never open that screen and inherit a default that does not match their product. Decide it on purpose.

An off session renewal on a European card can return requires_action, not a decline. Under PSD2 and strong customer authentication, the issuer can demand a 3D Secure step even on a merchant initiated charge. There is no code path that fixes this server side. Your only move is to send the customer the hosted_invoice_url and have them authenticate. If your dunning treats requires_action as a failure, you cancel customers who were trying to pay you.

Pre dunning beats dunning. The card expiry month is on the PaymentMethod object. Email at 30 days and 7 days before expiry. The cheapest recovery is the charge that never fails.

Channel matters more than copy. Across the SaaS products we bill for, a persistent in app banner with a one click update card link recovers materially more than an email only sequence. The email goes to whoever signed up two years ago. The banner goes to whoever is using the product today.

Then design the grace window explicitly: what can a past_due account do on day 1, day 7, day 21. Full access for a week, read only after, suspended at the end of the retry window is a defensible default. Deleting data at any point is not.

Entitlements are where the product breaks, not the payment

The question that decides your architecture: where does "this user is allowed in" live? It cannot be a Stripe API call on every request. That couples your login page to Stripe's uptime and your rate limits.

Cache the state in your own table: plan, status, current_period_end, seat count, and the event ID that last wrote it. Everything in the product reads that row. Then run a nightly reconciliation sweep that compares your table against Stripe and alerts on drift. Every book we have audited has had drift, usually a handful of accounts that are free forever because one webhook was dropped during a deploy eighteen months ago.

Plan changes are the second trap. An upgrade should charge now, a downgrade should take effect at period end. That is proration_behavior and billing_cycle_anchor on one path and Subscription Schedules on the other, and choosing wrong turns into refund conversations. Seat sync is the third: if you push quantity to Stripe every time someone adds a user, you will generate $0.37 proration invoices at random hours. Batch it.

Never hardcode price IDs. Use lookup keys, because grandfathered prices live forever and you will be running four generations of pricing at once. For usage based charges, Stripe's Billing Meters replaced the old usage records API, and meter events aggregate asynchronously, so your in app usage counter and the invoice will disagree for a window. Decide now which number you show the customer.

Tax, invoices and what surfaces in month four

Stripe Tax calculates. It does not register you. Nexus and VAT registration are your legal obligation, and no code fixes that. EU business to business reverse charge needs VAT ID validation against VIES, and VIES goes down regularly, so you must decide whether an outage blocks checkout or you accept and validate asynchronously. Some jurisdictions require gapless sequential invoice numbering per legal entity, which constrains how you configure Stripe's numbering. If you sell into India, the RBI e-mandate rules for recurring card payments add pre debit notification and authentication requirements that are weeks of work on their own.

Refunds and credit notes are not the same thing and finance cares which one support clicks. Early fraud warnings arrive before a dispute does, and refunding on the warning avoids the dispute and the fee. And build the four admin buttons: comp a month, extend a trial, refund a line, change a plan. Teams that skip them pay a developer to run scripts against production forever.

What it costs and how long it takes

These are Digital Heroes delivery bands from more than 2,000 projects.

Focused build inside an existing product: $15,000 to $60,000, typically 4 to 8 weeks. Checkout or Elements, the webhook layer done properly, entitlement caching, the dunning state machine, test coverage with Stripe test clocks, and the admin tooling. The bottom of that band is three flat plans, one currency, one country. The top is seats plus annual plus trials plus coupons.

Part of a first release: $50,000 to $130,000 in 10 to 16 weeks. Here billing is entangled with auth, roles, the pricing page, and a product that is still moving under it.

Full platform: $150,000 to $350,000 over 6 to 12 months. Usage metering, multi currency, multi entity, revenue recognition feeds, invoicing with purchase orders and ACH, and a finance dashboard.

What drives this specific capability's number up, in order of impact. First, migrating existing paying subscribers from another system or another price structure. You cannot break live billing, so it needs shadow running, reconciliation and a rollback plan, and it can double the estimate on its own. Second, plan shape: flat is cheap, seats are moderate, hybrid seats plus usage plus overage is expensive because every proration and every mid cycle change multiplies. Third, tax jurisdictions. Fourth, self serve upgrade and downgrade, because every path a customer can take alone is a path you must make safe. Fifth, annual and monthly together, which doubles the proration surface.

Separately, budget the running fees. Stripe's standard card rate is 2.9% plus 30 cents in the US, Stripe Billing adds a percentage of recurring volume on top depending on your plan tier, and Stripe Tax is charged per transaction. Check current list pricing before you model margins.

When you should NOT build this

Two honest cases.

If you sell three flat plans, one currency, no seats, no usage, do not hire anyone. Stripe Checkout plus the Stripe Customer Portal plus Stripe's built in Smart Retries and its own dunning emails, wired to one idempotent webhook that flips a status field, is two to four days of work. Everything in this article still applies, but Stripe's hosted pieces already implement most of it. Anyone quoting you $40,000 for that is selling you a state machine you do not need yet.

If your actual problem is global tax, buy a merchant of record. Paddle or Lemon Squeezy become the seller of record and file the VAT and sales tax returns for you. You pay roughly 5% plus a fixed fee instead of 2.9% plus 30 cents. Under a few million in ARR selling digital products worldwide, that spread is far cheaper than registering in twenty jurisdictions and staffing the filings. The tradeoff is real: you lose control of the checkout, payout timing, and some data. Take it anyway if tax is your bottleneck.

Similarly, if your problem is usage metering at serious scale, Orb or Metronome exist and are better than what you will build. Build on Stripe directly when your pricing is your product strategy, when you need entitlements woven into the app, or when you already have subscribers on Stripe and are fixing what is there.

How to brief and vet a developer for this

Give them three things in the brief: your full price list including the plans you are embarrassed by, the exact list of what a past_due account should still be able to do, and whether existing paying customers must be migrated. Those three decide the estimate.

Then ask these.

"How do you make a webhook handler idempotent?" The answer must include a unique constraint on the event ID committed in the same transaction as the state change. "Stripe signs the payload" is a wrong answer to a different question.

"Stripe does not guarantee event order. How do you avoid writing stale subscription state?" Re-fetch the object, or version by event created timestamp. Anything else means they have not hit it.

"Walk me through your test clock setup." If they have not heard of Stripe test clocks, they have never tested a renewal, a trial ending, or a dunning cycle. They tested a first purchase and shipped.

"Which decline codes do you never retry, and why?" and "What happens when an off session charge returns requires_action?"

"Where does entitlement live, and how do you detect drift?" Listen for a reconciliation job. Nobody who has run billing for a year is without one.

"How do you handle a mid cycle downgrade?" Subscription Schedules and period end should appear in the first sentence.

Finally, ask for a redacted webhook handler from a real project. Thirty seconds of reading it tells you more than an hour of conversation.

Research & sources

The evidence behind this guide

Independent findings on why this investment pays off. Every link goes to the primary source.

  1. McKinsey argues software developer productivity can be measured by combining system-level metrics (DORA and SPACE) with its own outcome-oriented approach, which it reports deploying across nearly 20 tech, finance, and pharmaceutical companies - a claim that sparked significant debate in the engineering community. Source: McKinsey & Company (2023) →
  2. Companies in the top quartile of McKinsey's Developer Velocity Index had 2014-18 revenue growth four to five times faster than bottom-quartile peers, showing that software-building capability is a driver of business performance, not just a support function. Source: McKinsey & Company (2020) →
  3. In the Flexera 2025 State of ITAM report, respondents reported roughly 33% of SaaS spend is wasted, underscoring how paying for off-the-shelf seats and tiers that go unused erodes the supposed cost advantage of generic SaaS. Source: Flexera (2025) →
  4. Retailers connecting point-of-sale and loyalty data in an omnichannel strategy reported up to 15% lower cost per purchase and nearly 20% higher incremental store revenue. Source: Deloitte (2024) →
Rohan Malhotra · Enterprise Software Consultant

Rohan advises mid-market and enterprise teams on ERP, CRM and custom software, and has led delivery on dozens of business-software builds.

Writes for Digital Heroes, shipping business software for 2,000+ brands across 55+ countries since 2017.

FAQ

Frequently asked questions

How much does it cost to add Stripe subscription billing and dunning to an existing app?
In Digital Heroes delivery experience, $15,000 to $60,000 over 4 to 8 weeks for a focused build inside a product you already have. The low end is three flat plans, one currency, one country. The high end adds seats, annual plus monthly, trials and coupons, each of which multiplies the proration and mid cycle change paths you have to make safe.
How long does it take to build?
4 to 8 weeks inside an existing product, 10 to 16 weeks if it ships as part of a first release, and 6 to 12 months for a full billing platform with usage metering and finance tooling. The payment flow itself is about a week. The rest is the dunning state machine, entitlement caching, reconciliation, and testing renewals with Stripe test clocks.
Can you add it to our existing app without breaking current subscribers?
Yes, but if you already have paying customers on another system or another price structure, migration is the single biggest cost driver and can roughly double the estimate. It needs shadow running, a reconciliation pass against Stripe, and a rollback plan, because a bad migration bills real people twice or stops billing them at all.
What breaks at scale?
Webhook duplicates and out of order events. Stripe delivers at least once with retries for up to three days and explicitly does not guarantee ordering, so without an event ID uniqueness constraint and version checks you get duplicate seats and permanently stale plan state. The second thing that breaks is entitlement drift: accounts that stay free forever because one webhook was dropped during a deploy.
Should we use a third-party billing service instead of building on Stripe directly?
Yes if your real problem is global tax. Paddle or Lemon Squeezy act as merchant of record and file the returns for you at roughly 5% plus a fixed fee instead of Stripe's 2.9% plus 30 cents, which is cheaper than registering in twenty jurisdictions. Build on Stripe directly when pricing is your product strategy, when entitlements are woven into the app, or when you already have subscribers on Stripe.
We have a $25,000 budget. What can we actually get?
A solid focused build: Checkout or Elements, a properly idempotent webhook layer, cached entitlements with a nightly reconciliation job, a real dunning state machine with pre-dunning emails and an in-app banner, test clock coverage, and basic admin tooling. What that does not cover is multi-currency, usage metering, tax beyond one jurisdiction, or migrating existing subscribers off another system.
Why does a full billing platform cost $150,000 or more?
Because at that level you are building usage metering with asynchronous aggregation, multi-currency and multi-entity handling, invoicing with purchase orders and ACH, revenue recognition feeds for finance, and a support console. Each of those is its own subsystem with its own edge cases, and they all have to agree on the same subscription state.
How much revenue does dunning actually recover?
There is no honest industry number, and anyone quoting one at you is guessing about your card mix and your customers. What is knowable is where the lift comes from, because Stripe's Smart Retries already makes up to four attempts on its own. Your gain comes from what you add on top: pre-dunning emails 30 and 7 days before card expiry, an in-app banner with a one-click update link, and handling requires_action correctly so European cards can complete 3D Secure instead of being cancelled as failures.
What Stripe fees will we pay on top of the build?
Stripe's standard US card rate is 2.9% plus 30 cents, Stripe Billing adds a percentage of recurring volume depending on your plan tier, and Stripe Tax is charged per transaction. Disputes cost $15 each in the US on top of losing the transaction, which is why refunding on an early fraud warning is worth automating. Check Stripe's current list pricing before modelling margins.
How do I make sure custom software is secure and compliant with rules like HIPAA?
Start with the baseline every business system should have: encryption in transit and at rest, role-based access control, and audit logs. If HIPAA applies, the hosting provider must sign a Business Associate Agreement, which AWS, Azure, and Google Cloud all offer, and access controls have to be designed in from day one, not bolted on. SOC 2 certifies a company's operating practices, not a codebase, so ask vendors what they have shipped in your regulated domain rather than which logos are on their website.
Can custom software connect to the tools we already use, like QuickBooks, Stripe, and Google Workspace?
Yes, and connecting your existing tools is one of the main reasons to build custom: mainstream platforms like QuickBooks, Stripe, Shopify, and Google Workspace all publish documented APIs. Budget 1 to 3 weeks of work per integration depending on API quality and how much data flows in both directions. Ask any vendor whether they have integrated with your specific tools before, because quirks like QuickBooks' OAuth token handling and API rate limits get learned on someone's project, and it should not be yours.
How do I vet a software development agency before signing a contract?
Ask to speak with two past clients whose projects resemble yours in size and industry, and ask exactly who will write your code, since some agencies sell senior faces and deliver junior or subcontracted hands. Demand a written specification with acceptance criteria before any fixed price, and check that their portfolio links to products that are actually live. An instant quote given without questions about your workflows is the clearest warning sign there is.
If an agency builds my software, who actually owns the code?
You should own everything, assigned in writing: the contract transfers full IP to you on final payment, the code lives in your GitHub organization, and hosting runs in cloud accounts you control. The red flag is a proposal that mentions the agency's proprietary platform or framework, which usually means you are renting, not buying. Digital Heroes structures every build this way precisely so a client can fire us and lose nothing but the relationship.
What does a $50,000 custom software budget actually buy?
One core workflow done properly: 10 to 15 screens, two or three user roles, a couple of integrations, an admin panel, and automated tests, delivered in roughly 12 to 14 weeks. What it does not buy is that workflow plus a mobile app plus AI features plus five more integrations. The discipline of picking the one workflow that matters is what separates $50,000 projects that ship from $50,000 projects that stall at 70% complete.
What should I have ready before I contact a development agency?
Three things, none of them technical: a one-page description of the problem in your own words, a list of the tools and spreadsheets the new system must replace or connect to, and a must-have versus nice-to-have split of features. Add a budget range, even a wide one, because it changes the conversation from fantasy to engineering. You do not need a formal specification; producing that is what a discovery phase is for.
Couldn't I just build my app in Bubble or another no-code tool instead of hiring an agency?
For validating an idea with real users, yes, and we tell clients that honestly. The walls come later: Bubble apps cannot be exported as code to run anywhere else, performance drops on complex data operations, and usage-based pricing climbs as you grow. A meaningful share of Digital Heroes custom builds are rebuilds of no-code MVPs that proved the business worked, which is the system operating as intended: validate cheap, then build the version that scales.
How many SaaS seats do we need before building custom becomes cheaper?
The crossover usually shows up between 20 and 50 seats on premium tiers. Salesforce Enterprise lists at $165 per user per month, so 40 users cost about $79,000 a year in subscriptions, which is real money against a custom system you would own outright. Run the comparison over three years: if subscription spend beats the build cost plus 15-20% annual maintenance, custom wins on price before you even count workflow fit.
Should we build an MVP first or go straight to the full system?
MVP first, for almost everyone: ship the single workflow that carries the business value in 10 to 16 weeks, learn from real users, then fund phase two from evidence instead of guesses. The caveat is that an MVP is a small version of a well-built system, not a badly built version of a big one; the data model must already support what comes next. An agency that cannot tell you what they deliberately left out of your MVP has not designed one.
How do we get years of data out of our old system and into the new one?
Treat migration as a planned sub-project: a field-mapping document, at least one dry run on a copy of your data, then a cutover with the old system kept read-only for 30 days as a safety net. On Digital Heroes projects it consumes 10 to 15% of the budget when the old system has an export, and more when data must be pulled out screen by screen. Ask any vendor to walk you through their last migration before you sign.
Keep reading
let's build

Build something worth launching.

A plan, a team, a timeline, within 24 hours. No decks, no discovery calls. Tell us what you're building and we'll come back with a real scope and a real number.

message us directly · we reply within one business day

mission briefing

Monthly dispatch

Playbooks, real build costs, and what we're shipping. One email a month. No fluff.

visit us

New York HQ

1140 Broadway, Suite 704 · New York, NY 10001

Get directions
Online now

Hey there 👋 How can we help you today?