@volter/twin-stripe
v0.1.0
Published
Local Stripe twin — a faithful, stateful local Stripe API your real `stripe` SDK talks to unmodified. Mirror, simulate, and fork. Built on @volter/twin.
Readme
@volter/twin-stripe
The Stripe twin — a local, spec-correct replica of the Stripe REST API on the
shared @volter/twin kernel. The real stripe SDK works against it
unmodified; it's the QA stack's authoritative local Stripe (it replaced the old
hand-made in-process mock).
Surface
- REST API (
stripe-twin.ts→handleStripeTwinRequest; HTTP wrapperstripe-server.ts→createStripeTwinServer): charges, customers, payment_intents, setup_intents, payment_methods, products, prices, invoices, invoiceitems, subscriptions, refunds, coupons, promotion_codes, identity verification sessions, ephemeral_keys, checkout sessions, billing/customer-portal sessions + configurations. - Writes are local transactions (action log); reads are the projection (R3/R5/R18).
- Card declines (
stripe-twin.ts): Stripe's documented test cards get vendor-faithful outcomes on charge create / PaymentIntent confirm —4242…4242(andpm_card_visa/tok_visa) succeed;4000…0002,4000…9995,4000…0069,4000…0127,4000…0119(and the matchingpm_card_*/tok_*tokens) return the realcard_errorenvelope (HTTP 402) and leave a confirmed PaymentIntent atrequires_payment_method. Unknown cards succeed. See_card.declines-test-set-onlyinstripe-known-deviations.json. - Idempotency keys: the
Idempotency-Keyheader is honored on POST — a replay with the same key returns the stored response without re-applying the write (_idempotency.stored-per-root). - Events (
stripe-events.ts): fires Stripeeventobjects on write (payment_intent.succeeded,customer.subscription.created,invoice.paid, …). - Conformance (
stripe-conformance.ts): field name + type checked vs Stripe's real OpenAPI (standing gate). - UI mirror (
stripe-mirror-ui.ts): a Stripe-dashboard-style React app over the twin's own REST API.
CLI
world-stripe serve [--read-only] [--port N] [--root DIR]
world-stripe mirror [--port N] [--root DIR]
world-stripe conformance [--fields FILE] [--root DIR]serve defaults to simulator (the stack uses the twin as a writable Stripe).
Point the real stripe SDK at it with { host, port, protocol: 'http' }.
Four ways to use it
- Mock SDK — zero edits (preferred):
STRIPE_TWIN_URL=http://127.0.0.1:PORT node --require @volter/twin/inject your-appredirects the realstripeSDK fromapi.stripe.comto the twin (cookbook/zero-edit-inject). For the browser too:volter-twin proxy --target <app> --map stripe=http://127.0.0.1:PORT(browser + backend share one twin). Or override directly:new Stripe(key, { host: '127.0.0.1', port: PORT, protocol: 'http' }). - API + CLI —
world-stripe serve(writable) + drive withvolter-twin status|plan|refs stripe, then push. - Read-only —
world-stripe serve --read-only: unlimited local reads, no rate limits; writes refuse like Stripe (4xx). - UI mirror —
world-stripe mirrorrenders a Stripe-dashboard-style view of the twin's state.
(See the repo's GETTING-STARTED → "Four ways to use a twin".)
Stable on the twin rubric: fidelity, read/write/fork, sync, observability, event emission, and conformance are tracked with explicit coverage gaps.
Coverage
Goal: honest, explicitly tracked coverage of Stripe's core feature surface. The only accepted carve-outs are the explicit out-of-scope items listed below. Anything not done or carved out is a gap to close.
Done — core resources: customers, charges, payment_intents (+ confirm),
setup_intents (+ confirm), payment_methods (+ detach), subscriptions (create/
update/cancel), prices, products, invoices (+ finalize/pay/void), invoiceitems,
refunds, disputes (+ update/close), payouts (+ cancel), balance_transactions,
balance (synthesized from the ledger), events (Events API list/retrieve),
Checkout Sessions (create/retrieve/list/line_items/expire + modeled completion that
creates+links a payment_intent/subscription/setup_intent; amount_total computed from
line_items), Customer Portal (billing_portal sessions + configurations create/retrieve/
list/update), Connect (connected accounts create/retrieve/list/update/delete +
login_links, and transfers platform→connected-account create/retrieve/list filtered by
destination; a new account starts un-onboarded with charges/payouts disabled + a requirements
hash), plus ephemeral_keys, identity verification_sessions, file_links
(synthesized) and coupons/promotion_codes (retrieve/list, seed-only). Cursor pagination
(limit/starting_after/ending_before + has_more); per-resource list filters; expand[]
on the modeled paths; vendor-faithful test-card declines (4242… succeeds; documented
decline PANs + pm_card_*/tok_* tokens return the real card_error 402); idempotency keys
(Idempotency-Key replay, no re-write); events/webhooks (stripe-events.ts); spec
conformance gate; UI mirror (rung-5 ✅, client/stripe-mirror.tsx + structure check);
connector pull (customers + subscriptions) + push (create/update/cancel/confirm/detach/
finalize/pay/void/close to the real REST surface).
This cycle added: ACH/SEPA micro-deposit verification (payment_intents/setup_intents
/verify_microdeposits — confirm with pm_us_bank_account → requires_action with a
verify_with_microdeposits next_action, then the 32/45 amounts or SM11AA descriptor →
succeeded), Customer.list_payment_methods (/v1/customers/:id/payment_methods),
subscription items CRUD (create/retrieve/list/update/delete, kept in sync with the parent
sub's items list), subscriptions/invoices search, invoice line items
(/lines + add_lines/update_lines/remove_lines with recomputed totals), invoice
edge-action gating (finalize/pay/mark_uncollectible/void state machine), Tax transactions
(create_from_calculation + create_reversal with negated lines), quote cancel-from-draft/
open lifecycle gates, customer + subscription discount delete, promotion-code update
(active toggle), charge fraud-marking (fraud_details[user_report]), and the Radar
family — Reviews (/approve), Value Lists (+ items), Rules — plus a Radar
dashboard screen in the UI mirror.
Planned (known-missing, will do) — Issuing (cardholders/cards/authorizations/
transactions + test helpers), Terminal (readers/locations/connection tokens), Treasury
(financial accounts), Climate, Financial Connections, Entitlements, Billing
credit grants + usage alerts, Connect remaining surfaces (account sessions, connected-account
payouts, top-ups, payout reverse), Reporting/Sigma, the legacy Sources API, and
additional list endpoints/filters as needed. (See stripe-capabilities.ts for the full honest
todo list — every entry not explicitly out-of-scope is a tracked gap.)
Out of scope (deliberately not modeled, with reason) —
- Pixel-rendering Stripe-hosted Checkout / Customer Portal pages: the hosted HTML is
Stripe's, not an API object — the twin models the Session/API object + redirect
urlinstead (now Done; the page pixels stay out of scope). See_checkout.hosted-page-pixelsinstripe-known-deviations.json. - Real money settlement / bank movement (proposed — pending owner approval): actually moving funds is real-world infra, not the API — the payout/balance_transaction/balance objects and their lifecycle are modeled.
