#195 added a notice reading "Demonstration only — This shop is not taking payments at the moment", gated on demoMode alone. That claim is false in a configuration the ops runbook actively steers towards.
demoMode and paypalClientId are independent. checkDemoMode and checkPayPal only make the PayPal secrets required when DEMO_MODE=false; nothing forbids them while it is true. And production-stack-cutover.md:65 says flipping to false without all three crash-loops the container — so the only safe order is to populate the secrets while demo mode is still on, verify, then flip. In that window Cart.tsx renders live PayPal buttons directly beneath a banner telling the customer the shop takes no payments, and it is precisely the window in which someone is clicking around production checking their work.
That is the same failure #195 fixed, pointed the other way: silent where a warning was needed, then confidently wrong where a customer can actually be charged. Telling someone nothing will be shipped above a live PayPal button is worse than saying nothing.
The notice now describes the button instead of the shop, which is true in both configurations and stays visible in the one with two controls that do different things — where a customer most needs to be told they differ. Gating it on !paypalClientId would also have removed the false claim, by hiding the notice exactly there, which is the worse trade.
The test for it was also not testing it. "Says so before the customer commits" seeded an address with isDefault: true, and the cart auto-selects the default on load — so an address was already selected and the button already rendered when it asserted. It would have passed with the notice moved inside the selectedAddressId guard, which is the regression it exists to catch. It now seeds no address and asserts the notice is up while the checkout button is absent, which states the property directly.
Mutation-tested rather than assumed: moving the Alert inside that guard fails the new test, and would not have failed the old one.
Verified: 3 end-to-end tests pass against a browser, frontend build clean, lint 0 errors (2 pre-existing warnings in src/filters.ts).
#195 added a notice reading "Demonstration only — This shop is not taking payments at the moment", gated on `demoMode` alone. That claim is false in a configuration the ops runbook actively steers towards.
`demoMode` and `paypalClientId` are independent. `checkDemoMode` and `checkPayPal` only make the PayPal secrets *required* when `DEMO_MODE=false`; nothing forbids them while it is `true`. And `production-stack-cutover.md:65` says flipping to `false` without all three crash-loops the container — so the only safe order is to populate the secrets while demo mode is still on, verify, then flip. In that window `Cart.tsx` renders live PayPal buttons directly beneath a banner telling the customer the shop takes no payments, and it is precisely the window in which someone is clicking around production checking their work.
That is the same failure #195 fixed, pointed the other way: silent where a warning was needed, then confidently wrong where a customer can actually be charged. Telling someone nothing will be shipped above a live PayPal button is worse than saying nothing.
The notice now describes the button instead of the shop, which is true in both configurations and stays visible in the one with two controls that do different things — where a customer most needs to be told they differ. Gating it on `!paypalClientId` would also have removed the false claim, by hiding the notice exactly there, which is the worse trade.
The test for it was also not testing it. "Says so before the customer commits" seeded an address with `isDefault: true`, and the cart auto-selects the default on load — so an address was already selected and the button already rendered when it asserted. It would have passed with the notice moved inside the `selectedAddressId` guard, which is the regression it exists to catch. It now seeds no address and asserts the notice is up while the checkout button is absent, which states the property directly.
Mutation-tested rather than assumed: moving the Alert inside that guard fails the new test, and would not have failed the old one.
Verified: 3 end-to-end tests pass against a browser, frontend build clean, lint 0 errors (2 pre-existing warnings in `src/filters.ts`).
Closes #203
Refs #195
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#195 added a notice reading "Demonstration only — This shop is not taking payments at the moment", gated on `demoMode` alone. That claim is false in a configuration the ops runbook actively steers towards.
`demoMode` and `paypalClientId` are independent. `checkDemoMode` and `checkPayPal` only make the PayPal secrets *required* when `DEMO_MODE=false`; nothing forbids them while it is `true`. And `production-stack-cutover.md:65` says flipping to `false` without all three crash-loops the container — so the only safe order is to populate the secrets while demo mode is still on, verify, then flip. In that window `Cart.tsx` renders live PayPal buttons directly beneath a banner telling the customer the shop takes no payments, and it is precisely the window in which someone is clicking around production checking their work.
That is the same failure #195 fixed, pointed the other way: silent where a warning was needed, then confidently wrong where a customer can actually be charged. Telling someone nothing will be shipped above a live PayPal button is worse than saying nothing.
The notice now describes the button instead of the shop, which is true in both configurations and stays visible in the one with two controls that do different things — where a customer most needs to be told they differ. Gating it on `!paypalClientId` would also have removed the false claim, by hiding the notice exactly there, which is the worse trade.
The test for it was also not testing it. "Says so before the customer commits" seeded an address with `isDefault: true`, and the cart auto-selects the default on load — so an address was already selected and the button already rendered when it asserted. It would have passed with the notice moved inside the `selectedAddressId` guard, which is the regression it exists to catch. It now seeds no address and asserts the notice is up while the checkout button is absent, which states the property directly.
Mutation-tested rather than assumed: moving the Alert inside that guard fails the new test, and would not have failed the old one.
Verified: 3 end-to-end tests pass against a browser, frontend build clean, lint 0 errors (2 pre-existing warnings in `src/filters.ts`).
Closes#203
Refs #195
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closing unmerged: this change is already on main and this PR adds nothing.
The same work merged as b897e99 (PR #214). Diffing this branch against current main for the files it touches returns empty:
git diff main f4a036e -- frontend/src/cart/Cart.tsx frontend/tests/e2e/pages/CartPage.ts
main/Cart.tsx already carries the corrected notice — "The Checkout (Demo) button places a pretend order: nothing is charged, and nothing will be shipped" — replacing the claim that the shop is not taking payments, which was false in the DEMO_MODE=true with PayPal configured window the cutover runbook steers through.
The branch simply predates several later merges (#202, #204, #205, #206), which is why it presents as diverged. Merging it would contribute no change.
Closing unmerged: this change is already on `main` and this PR adds nothing.
The same work merged as `b897e99` (PR #214). Diffing this branch against current `main` for the files it touches returns empty:
```
git diff main f4a036e -- frontend/src/cart/Cart.tsx frontend/tests/e2e/pages/CartPage.ts
```
`main/Cart.tsx` already carries the corrected notice — "The Checkout (Demo) button places a pretend order: nothing is charged, and nothing will be shipped" — replacing the claim that the shop is not taking payments, which was false in the `DEMO_MODE=true` with PayPal configured window the cutover runbook steers through.
The branch simply predates several later merges (#202, #204, #205, #206), which is why it presents as diverged. Merging it would contribute no change.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
#195 added a notice reading "Demonstration only — This shop is not taking payments at the moment", gated on
demoModealone. That claim is false in a configuration the ops runbook actively steers towards.demoModeandpaypalClientIdare independent.checkDemoModeandcheckPayPalonly make the PayPal secrets required whenDEMO_MODE=false; nothing forbids them while it istrue. Andproduction-stack-cutover.md:65says flipping tofalsewithout all three crash-loops the container — so the only safe order is to populate the secrets while demo mode is still on, verify, then flip. In that windowCart.tsxrenders live PayPal buttons directly beneath a banner telling the customer the shop takes no payments, and it is precisely the window in which someone is clicking around production checking their work.That is the same failure #195 fixed, pointed the other way: silent where a warning was needed, then confidently wrong where a customer can actually be charged. Telling someone nothing will be shipped above a live PayPal button is worse than saying nothing.
The notice now describes the button instead of the shop, which is true in both configurations and stays visible in the one with two controls that do different things — where a customer most needs to be told they differ. Gating it on
!paypalClientIdwould also have removed the false claim, by hiding the notice exactly there, which is the worse trade.The test for it was also not testing it. "Says so before the customer commits" seeded an address with
isDefault: true, and the cart auto-selects the default on load — so an address was already selected and the button already rendered when it asserted. It would have passed with the notice moved inside theselectedAddressIdguard, which is the regression it exists to catch. It now seeds no address and asserts the notice is up while the checkout button is absent, which states the property directly.Mutation-tested rather than assumed: moving the Alert inside that guard fails the new test, and would not have failed the old one.
Verified: 3 end-to-end tests pass against a browser, frontend build clean, lint 0 errors (2 pre-existing warnings in
src/filters.ts).Closes #203
Refs #195
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
Closing unmerged: this change is already on
mainand this PR adds nothing.The same work merged as
b897e99(PR #214). Diffing this branch against currentmainfor the files it touches returns empty:main/Cart.tsxalready carries the corrected notice — "The Checkout (Demo) button places a pretend order: nothing is charged, and nothing will be shipped" — replacing the claim that the shop is not taking payments, which was false in theDEMO_MODE=truewith PayPal configured window the cutover runbook steers through.The branch simply predates several later merges (#202, #204, #205, #206), which is why it presents as diverged. Merging it would contribute no change.
Pull request closed