DEV Community

Ray
Ray

Posted on

I built a self-hosted Stripe anomaly detector after missing a billing bug for 3 days

Three days. That's how long a billing anomaly can go unnoticed if you're only watching Stripe's dashboard and waiting for customer complaints.

I built BillingWatch after exactly that happened. A webhook retry storm was silently inflating my event counts and I missed it until a user asked why they'd been charged twice.

GitHub: github.com/rmbell09-lang/billingwatch

The problem with "just watch Stripe"

Stripe's dashboard shows you events. It doesn't tell you when your failed charge rate just doubled in 6 hours, or when 1,000 payment methods are about to expire in the next 30 days, or when your subscription churn spiked 3x compared to last week's baseline.

You find out when a customer emails you.

What BillingWatch detects

  • Failed charge spikes — failed charges as a % of total, rolling window comparison
  • Subscription churn spikes — cancellations vs. 7-day baseline
  • Webhook retry storms — same event firing 5x+ in 24h window
  • Payment method expiry clusters — cards expiring soon, grouped by cohort
  • Refund rate anomalies — refund % spiking above threshold

Each pattern runs on a rolling window. When a threshold breaches, it queues a digest email.

Architecture

FastAPI receives your Stripe webhooks. Events land in SQLite. Pattern detection runs on each ingest (lightweight — sub-10ms per event on typical load). Anomaly digests go out via configurable email.

Multi-tenant: one BillingWatch instance can monitor multiple Stripe accounts. Useful if you have staging + production or multiple products.

Runs locally for development, deploys to any VPS in minutes.

Why self-hosted beats cloud dashboards for this

Cloud monitoring tools are great until you need to customize a threshold. With SaaS dashboards you're locked into their anomaly definitions. With BillingWatch you edit a config file.

You also own the raw event data. If you want to build a custom alert — say, flag any single customer who generates 10+ webhook retries — you write a SQL query against your local SQLite. No API export, no data caps.

Getting it running

You need:

  1. A Stripe webhook pointing at your BillingWatch instance
  2. Python 3.9+
  3. An SMTP config for digest emails (or disable email and poll the API)

If you already have Stripe webhooks set up, you can be running it in under 10 minutes.

It's free and self-hosted. No SaaS subscription, no per-event pricing.

github.com/rmbell09-lang/billingwatch

Top comments (0)