DEV Community

Ray
Ray

Posted on

Why I Self-Host My Stripe Billing Monitor Instead of Paying for a SaaS

I caught a billing bug in production because my monitoring tool ran on the same machine as my app. Not on someone else's cloud. Not behind a 3-day trial wall. On my $5/month VPS, processing webhook events in real-time.

Here's why that matters, and why I built BillingWatch instead of paying for yet another SaaS.

The Problem With Stripe Monitoring

Stripe's dashboard tells you what happened. It doesn't tell you when something stops happening.

No new subscriptions for 48 hours? Dashboard looks fine — there's just nothing there. Payment failure rate quietly crept from 3% to 18%? You'll notice in a week when MRR dips. A promo code stacking bug producing negative invoices? Stripe will happily process those.

The monitoring gap isn't Stripe's fault. They build a payment processor, not an anomaly detector. That's your job.

Why Not Use [Insert SaaS Here]?

I evaluated three billing monitoring SaaS tools. They all had the same problems:

1. Your revenue data lives on their server. Every webhook event — customer emails, payment amounts, subscription tiers, failure reasons — gets piped to a third party. For what? A dashboard and some Slack alerts.

2. Pricing scales with your events. The more successful your business gets, the more you pay for monitoring. That's a misaligned incentive.

3. You can't customize the detectors. Every business has different failure modes. A marketplace with 50% refund rates has different "normal" than a B2B SaaS with annual contracts. Fixed thresholds miss this entirely.

What I Built Instead

BillingWatch is a FastAPI app that listens to your Stripe webhooks and runs 7 anomaly detectors against every event:

DETECTORS = {
    "charge_failure_spike":  # Failure rate > 15% in 1 hour
    "duplicate_charge":      # Same customer charged twice in 5 min
    "fraud_spike":           # Dispute rate exceeds threshold
    "negative_invoice":      # Invoice total < 0 (broken promo)
    "revenue_drop":          # MRR 15% below 7-day rolling avg
    "silent_lapse":          # Active sub, no payment in period
    "webhook_lag":           # Events arriving 10+ min late
}
Enter fullscreen mode Exit fullscreen mode

Each detector is a Python class with a check() method. Want to change the failure spike window from 1 hour to 30 minutes? Edit one number. Want to add a detector for unusual refund patterns? Subclass BaseDetector and add 20 lines.

The whole thing runs in ~50MB of RAM. SQLite for persistence. No external dependencies besides Stripe's webhook payload.

The Setup That Actually Works

git clone https://github.com/rmbell09-lang/BillingWatch.git
cd BillingWatch
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env  # Add your Stripe webhook secret
python3 -m uvicorn src.main:app --port 8000
Enter fullscreen mode Exit fullscreen mode

Point your Stripe webhook endpoint at your-server:8000/webhook/stripe. That's it.

For local development, use Stripe CLI:

stripe listen --forward-to localhost:8000/webhook/stripe
Enter fullscreen mode Exit fullscreen mode

What It Looks Like In Practice

Last Tuesday at 2 AM, BillingWatch fired a charge_failure_spike alert. Payment failure rate hit 22% over a 45-minute window. Turned out a card processor was having intermittent issues — Stripe's status page didn't update for another 3 hours.

Without monitoring, I would've woken up to angry customer emails. Instead, I woke up to a webhook notification that said "failure rate anomaly detected, 14 failures in 45 min, top decline code: processing_error."

The fix was nothing — the processor recovered on its own. But knowing about it in real-time vs. discovering it from churn data a week later is the difference between "that was fine" and "why did 30 people cancel?"

The Self-Hosting Advantage

When your monitoring runs on your infrastructure:

  • Latency is zero. Webhook hits your app → detector runs → alert fires. No round-trip to a third-party API.
  • Data never leaves. Your customer's payment data stays on your machine. Period.
  • Cost is fixed. Whether you process 100 or 100,000 events/month, it's the same $5 VPS.
  • You own the logic. Custom detectors for your specific failure modes. Not a feature request in someone else's backlog.

When This Isn't the Right Choice

If you want:

  • Pretty dashboards with historical trends → use Stripe's dashboard or ChartMogul
  • Revenue forecasting and cohort analysis → use ProfitWell or Baremetrics
  • Zero setup, zero maintenance → use a SaaS (seriously, they're fine for most people)

BillingWatch is for the specific problem of real-time anomaly detection on your webhook stream. It's a smoke detector, not a financial analyst.

Get It

MIT licensed. Zero SaaS fees. Works with Stripe test mode.

git clone https://github.com/rmbell09-lang/BillingWatch.git
Enter fullscreen mode Exit fullscreen mode

GitHub repo · Landing page

If you're running a SaaS on Stripe and you've ever had a silent billing failure, you know why this exists.

Top comments (0)