Test rail
Run the full payment lifecycle locally with no wallet, network, or account.
import { createTollstile, memoryLedger, testRail } from "tollstile";
export const toll = createTollstile({ rails: [testRail()], ledger: memoryLedger() });Send Payment: test to pay a fixed price, or pay against the quote from the 402:
Payment: test quote=<token> proof=p1 payer=agent_1 amount=$0.01 limit=$1.00 paymentId=operation_1| Parameter | Effect |
|---|---|
quote | The quote token from the 402. Required on dynamic routes. |
proof | Stable proof id. Without it, every request is a fresh payment. |
payer | Payer id, used by requirements and idempotency scope. Defaults to test-payer. |
paymentId | Protocol idempotency key when no client key is supplied. The HTTP or MCP client key takes precedence. |
amount | Must equal the price, otherwise the proof is invalid. |
limit | Capacity of a reusable authorization. |
signature | Stands in for payer evidence a real rail keeps until the charge is final. It is stored in the authorization's data and dropped by the rail's redact once the charge is settled. |
| Option | Default | Effect |
|---|---|---|
authorization | "single" | "reusable" behaves like an L402 credential or a KYAPay token |
refund | true | false behaves like a rail that cannot refund, such as x402: only the authorization flow is offered |
Over MCP, send the same string in _meta["tollstile/test-payment"]; the receipt is _meta["tollstile/test-receipt"].
See Test payment failures for rail.simulate() and rail.effects.
Never alongside live rails
createTollstile() refuses a test rail configured together with a live rail.
Exercise retries
curl -i -H 'Payment: test proof=p1 payer=agent_1 paymentId=operation_1' localhost:3000/weather
curl -i -H 'Payment: test proof=p1 payer=agent_1 paymentId=operation_1' localhost:3000/weatherOn the fixed-price quickstart route the first call succeeds and the second returns 409 already_paid. Omit paymentId and both client-key carriers to test 409 proof_already_used. With authorization: "reusable", omitting a key creates another charge instead. Payment: test alone uses a fresh proof ID on each request, so it does not test proof replay.
See Idempotency and the conformance kit.