Credit-Card Entry Sheet — Parity Case Spec
Problem Statement
How might we enumerate every behavioral case of the card-entry bottom sheet (open → fill → tokenize → confirm/close) as a falsifiable parity contract, so the migration is verified case-by-case instead of happy-path-plus-luck?
Origin: a 4-agent migration audit found the card-entry flow “looks complete,” but a manual read of the Confirm path surfaced a real divergence (lost
toast.erroron tokenization failure) that no happy-path check would catch. A case spec is the antidote.
Recommended Direction
A map-flow-style doc at apps/docs/src/content/docs/flows/payment-card-entry-sheet.md built on a
state-machine spine (cases = transitions), with factored axis tables (provider, lifecycle,
outcome, save-vs-temporary) instead of a Cartesian blow-up, and a provider × outcome reachability
matrix as the exhaustiveness proof. The full payment leg is covered by linking the existing
journey/checkout claim IDs (journey-package-booking JPB-002..005, checkout-page-package-confirm)
downstream of “token produced” — not re-documenting them. Each claim row carries source sourceRef,
target sourceRef, and a parity verdict (✅ match / ⚠️ divergence / 🔵 intended / 🟡 latent-in-both).
Built GB PrimePay first as a tracer bullet — it is the default provider AND the only one that supports save-to-member, so it exercises both the temporary and persisted paths. Once the structure is validated on GB PrimePay, fan out to Omise, Stripe, Xendit.
Key Assumptions to Validate
- Source = target spec, except flagged divergences. RESOLVED for the pivotal case: source
onReservationErrorcallstoast.error(message)on web (checkOutEvents.ts:1007); monorepo stubbed it to a no-op → confirmed regression, not an intended redesign. - Unconditional sheet-close on failure is parity, not a bug. RESOLVED: source closes the
sheet + resets the model unconditionally too (
BookingPayment.ts:539-541). The earlier “regression” read (from an agent summary) was wrong. - Axis factoring stays exhaustive — prove via a provider × outcome matrix with no uncovered cell.
- The spec will be consumed by a verification pass (e2e or manual QA) — else it rots. (Open Q.)
MVP Scope
In: the state-machine spine; ALL four providers listed in the reachability matrix; the lifecycle table (open / close / reopen / reset); the outcome + error cases (incl. the lost-toast gap) with GIVEN/WHEN/THEN for the high-risk ones; GB PrimePay traced fully end-to-end (temporary + save) as the tracer that proves the factoring before fan-out.
Out (this pass): re-documenting 3DS / create-booking internals (link them); the saved-card
edit/delete detail page (CreditCardDetailPage — separate sheet); Omise/Stripe/Xendit deep rows
(matrix-stubbed as TODO-next-pass).
Not Doing (and Why)
- Full Cartesian matrix — ~400 rows, mostly N/A; the reachability matrix proves coverage without listing dead cells.
- Per-provider sub-docs — duplicates shared lifecycle/error logic 4×.
- Deep downstream payload/3DS docs — already owned by the journey docs; link to avoid drift.
- Fixing the bugs inside this task — spec first; fixes land in a separate worktree so the contract stays the reference.
Open Questions
- Is the no-op
onReservationErroran accepted web-vs-hybrid simplification, or unfinished migration? (Now leaning “regression” — source shows a web toast;@hh-pegasus/shared-lib/toastexists in the monorepo.) - Should this be a standalone
kind: flowdoc, or a step linked fromjourney-package-booking? (Built standalone for now.) - Does a Cypress/e2e pass exist for the sheet to consume these scenarios, or is verification manual for now?
Status
- ✅ One-pager saved (this file).
- ✅ Flow doc: all 4 providers covered at
apps/docs/src/content/docs/flows/payment-card-entry-sheet.md(quote-verified vs both repos). GB PrimePay (CCS-0xx), Omise (CCS-O-xx), Stripe (CCS-S-xx), Xendit (CCS-X-xx). - 📌 Fan-out finding: the lost-toast regression is one root cause across all 4 providers (the
onReservationErrorno-op). Single fix closes all. Stripe is the exception on close (stays open on failure). Xendit deferred-token failure is the highest-risk case. - ⬜ Fix: restore web
toast.errorinonReservationError. - ⬜ Verify: Xendit deferred-token-failure feedback at create-booking.