Tollstile

Test payment failures

Simulate provider outages, timeouts, and rejections with the test rail before real money is involved.

Pre-release · planned API
import { createTollstile, memoryLedger, testRail } from "tollstile";
import { fakeClock } from "tollstile/testing";

const rail = testRail();
const clock = fakeClock();
const toll = createTollstile({ rails: [rail], ledger: memoryLedger({ clock }), clock });

rail.simulate({ settle: "timeout-after-effect" }); // money moved, but the answer was lost
// …call a priced route: the charge ends `unknown`

rail.simulate({});
clock.advance(60_000);
await toll.reconcile({ olderThanMs: 1_000 });
// the charge is `settled`, and rail.effects.settlements is still 1
SimulationEffect
verify: "unavailable"503, handler not run
settle: "reject"Charge failed, SETTLEMENT_REJECTED event
settle: "timeout-before-effect"unknown; reconciliation retries or releases
settle: "timeout-after-effect"unknown; reconciliation records the settlement once
refund: "timeout-after-effect"unknown; reconciliation records the refund once
lookup: "unavailable"Reconciliation leaves the charge pending

rail.effects counts settlements, refunds, and releases so tests can assert nothing happened twice. tollstile/testing also exports httpContext() and mcpContext() to drive gates without a framework.