Pay without leaving the order, and never lose it again

Fixkamo-internal
Shipped
September 6, 2026 at 11:51 PM UTC
Author
Kamo
Commit
1e71127

Sending somebody to another page to add a card was the wrong answer, and it was wrong at the worst possible moment. The console noticed there was no card and linked to the Payment Info sub-tab. Following that link unmounted the console — and the order lived in React state and nowhere else, so the tier, term, seat count, modules and storage blocks they had just spent minutes choosing were destroyed on the way. They arrived at a card form having lost the thing they were trying to pay for, and came back to a picker reset to whatever the organization was already on. PAYING IS PART OF THE ORDER NOW InlinePaymentSection sits directly beneath the running total, and the order is never unmounted. Confirm tokenises the card and buys the plan in one press: save() takes a resolver that returns a Stripe payment method id, so the card step happens first, inside the same action, and a decoder stops everything before anything is sent. It shows one of two shapes without being asked. With no card on file the fields are simply open — there is nothing to choose between, so a list would be a step that exists only to be dismissed — and "Set as default" is checked and disabled, because a first card is the default whatever anybody clicks. With cards on file the list is shown with the account's own default already selected, so the common path is a glance and one press; adding another is a link that expands the fields inline, with "Set as default" now a real choice, and a way back to the saved card. Whichever is active carries the selection ring, so the screen always says what will be charged. Expired cards are shown, dimmed and unselectable, rather than silently offered. Payment Info goes back to being what it is for — managing cards — instead of a detour through the middle of a purchase. THE ORDER SURVIVES LEAVING THE PAGE Every change is written to the member's ui-preferences map, debounced, and read back on return: a draft begun on a laptop is waiting on the phone. Only the order — no card details pass through it, because a card goes into Stripe's own iframe and is exchanged for a token that never touches us. It is deliberately quiet. A draft matching the live subscription is not a draft, so nothing is stored and nothing is announced. One that does differ says so, with one click to start over. Drafts expire after a week, because a plan abandoned last quarter is not a decision in progress and the catalogue may have moved under it. A link naming a plan beats a saved draft — the person has just said what they want — and add-ons the catalogue no longer carries are dropped on restore rather than held invisibly against a difference nobody could see. ALSO Card fields read the MUI theme instead of hard-coded near-black, which was invisible ink on a dark background. Brand marks are the networks' own abbreviations rather than a four-character slice that rendered Mastercard as "MAST". A spent SetupIntent is discarded after a purchase, so a second card gets a fresh one instead of an already-consumed client secret failing silently. Two races found on a third pass and fixed: the payment panel never re-read its cards after a purchase, so buying with a new card left the fields open inviting the card just used; and the restore landed after the URL preselect, letting a week-old draft overwrite the plan somebody had clicked a moment earlier. Fixed while here, both surfaced by this work: Date.now() during render (the React Compiler purity rule, and a countdown that would tick on unrelated re-renders), and a setState inside the preselect effect — the sales dialog is derived from the link now rather than written from an effect. Full suite green first run: 495 files, 6114 tests. 38 new here. Typecheck clean, and eslint on these files is clean where HEAD had an error and a warning. Copy is in kamo-translation-dictionary 9526e398, pushed first.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing