Add dev-only frontend Square mock mode for as-if-live walkthroughs
VITE_SQUARE_ENVIRONMENT=mock renders a plain HTML card form (MockCardForm) instead of the Square Web Payments SDK iframe, minting the same cnon: tokens the backend dev mock accepts — all 8 payment flows run end-to-end locally with zero credentials. - isSquareMock() gated on import.meta.env.DEV: structurally impossible in a production build even if the env var is mis-set - MockCardForm: Luhn/brand/expiry/CVC validation, Amex 15-digit + 4-digit CVC, error states, disabled propagation — mirrors the real form's onReady contract so CardSelection.isCardValid and submit guards behave identically - tokenize() maps typed card -> deterministic cnon: token matching backend detectCardInfo (4242->test-card, 4111->visa, 5555->mastercard, 3782->amex) - lazy-loaded via dynamic import: mock code ships in its own chunk, referenced only from the mock branch, never statically imported into the main bundle - docs: .env.example (mock pairing with SQUARE_ENVIRONMENT=mock), P11 plan (mock opt-in + canonical-last4 caveat), Feature Catalog (2.1, 2.5) - prettier formatting fixes in 10 unrelated files (line wrapping only)
This commit is contained in:
@@ -163,7 +163,7 @@ Multi-method payment system accepting Square (card terminal & online), cash, gif
|
||||
**Related:** [[Booking System|1. Booking System]] (deposits), [[Gift Cards|4. Gift Cards]] (pay by gift card), [[Admin Dashboard|5. Admin Dashboard]] (till purchases)
|
||||
|
||||
### 2.1 Online Card Payment (Square — saved cards or new cards via Web Payments SDK)
|
||||
**What it does:** Customers pay online with a card. Saved-card payments work via Square tokenized card IDs (`ccof:`); new-card payments are tokenized client-side through the Square Web Payments SDK into `cnon:` nonces and accepted by the backend everywhere. The backend rejects raw PANs (PCI-DSS parity, mirrored by the dev mock). Local dev without Square credentials (`VITE_SQUARE_APPLICATION_ID`/`VITE_SQUARE_LOCATION_ID`) keeps new-card entry gated behind a `CardEntryUnavailable` notice. Used for deposits, full payments, balance payments, and tips.
|
||||
**What it does:** Customers pay online with a card. Saved-card payments work via Square tokenized card IDs (`ccof:`); new-card payments are tokenized client-side through the Square Web Payments SDK into `cnon:` nonces and accepted by the backend everywhere. The backend rejects raw PANs (PCI-DSS parity, mirrored by the dev mock). Local dev can opt into the built-in frontend mock (`VITE_SQUARE_ENVIRONMENT=mock`), which renders a plain HTML card form and mints the same `cnon:` tokens the backend dev mock accepts — a full as-if-live walkthrough with zero credentials; without credentials or mock mode, new-card entry is gated behind a `CardEntryUnavailable` notice. Used for deposits, full payments, balance payments, and tips.
|
||||
|
||||
**Layman summary:** "Pay online with your card — just like any online shop."
|
||||
|
||||
@@ -191,7 +191,7 @@ Multi-method payment system accepting Square (card terminal & online), cash, gif
|
||||
**Related:** [[Gift Cards|4. Gift Cards]], [[VAT Calculation|2.10 VAT Calculation]]
|
||||
|
||||
### 2.5 Saved Cards
|
||||
**What it does:** Customers can save their card details for faster checkout next time. Cards are tokenized via Square (`ccof:` card IDs; the full PAN exists only in Square's vault — our DB stores only the reference + brand/last4/fingerprint). The dev mock mirrors this (raw PANs rejected). Soft-deleted with 7-year UK retention. The "Add Card" flow posts a `card_token` (a Web Payments SDK `cnon:` nonce) to `CreatePaymentMethodFromToken`, which calls `CreateCardOnFile`. When frontend Square credentials are unset (local dev), add-card shows the `CardEntryUnavailable` notice.
|
||||
**What it does:** Customers can save their card details for faster checkout next time. Cards are tokenized via Square (`ccof:` card IDs; the full PAN exists only in Square's vault — our DB stores only the reference + brand/last4/fingerprint). The dev mock mirrors this (raw PANs rejected). Soft-deleted with 7-year UK retention. The "Add Card" flow posts a `card_token` (a Web Payments SDK `cnon:` nonce) to `CreatePaymentMethodFromToken`, which calls `CreateCardOnFile`. When frontend Square credentials are unset and mock mode is off (local dev), add-card shows the `CardEntryUnavailable` notice; with `VITE_SQUARE_ENVIRONMENT=mock` it uses the frontend mock form instead (saved mock cards appear as `ccof:mock_*` rows in the dev DB).
|
||||
|
||||
**Layman summary:** "Save your card for next time — one-click payment."
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ New-card entry is **tokenized via the Square Web Payments SDK** (`cnon:` nonces)
|
||||
## Current State (verified August 2026)
|
||||
|
||||
### Frontend — new-card entry is TOKENIZED (no raw PANs anywhere):
|
||||
All 8 flows now render `SquareCardInput` (`frontend/src/lib/components/payments/SquareCardInput.svelte`), which loads the Square Web Payments SDK (`frontend/src/lib/square/square.ts`, env-gated on `VITE_SQUARE_APPLICATION_ID`/`VITE_SQUARE_LOCATION_ID`) and tokenizes the entered card into a `cnon:xxx` nonce sent as `new_card_token`. The tokenized form is the only card-entry path — there is no raw-PAN fallback. When the SDK env vars are not configured (e.g. local dev), flows keep the `CardEntryUnavailable` notice.
|
||||
All 8 flows now render `SquareCardInput` (`frontend/src/lib/components/payments/SquareCardInput.svelte`), which loads the Square Web Payments SDK (`frontend/src/lib/square/square.ts`, env-gated on `VITE_SQUARE_APPLICATION_ID`/`VITE_SQUARE_LOCATION_ID`) and tokenizes the entered card into a `cnon:xxx` nonce sent as `new_card_token`. The tokenized form is the only card-entry path — there is no raw-PAN fallback. Local dev can opt into the built-in frontend mock (`VITE_SQUARE_ENVIRONMENT=mock`): `SquareCardInput` renders a plain HTML card form (`MockCardForm.svelte`) and `tokenize()` returns the deterministic `cnon:` tokens the backend dev mock (`SQUARE_ENVIRONMENT=mock`) accepts, so all 8 flows run end-to-end with zero credentials. When neither the SDK env vars nor mock mode are configured, flows keep the `CardEntryUnavailable` notice. Note: in mock mode the response's brand/last4 always reflects the token's canonical test card (e.g. `4242 4242 4242 4242` → VISA 4242, `4111 1111 1111 1111` → VISA 1111) — an arbitrarily typed card that is Luhn-valid but not one of the four canonical numbers still maps to `cnon:test-card` (VISA 4242), so its displayed last4 is the canonical one, not the typed digits. Cosmetic and dev-only.
|
||||
|
||||
1. `frontend/src/routes/tip/+page.svelte` — tip; saved-card list + `card_id`, SquareCardInput for new card
|
||||
2. `frontend/src/routes/pay-tip/[id]/+page.svelte` — tip; same pattern
|
||||
@@ -105,7 +105,7 @@ All 8 flows render `SquareCardInput` and send the resulting `cnon:xxx` as `new_c
|
||||
- **Square iframe requires HTTPS** — localhost is exempt, but any non-local dev URL needs TLS.
|
||||
- **Tokenization is one-shot** — a `cnon:` nonce is single-use. Implemented per the plan: each flow caches the token after the first `tokenize()` and **reuses it on retry** (the backend idempotency key dedups), so a retry does not re-tokenize or double-charge.
|
||||
- **PCI-DSS parity preserved** — the backend rejects raw PANs by design; the tokenized form never falls back to sending PAN/CVC to our server.
|
||||
- **`CardEntryUnavailable` stays as the offline/dev fallback** — when no `VITE_SQUARE_*` credentials are configured, flows keep the gated notice rather than breaking.
|
||||
- **`CardEntryUnavailable` stays as the fallback** — when neither the `VITE_SQUARE_*` credentials nor dev mock mode (`VITE_SQUARE_ENVIRONMENT=mock`) are configured, flows keep the gated notice rather than breaking. The dev mock is local-only and token-only; `VITE_SQUARE_ENVIRONMENT=mock` must never be set in a deployed (non-local) build.
|
||||
|
||||
---
|
||||
|
||||
@@ -115,7 +115,7 @@ All 8 flows render `SquareCardInput` and send the resulting `cnon:xxx` as `new_c
|
||||
- [x] Square Web Payments SDK loads (sandbox + prod URLs, env-gated)
|
||||
- [x] `SquareCardInput` tokenizes cards → `cnon:xxx`
|
||||
- [x] All 8 flows re-enabled to send nonces, not PANs (tip ×3, booking payment, deposit, Buy a Gift Card, account Add Card, admin till `online_square`)
|
||||
- [x] `CardEntryUnavailable` kept only as the no-credentials fallback
|
||||
- [x] `CardEntryUnavailable` kept only as the no-credentials/no-mock fallback (local dev opts into the built-in frontend mock via `VITE_SQUARE_ENVIRONMENT=mock`)
|
||||
- [x] Backend nonce paths verified unchanged (Step 4/5 done)
|
||||
- [x] Frontend checks pass: svelte-check 0 errors, eslint 0 errors, build succeeds
|
||||
- [x] Docs updated (README, Gap Backlog, Feature Catalog, Technical Manual)
|
||||
|
||||
Reference in New Issue
Block a user