Skip to content

Billing correctness monitoring for Stripe

Everything returned 200.
The invoice was still wrong.

Murphy is for billing and monetization engineers, and for whoever has to answer for a number that turns out to have been wrong all year. It re-derives what an invoice should have said from the objects it was built out of, then holds that against what you charged.

Revenue leakage does not throw. It looks like a plausible invoice and it gets paid.

Run it on your invoices

Two weeks free. The first report covers the invoices you already have.

Murphy example finding · a real incident, re-derived
Caught Undercharge

A customer added seats and was refunded for it.

What the objects say

subscription
created back-dated to the start of the migration
coupon
attached, and recorded on the subscription
first payment
computed without the discount
seats
raised from 5 to 12 mid-period
proration
computed with the discount

What it comes to

invoice
in_1PQ7xA2eZvKYlo2CkzGSFP4X
claimed
-$18.40
recomputed
$61.60
gap
$80.00 owed and never billed

What went quiet

A coupon can be attached and discount nothing. A retry can be scheduled and never run.

All three of these happened. None of them threw an error. A billing engineer found each one by hand, long after it started.

The coupon that discounted nothing

A Stripe coupon carries a list of the products it is allowed to reduce. The list on this one matched no product on the invoice.

It was attached and valid the whole time, and the dashboard showed it sitting there. The invoice generated with no discount on it at all. Attached is a different fact from applied, and only one of the two shows up on the invoice.

The coupon that paid a customer to buy more

A coupon went onto a back-dated subscription during a migration. The initial payment was worked out without the discount and the prorations were worked out with it.

The customer expanded their seat count and the delta came out negative, so they were refunded for buying more. Every invoice in the sequence added up on its own.

The retry that was never scheduled

A payment failed and the retry sequence that should have followed it did not run. The invoice sat open for five weeks. The card had worked the whole time.

Nothing errored, because what went wrong was an absence. The only evidence was next_payment_attempt sitting null on one invoice, a field no handler has a reason to re-read.

Why nothing caught it

Your monitoring is watching for errors. This is not an error.

Every layer that could have caught it was right to report success.

  • Your dashboards are clean

    Error rate and latency say nothing about whether a number is correct.

  • Your integration test passes

    It asserted that the coupon was attached. The coupon was attached.

  • Nothing happened at all

    There is no error to catch and no event to handle. A step that never ran emits nothing, so an integration built on incoming events waits for a message that is not coming.

How it works

Murphy re-derives the invoice from the objects it was built out of.

It checks what you charged. It never checks what you were charged. The only account it reads is your own Stripe account.

Which means it flags the gaps that run in your favour and the gaps that run against you. An overcharge has a customer who will eventually report it. An undercharge has nobody.

How Murphy arrives at a gap What Stripe sent feeds a re-derivation of the invoice total. The recomputed total is held against the total that was charged. The difference between them is the gap Murphy flags. WHAT STRIPE SENT RE-DERIVE HOLD AGAINST subscription invoice discount meter event the total the invoice should have said recomputed $61.60 charged -$18.40 GAP $80.00
  1. 01

    Read

    A restricted read key and one webhook endpoint.

    Murphy reads your existing invoices, subscriptions and discounts on the day you connect, then keeps up from the webhook. It holds its own copy, so a check reads that rather than your account, and it never issues a write back to Stripe.

  2. 02

    Recompute

    The number, derived again from the objects the invoice was built out of.

    Not read off the invoice and not compared against last month. Given the same objects the recomputed number comes out the same every run, so a finding is a fact about the objects rather than about when it was looked at.

  3. 03

    Flag

    The gap between what you charged and what the objects say.

    It arrives with the objects it was derived from, so the first question after the alert has an answer already attached.

The first run is the interesting one. Murphy re-derives every invoice you already have, so the first thing it tells you is what has been wrong for a while, not what breaks next Tuesday.

Living with it

A tool you mute is a tool you are paying for and not using.

The person who installs billing monitoring is the person who gets woken up by it, so the noise question is the whole question.

One cause is one finding.
A coupon scoped wrong across four hundred subscriptions is one finding with four hundred accounts attached to it, not four hundred emails. It closes once, when the cause is fixed.
You set the floor.
Nothing under the amount you choose becomes a finding. Most teams start somewhere around the cost of looking into it.
Intended is a thing you can say.
A negotiated price, a goodwill credit, a deal that was always going to look wrong. Mark it and that check stays quiet on that account, permanently, without turning the check off for everyone else.
Nothing here is a model.
There is no baseline, no trend and no anomaly score. A finding is arithmetic over objects you can open in your own dashboard, which means you can settle it in about the time it takes to read it.
It is not an outage.
A daily digest by default, because a two-month-old undercharge does not need a pager. Send it to a webhook and route it yourself if you would rather.

The first run alerts nobody. It produces a report, you read it, and you decide what is worth hearing about before anything is turned on.

The failure catalog

The expensive failures are the ones you have not met yet.

You can write a check for the bug that already bit you. The rest of them you would have to know about first, and nothing in your week suggests you go looking. Murphy keeps a catalog of the ways billing goes wrong quietly, grouped by what breaks.

Discounts and coupons

  • attached to a subscription and applied to nothing on the invoice
  • reducing a price further than it was ever scoped to reduce
  • counted into the prorations and left out of the first payment

Retries that never run

  • a failed payment that starts no retry
  • an invoice left open with next_payment_attempt null

Tax and credits

  • a line that changed price and kept the tax the old price was calculated on
  • a credit balance that never draws down against the invoice it was meant for

Invoice state

  • several open invoices on one customer, none of them resolving
  • a subscription that keeps invoicing after the cancellation reached Stripe

Usage and metering

  • a month of consumption that no invoice ever picks up
  • a meter event that never lands, on a month the invoice still goes out for

Proration and credits

  • a mid-cycle plan change that credits a number nobody chose
  • a seat expansion that comes out as a refund

Plans and tiers

  • billed for a tier more expensive than the one that ran

Currency and precision

  • every amount in a currency wrong by a factor of a hundred

Reported totals

  • a total Stripe reports that its own underlying objects do not add up to

Every category here runs on the day you connect. Nothing in the catalog is held back for a higher tier and nothing in it is a plan.

What this is built on

Every entry in it cost somebody money.

The catalog started with billing systems I ran and broke, at companies between three and two hundred and fifty million in revenue. Engineers who hit their own version filled in more of it, and the rest comes from behavior Stripe documents in public.

Nothing in it is a failure somebody imagined might be possible. That is the whole difference between a catalog and a feature list.

The working notes go up one failure at a time, with the Stripe behaviour underneath each one written down. Who is behind this.

Whatever is wrong right now has been wrong for a while.

Connect a read key and the first report tells you which of your invoices already disagree with the objects they were built from. Two weeks free, and no call to book.

Start free