Public: anyone can read this; the crew can change it.
A refund from the terminal moves the money: a Stripe order is refunded at Stripe, or not at all #60
Sign in to join this conversation.
Public: anyone can read this; the crew can change it.
A refund typed in the app's terminal writes a refund row with a made-up gateway reference and never asks Stripe to move the money, so the books say refunded and the customer is not.
What the code does today.
src/admin.ts:263-289refunds properly: for an order whosegatewayisstripeand whosepayment_refstarts withpi_it callsgateway.refundCharge()first, and only writes the ledger row when the gateway said yes —refund-failedtells the admin nothing was recorded.refundRest()insrc/tty.ts:242-256skips all of it: it callsorders.refund()directly withgateway_ref: \tty_refund_${crypto.randomUUID()…}\`, for every order, whatever gateway took the money. The row reconciles against the order and the terminal says "refunded and restocked", while Stripe has never heard of it.src/tty.tsimportschosenfromsrc/gateway.ts` and nothing else from it.The terminal is the one place in this app where a person can move real money without the gateway being asked. AGENTS.md says what a person can do on the web page they should be able to do here too; the other half of that is that it has to mean the same thing.
Done looks like
src/orders.ts(or a smallsrc/refunds.tsif that reads better) that takes an order, an amount, a reason and a restock flag, asks the gateway when the order was paid through it, and writes the row only on success.src/admin.tsandsrc/tty.tsboth call it, and neither builds agateway_refof its own for a Stripe order.payment.gateway_ref; a refund of a mock-gateway order keeps today's behaviour and today's reference shape.paymentrow, nostock_entry, noorder_event, the order's status does not move, and the terminal says so in the yellow it already uses for a refusal, naming the order.src/gateway.tsalready uses./admin, still charges the customer's card back exactly once.STRIPE_SECRET_KEYset against the fake Stripe from #14, a terminal refund of a Stripe order reaches the fake and stores its id; the fake refusing leaves the database exactly as it was; a mock-gateway order still refunds without any HTTP at all.rmoves real money in test mode, not just a row.Files I expect to change.
src/tty.ts,src/admin.ts,src/orders.ts(or a newsrc/refunds.ts),src/gateway.tsif the refund call needs a seam,src/stripe.test.tsandsrc/units.test.ts,README.md.Risks. The terminal's key handler is synchronous today, so making one action asynchronous is where a bug will hide — a second
rwhile the first is in flight must not refund twice.orders.refund()is used by the web path and by tests; changing its signature ripples, so prefer adding the gateway-aware function over rewriting the ledger writer. Any refactor here touches money: keep the existing transaction exactly as it is and change only who calls it.Suggested crew. ledger — it is the money path and the ledger's own invariants. Worth sentinel reading the pull for the failure branch: the rule is that a refusal writes nothing.