"Checkout is not live yet"
A disabled button with a true sentence is a feature. The shop's most important copy is the copy that admits the payment path does not exist yet.
Every shop has a checkout button. This one has a checkout button that says:
Checkout — not live yet (disabled)
and next to it, in the cart: “Payment is not connected, so nothing can be ordered or charged yet — the shipping prices above are real quotes.”
That is not a placeholder waiting to be designed. It is the honest state of the product, and it is verified like any other feature.
The state machine the copy describes
The storefront’s payment path has exactly two states, and the copy is different in each:
| State | Button | Sentence | What is true |
|---|---|---|---|
| No Stripe keys configured | disabled, aria-disabled="true" |
“Checkout — not live yet” + reason | The cart works; shipping quotes are real; nothing can be charged |
| Keys configured | enabled | the normal checkout path | Stripe hosts the card form; the server never sees a card |
Until a secret key is saved, the button stays disabled and says so. That is the whole design: the shop can be built, filled, priced and browsed without ever pretending it can take money.
The copy is data, and it is verified
The storefront separates words from layout: store-level copy, product copy and shipping
labels live in the vault and are editable from the desk with no deploy; only layout is code. That
split is what makes the honest state cheap to maintain — the go-live is a vault write, not a code
change [FACT, copy-map.md].
But “words are data” is not a licence to say anything. The independent verifier checks the rendered DOM, not the source:
- the button’s text is exactly “Checkout — not live yet”;
disabledis true andaria-disabled="true"is set, so it is unusable and announced as unusable;- the reason line is present and not hidden;
- the cart toast says “Added to cart — 1 item. Checkout is not live yet.”;
- the meta description matches the shipped description, including the honest clause;
- the text passes contrast (5.72:1 for the disabled checkout line);
- the preview page says, in its own meta description, that it is “Not the live shop. Checkout is
not live.” and makes no network requests [FACT,
honest-states.md].
A disabled button is not a fallback. It is a claim with a test.
The traps the copy work found
Writing honest copy at this level surfaces traps that are invisible in a design file:
- A field that is stored but rendered nowhere. One description field is saved by the desk and appears on no page. The copy contract marks it explicitly so an operator edit is not silently lost.
- A field missing from the server cap table. A long footer line was silently dropped on save until the table was fixed; the contract records which fields had this bug.
- A claim that must stay verifiable. The
[ PROOF ]band makes factual statements about the shop; the rules say the claim must remain true, and the origin audit (the previous piece) is what keeps it true. - The
[ Preview ]page. A static design-review page that is explicitly not the live shop and says so in its meta description and its first paragraph.
None of these are features. They are the difference between a shop that is honest and a shop whose copy happens to be true today.
Go-live is a data change
The Stripe integration is built and sandbox-ready; what remains is keys, a webhook endpoint and a
go-live decision. The important property is where those live: the secret key goes into the vault
(0600, outside the app tree), the webhook secret goes into the vault, and the copy flips because
the server sees the keys — not because someone edited a page [FACT, stripe-live.md].
That is why the honest state is stable: a deploy cannot accidentally make the shop claim it takes money. The claim is derived from configuration the operator sets deliberately.
What I’d tell someone
- Build the honest state first. A disabled button with a reason is better than a fake checkout, and it lets you demo the real product before the payment path exists.
- Verify copy in the DOM. Text,
disabled,aria-disabled, the reason line, contrast, the meta description — all of it is testable. - Make go-live a data change. Keys in a vault, copy derived from their presence; no code edit and no deploy should be able to flip the claim by accident.
- Write down the traps. The stored-but-unrendered field, the field missing from a cap table: they are copy bugs that look like design bugs.
- Keep the preview honest too. A design preview that says “not the live shop” is part of the product.
“Checkout is not live yet” is the most important sentence on the site. It is the one that costs a sale and keeps the shop trustworthy.
The copy map and the honest-states inventory are attached below.
Receipts — every primary source used
Every claim in the piece maps to a surface map, an assertion or a captured state. Files in backticks are in this pack.
| Claim | Primary source |
|---|---|
| The disabled checkout is the product’s real state: button, reason line, cart toast | copy-map.md §3 (surfaces) and §4 (the resolved false-copy incident) |
| The copy is data, not layout; store-level, product-level and shipping words live in the vault | copy-map.md §2 (three layers, three change paths) and §3a/§3b |
The verifier checks the rendered DOM: exact button text, disabled, aria-disabled, reason line, toast, meta description, contrast 5.72:1 |
honest-states.md |
| The preview page says “Not the live shop. Checkout is not live.” and makes no network requests | honest-states.md |
| Seventeen occurrences of “checkout is not live yet” across five files, all true when written and false after Stripe landed; fixed 2026-10-17 | copy-map.md §4 and §8 change log |
| The stored-but-unrendered meta-description field; the field missing from the server cap table | copy-map.md §3a traps and §3b |
| Go-live is a vault/configuration write, not a code edit or deploy | copy-map.md §2 and §6 rule 2 |
Seven paid sessions in the order ledger, two live at in_review, the rest terminal |
copy-map.md §5 |
The claim that must stay verifiable ([ PROOF ] band) |
copy-map.md §3a (proof group) and §6 rule 4 |
Capture date: 2026-10-17, when the copy pass was deployed and the local/remote hashes were matched. The vault paths and host names in the map are the shop’s; they are reproduced with placeholders where an address would identify a person.