2
7 Comments

7 places SaaS quietly leak money between Stripe and their own database

I've spent most of my career doing reconciliation at a fintech — the unglamorous job of making two systems agree about money. Point that habit at indie SaaS and you keep finding the same leaks, over and over. Roughly in order of how often I see them:

  1. Somebody cancels through support, your app gets updated, the Stripe subscription doesn't. Congrats, you're now billing someone who thinks they left — that one usually surfaces as a chargeback. The mirror version (cancelled in Stripe, app never heard) is quieter: you're just... serving them. For free. Indefinitely.

  2. Users with Pro access and no subscription behind it. Failed trial conversion, a comp somebody set up by hand and forgot, a checkout webhook that never landed. Nobody's watching for "has features but no invoice", so nobody finds it.

  3. Refund goes out, access stays on. The refund happens in Stripe, the entitlement lives in your database, and there's no code connecting them unless you specifically wrote it. Most people never wrote it.

  4. Webhook drift. Your endpoint said 200, something inside the handler quietly died, and from that day forward Stripe and your DB just... disagree. This is the one people find a year later while migrating something unrelated.

  5. past_due limbo. Card's failing, Stripe's retrying, user still has full access — and your churn numbers say everything's fine, because nobody technically cancelled. It's a grace period, except nobody decided to grant it.

  6. The plan mismatch. Upgraded in the app during some support call, never re-priced in Stripe. Team features at the Pro price, forever. (Also runs in reverse, which is worse — that one ends in a very awkward email to a customer.)

  7. That 100%-off coupon from your launch three years ago. Still valid. Still getting passed around.

The nasty part: your MRR chart flags none of this. Revenue looks fine right up until you actually reconcile the two sides.

The fix isn't clever. It's a cron job that diffs Stripe against your DB and yells when they disagree. Boring wins.

(I'm doing free read-only leak audits for a handful of SaaS right now — basically running these exact checks. Link in profile if you want one. Either way I'll keep posting patterns here.)

on August 11, 2026
  1. 1

    The “has features but no invoice” check is probably the most underrated one here. Revenue dashboards can look healthy while entitlement drift quietly compounds in the background. The dangerous part is that each individual mismatch looks harmless until you reconcile both systems against each other.

    1. 1

      Ye, Compounds is the right word. Individually each mismatch rounds to zero, which is why nobody escalates it — then you diff the two systems for the first time and the total has a comma in it. One thing I'd add: the drift rate is a health metric on its own. If new mismatches keep appearing after you clear the backlog, something upstream is still broken, usually a webhook path.

      1. 1

        Yeah, that drift-rate point is a good distinction. A cleared backlog can create a false sense of resolution if the system keeps generating new mismatches. Curious how you’ve been tracking that in practice?

        1. 1

          Unhh,Honestly nothing fancy — a nightly job dumps every mismatch into a table: type, the two object ids, first_seen, last_seen. Clearing the backlog closes rows. The number I actually watch is new rows per day, by type. The total tells you how much cleanup is left; the rate tells you what's still broken — a spike in "cancelled in Stripe, active in app" usually means a webhook consumer regressed. One hard-won tip: alert on the rate, not the total, otherwise the alert is permanently red and everyone learns to ignore it by week two.

          1. 1

            That distinction between cleanup and ongoing drift is really useful. I’d be interested in continuing this conversation — what’s the best email to reach you at?

            1. 1

              Sure — carsh.blue522@gmail.com. Happy to talk shop.

              1. 1

                Thanks! I’ve just sent it over.

                Looking forward to hearing your thoughts whenever you have a chance.