The 5 SaaS revenue leak study
I'm recruiting 5 indie SaaS founders on Stripe (or Paddle) + Postgres for a public, fully redacted study. You run ProdVerdict on your setup in a ~10-minute Zoom call. I publish aggregate counts only. No customer IDs, emails, or subscription IDs.
Slots open: 5/5. The findings table fills in as runs complete. Volunteer your setup or read why billing drift is real.
Background
Why we're running this
Webhooks are necessary. They are not sufficient. Stripe documents out-of-order delivery and multi-day retries. Practitioners add nightly reconciliation anyway. This study measures how often indie setups actually drift.
Stripe docs
Stripe does not guarantee event order
Webhook deliveries can arrive out of order and retry for up to three days. Your handler must cope without assuming sequence.
Read sourceAlex Mayhew
Billing bugs can cost five figures
Missed webhooks and race conditions in subscription upgrades leak revenue before anyone notices in dashboards.
Read sourceOperational.co
Daily reconciliation is the backstop
Webhooks fail often enough that teams run a cron comparing Stripe records to their database every day.
Read sourceDEV Community
Retries expire; drift stays
After Stripe gives up retrying, state stays wrong unless you reconcile against the payment API on a schedule.
Read sourceBase44Devs
Paid in Stripe, locked in the app
Four silent links between payment success and access: wrong event subscription, bad signature parsing, column name mismatch, stale session.
Read source
Patterns
Common drift patterns (industry)
These show up in billing writeups and support threads. They are not study results yet. After 5 concierge runs we'll count how many setups had each pattern.
Webhook refactor drops a branch
Handler rewrite removes the `has_paid_access = true` path on `subscription.updated`. Tests pass with mocks. Drift shows up weeks later.
Missing subscription.deleted handler
Cancel in Stripe never flips the DB flag. Customer keeps Pro access for months.
Plan drift after a price change
New Stripe price ID ships but the `plans:` map in code or config still points at the old tier.
Duplicate stripe_customer_id
Backfill or import writes two user rows to one customer ID. Webhook updates the wrong row.
Findings
0/5 completeAggregate table (in progress)
Each row is one volunteer SaaS. “Leaks” = paying customers locked out. “Wrongful” = cancelled customers still accessing. “Drift” = plan mismatch between Stripe and the DB.
| SaaS | MRR | Stack | Leaks | Wrongful | Drift | Est. $/mo leak |
|---|---|---|---|---|---|---|
| SaaS A | $5k–10k | Next.js + Stripe + Supabase | — | — | — | — |
| SaaS B | $10k–50k | Rails + Stripe + RDS | — | — | — | — |
| SaaS C | $1k–5k | Next.js + Stripe + Neon | — | — | — | — |
| SaaS D | $50k+ | Remix + Stripe + Planetscale | — | — | — | — |
| SaaS E | $1k–5k | Next.js + Paddle + Supabase | — | — | — | — |
| Total | — | — | — | — | ||
Numbers populate as concierge runs complete. Last updated: pending first run.
Methodology
How the runs work
- Founder creates a restricted Stripe key (Customers: Read, Subscriptions: Read).
- Founder creates a read-only Postgres role on the
userstable. - Founder runs
npx prodverdict check accessin a Zoom share-screen. Founder keeps all secrets; I only see the redacted findings. - I record aggregate counts plus 1–2 anonymized representative findings.
- I redact all customer IDs, emails, subscription IDs. Only aggregate counts go public.
Volunteer
Want to be one of the 5?
Free concierge run. ~10 minutes on Zoom. You keep all the data. I get a redacted finding for the study. If we find drift, you get a fix list. If we don't, you get a “no drift found” line for your README.
Prefer to run it yourself? 5-minute quickstart — no signup, no call, no pitch.