Billing Replay

REPRODUCTION GUIDE / NODE.JS

Test a credit grant that arrives twice.

A handler can return success twice and still charge your product for twice the work. Test the balance your app stores after each delivery.

Suppose one paid invoice grants ten credits. The expected balance after processing it is ten. If the same event is delivered again, the expected balance is still ten.

DeliveryEvent IDExpected credits
Firstinvoice-110
Repeatedinvoice-110

Stripe documents that webhook events can be delivered more than once. It also distinguishes repeated event IDs from separate events describing the same underlying object. Your business rule needs to define which operation can grant credits once. Stripe webhook guidance.

Run the reproducible example

Download Billing Replay and its examples, unzip it, then run these commands from the billing-replay folder with Node.js 22 or later. This release was tested on Node 24.

node bin/billing-replay.mjs demo --mode vulnerable --json broken.json --html broken.html
node bin/billing-replay.mjs demo --mode fixed --json fixed.json --html fixed.html

The first command returns exit code 1 because the teaching implementation contains deliberate defects. In its duplicate-invoice scenario, the second delivery produces 20 credits instead of 10. The corrected example keeps the balance at 10. Both runs use the same written expectations.

Connect the check to your actual app

The included adapter is a teaching example. To check your application, provide an adapter that resets test state, delivers an event through your actual billing handler, and observes your actual balance and access decision.

export default {
  async reset({ scenarioId }, { signal }) {
    // Reset only the disposable test accounts for this scenario.
  },
  async deliver(event, { signal }) {
    // Map this normalized fixture to your staging handler.
  },
  async observe({ customerId }, { signal }) {
    // Read the real app state: { access: boolean, credits: number }.
  }
};

This outline intentionally has no substitute implementation. Fill it with your own application calls; returning the expected values would hide the behavior you are trying to measure. The downloaded README includes the complete interface and a local HTTP example.

node bin/billing-replay.mjs run --suite examples/basic-suite.json --adapter ./your-adapter.mjs --json result.json --html result.html

Define the boundary of the result

A sequential duplicate test covers the sequence you executed. It does not cover simultaneous workers, a crash between updating a balance and recording an event, or two distinct event IDs for the same purchase. Add those tests at your application or database boundary when they apply.

The supplied fixtures are normalized synthetic events. They do not validate Stripe signatures or reproduce complete provider payloads. The demo uses logical timestamps to teach one explicit ordering policy. Stripe’s created timestamps can tie and must not be treated as a reliable event ordering rule. Stripe event ordering.

Make one billing flow reproducible.

The proposed $99 pilot covers one agreed staging flow, three failure cases, an adapter, and a result report. This is an early product with no customer case studies yet.

Review the pilot scope →