fix: review-loop A — discount credit on admin payments, campaign over-credit cap, sweep replay window, dedup refund revalidation, duplication/modularisation, GBP pence naming

Round-A fresh review (6 agents) + fix + secondary cross-cutting + verification rounds:
- F1: campaign discounts reduce the charged amount (deposit credit + admin PaymentModal discounted total); capDiscountToRemainingObligation prevents over-credit at completion in all four campaign blocks
- F2: sweep replay rescue distinguishes legitimate same-key retries (21h window) from expired-key new charges; ccof blind-fails leave pending + CRITICAL instead of clawing back
- F3: post-start online overflow carved as a tip record (mirrors terminal split builder)
- A1: single-source Square decline-code classification (till delegates to square.IsDefinitivePaymentError)
- A2/A5: refund attempt-cap literals consolidated; refund-failure counter capped + reset on terminal resolutions + admin notifications
- A3/A9: idempotency helpers adopted across derivations; IsExplicitDevOrMockEnv relocated + all gates unified (incl. health-check)
- A7: 2FA user+IP limiter + TRUST_PROXY_HEADERS startup warning; SNAPSHOT_ENC_KEY startup validation; TWO_FACTOR_PEPPER docs corrected
- A8: snapshot encryption on all 6 write sites + marker-aware reuse paths; MPV->SPV effective voucher type (single VAT point)
- A10/A11/A12/A16: gift-card slot scan advances past failed; amount-aware refund reconciliation; completed-booking refund re-check; PaymentWasRefunded on SquareClient interface
- Dedup refund revalidation on tip/terminal/gift-card paths; sweep acknowledged_at IS NULL parity; refund-notification single source (exported payments.InsertRefundFailedNotifications)
- Duplication/modularisation round: shared frontend helpers (sanitizeDecimalInput, campaignDiscountCents, twoFactorBlocksSavedCards getter, generateUUID), single-source MaxIdempotencyKeyLength, notification-helper consolidation, snapshot-guard comments
- Cross-cutting GBP rename: Cents->Pence across backend + frontend + tests (26 identifiers, 16 files)
- Tests: 11 behavior-change tests updated to new invariants; coverage for fixed functions; frontend vitest 55 tests; docs corrected (test counts, 2FA delivery, pre-launch checklist, resolution status)
- gitleaks: allowlist backend/internal/square test fixtures (mock idempotency keys)

All 25 backend packages pass; frontend 55/55 + build clean; env-docs 41/41.
This commit is contained in:
2026-08-22 00:34:50 +01:00
parent 6d82535780
commit faceb9809c
49 changed files with 2006 additions and 1074 deletions
@@ -72,6 +72,15 @@ Notes:
- Spot-checks on 13 Aug 2026 confirmed the tree matches the recorded statuses for C1C3, H1H5, B1B11, D7, D9, D10, D11, D12, D13. Any residual uncertainty is limited to the exact frontend styling diffs (F1F14, M1M3), which batch-1 verified; treat those as "verify on final visual pass" if in doubt.
- The only genuinely open item is **T1** (frontend automated tests), scheduled for batch 2.
### Round A re-review (14 Aug 2026)
A fresh-eyes round-A review (legal/ops/docs + money-path) of commit `e9315c9` re-opened **two entries this table marked RESOLVED**:
- **F1 — admin overcharge (re-opened).** The round-1 frontend F1F14 batch (touch-target sizing etc.) is verified, but the round-A **money-path** review found a separate live money bug still present in the same admin payment surface: campaign credit can be applied on terminal/cash/saved-card admin payments (`CreateTerminalPayment` / till / saved-card paths), producing a charge that exceeds the remaining booking balance. Owned by the money-path fix round; not closed by the round-1 batch.
- **/terms tiers & refund-method wording (D6/D10 — re-opened).** Round-1 marked the terms-page wording reconciliation (D6) and "refunds to the original payment method" verification (D10) RESOLVED, but the round-A legal/docs review found the `/terms` route still contradicted the code: §3 claimed the deposit is forfeited only under 24 hours' notice (the code keeps up to 50% of the subtotal on 2472h notice and everything under 24h — `CalculateRefundForCancellation`, `handlers/payments/refunds.go`), and §4 overbroadly claimed refunds go to "the original payment method" (cash refunds are credited to the account balance, gift-card refunds to the balance when no `gift_card_id`, only card goes back via Square). The `/terms` route has now been aligned to the verified three-tier schedule and per-method refund disclosures (Aug 2026).
No other RESOLVED entries were contradicted by round A.
---
## Overall Verdict