billing-invariant
v0.1.0
Published
Asserts Stripe billing math (credit conservation, proration totals, invoice totals) is actually correct across a sequence of webhook events.
Maintainers
Readme
billing-invariant
Checks that a sequence of Stripe webhook events actually adds up — before a silent billing-math bug reaches a customer's invoice.
Stripe's test clocks let you simulate subscription and proration scenarios, but nothing asserts the resulting math is correct. A real incident motivated this: a Stripe proration bug silently destroyed gift-subscription credit across 14 redemptions, and nothing caught it until a customer noticed. This package is the check that would have.
Quick start
npm install --save-dev billing-invariantimport { checkInvariants } from "billing-invariant";
const violations = checkInvariants(events); // events: your Stripe webhook log
console.log(violations);[
{
rule: 'credit-conservation',
message: "Customer cus_gift credit balance decreased from 14000 to 13000 (decrease of 1000) with no matching charge/debit event.",
eventIds: [ 'evt_gift_bal_1' ],
expected: 14000,
actual: 13000
}
]Drop it straight into a test assertion:
expect(checkInvariants(events)).toHaveLength(0);What it checks (v0.1)
- Credit conservation — a customer's credit balance never decreases without a matching charge/debit event accounting for it. Directly matches the cited real incident.
- Proration conservation — an invoice's proration-marked line items,
grouped by their exact
period.start/period.endwindow (real Stripe proration pairs share the same window, not adjacent ones), include both a credit (old-plan) and a charge (new-plan) side; a one-sided group is flagged. - Invoice total match — an invoice's stated
totalequals the sum of its own line items.
No live Stripe API calls, ever — this library only reads event objects you already have from a webhook log or a test-clock test run.
Module format
Ships as dual ESM + CJS with full TypeScript types for both:
import { checkInvariants } from "billing-invariant"; // ESMconst { checkInvariants } = require("billing-invariant"); // CJSv0.1 scope
- Stripe only. Three deterministic checks (above).
- Not yet covered: orphaned-negative-balance-line-item detection (v0.2), any provider other than Stripe, live Stripe API calls.
See DETAILS.md for the full design and docs/USAGE.md for the complete API reference.
Examples
See examples/ for a sample event log and a CI test-assertion usage pattern.
License
MIT
