From 741fa3fbf9f12b78d0c023d1f12527eb6392b2cb Mon Sep 17 00:00:00 2001 From: AdilAzhariOmsan Date: Wed, 4 Nov 2026 07:36:46 +0800 Subject: [PATCH] docs: formalise Phase 2 owner decisions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record two owner decisions made on 2026-11-04 before Phase 2: - Accept ADR-0020: Filament for platform administration at /admin (staff only, English-only, Phase 2 tokens, use cases only, Laravel 13 check in Phase 3 with fallback). Vendor dashboard stays Vue + Inertia. Adds the surface-separation and language clauses, and notes that the ADR-0011 mechanisms it relies on are still Proposed. - Approve P-1: guest browsing and cart; account-required checkout with inline registration; verified email before order submission and payment initiation; resume checkout after verification. Resolve Phase 1 briefing C7 (P-1 inline sign-up vs FR-ID-01 redirect): verification becomes a step inside checkout. The signed 60-minute link is unchanged. Amend FR-ID-01, FR-CART-02, FR-CHK-01, CJ-1, ADR-0015 §1 and security-model §5 routing to enforce it consistently. Scope PRD §6.1, gate 7 and NFR-I18N-01 to the storefront and vendor UI, because the admin is English-only (briefing C13). Record everything in PHASE-1-REVIEW Addendum A. Documentation only. --- docs/architecture/README.md | 2 +- ...0015-checkout-and-payment-orchestration.md | 6 ++- ...020-platform-administration-in-filament.md | 42 +++++++++++++++++- docs/architecture/security-model.md | 2 +- docs/discovery/PHASE-1-REVIEW.md | 43 +++++++++++++++++++ docs/product/functional-requirements.md | 6 +-- docs/product/non-functional-requirements.md | 2 +- docs/product/product-requirements.md | 14 +++--- docs/product/user-journeys.md | 12 +++++- 9 files changed, 112 insertions(+), 17 deletions(-) diff --git a/docs/architecture/README.md b/docs/architecture/README.md index a9d20f6..cf7b952 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -44,7 +44,7 @@ The template is [`adr/ADR-TEMPLATE.md`](adr/ADR-TEMPLATE.md) (ADR-0006). Propose | [0017](adr/0017-domain-events-and-transactional-outbox.md) | Transactional outbox and idempotent consumers | Proposed | | [0018](adr/0018-backend-frontend-contracts.md) | PHP data objects → generated TS types | Proposed | | [0019](adr/0019-search-behind-a-port.md) | Search port, MySQL first | Proposed (D-8) | -| [0020](adr/0020-platform-administration-in-filament.md) | Filament for staff; Vue for vendors and customers | Proposed | +| [0020](adr/0020-platform-administration-in-filament.md) | Filament for staff; Vue for vendors and customers | Accepted (owner, 2026-11-04) | | [0021](adr/0021-environments-demo-data-and-production-isolation.md) | Isolated environments; reproducible demo | Proposed | Engineering standards that do **not** shape a domain, data-isolation, money, runtime-dependency or deployment boundary live in [`../engineering/`](../engineering/), not in an ADR. diff --git a/docs/architecture/adr/0015-checkout-and-payment-orchestration.md b/docs/architecture/adr/0015-checkout-and-payment-orchestration.md index 5095e6e..ddecd0a 100644 --- a/docs/architecture/adr/0015-checkout-and-payment-orchestration.md +++ b/docs/architecture/adr/0015-checkout-and-payment-orchestration.md @@ -50,7 +50,11 @@ Rejected. A closed browser loses the order, and the return URL is client-control ## Decision -1. **Checkout requires an authenticated customer.** Guest checkout is out of scope for v2 (PRD §6); the cart is merged on login. This removes the guest-cart ownership class of bug. +1. **Checkout requires an authenticated customer with a verified email (P-1, approved 2026-11-04).** + - Guests may browse and keep a cart. They start checkout by signing in or registering inline. Either way the guest cart is merged into the customer's cart (FR-CART-02). + - Step 1 (place) and Step 2 (attempt) both reject a guest or an unverified account **before any write**. No OrderGroup, reservation, redemption, points hold, payment attempt or provider call is made. The check is in the use cases as well as the routes (security-model §5), so it holds even when the UI is bypassed. + - Verification happens before placement, and reservations are created only at placement. So the reservation TTL never runs while a customer verifies. + - Guest checkout is out of scope for v2 (PRD §6). This removes the guest-cart ownership class of bug. 2. **Step 1, place.** `POST /checkout` carries an `Idempotency-Key`: a UUID generated when the checkout page renders, unique per customer. 1. Re-price the cart server-side: current prices, promotions, points, shipping quotes. If the total differs from the total the customer confirmed, return **409** with the new summary. Never charge an amount the customer did not see. diff --git a/docs/architecture/adr/0020-platform-administration-in-filament.md b/docs/architecture/adr/0020-platform-administration-in-filament.md index 1171b90..cdc0bbe 100644 --- a/docs/architecture/adr/0020-platform-administration-in-filament.md +++ b/docs/architecture/adr/0020-platform-administration-in-filament.md @@ -1,12 +1,38 @@ # ADR-0020: Build platform administration with Filament; build the vendor dashboard in Vue - **Number:** 0020 -- **Status:** Proposed +- **Status:** Accepted, by owner decision (2026-11-04) - **Date:** 2026-11-01 - **Decision owner:** Project owner --- +## Owner decision (2026-11-04) + +The owner accepted this ADR before Phase 2: + +- Filament is used for platform administration at `/admin`, for authorised platform staff only. +- The vendor dashboard stays Vue 3 + Inertia.js (ADR-0008). +- Filament uses the Phase 2 design tokens. +- The admin interface is English-only in v2. +- Filament resources and actions delegate business operations to Application use cases. They never mutate domain status or financial data directly. +- Laravel 13 compatibility is verified in Phase 3. The fallback in Decision §5 is retained. + +**Rationale** (the decision drivers below): + +- Filament delivers dense back-office tooling for a staff-only surface at low cost. +- Vendors keep a product-grade, localised Vue experience. +- Keeping vendors out of Filament avoids the panel-tenancy pattern that Phase 0 found unsafe (S1). + +**Dependency on a Proposed ADR.** This ADR relies on mechanisms defined by ADR-0011, which is still **Proposed**: + +- `platform_role`; +- the permission enum; +- `VendorContext`; +- the rule that staff cannot hold a vendor membership. + +Decision §1 and §6 name these mechanisms as the intended design. They are not accepted until ADR-0011 is. If ADR-0011 changes, §1 and §6 follow it, through a new ADR (ADR-0006). + ## Context - The brief (§6.3) asks to *evaluate and improve* Filament rather than assume replacement. @@ -44,6 +70,20 @@ Which technology serves the platform-administration and vendor surfaces? 3. Admin dashboards (GMV, orders, refunds, payables, outbox and reconciliation health) read from Finance and Platform read models. 4. Filament is themed with the Phase 2 design tokens (colours, typography) so it looks like part of the same product. 5. **Compatibility check:** Filament's support for Laravel 13 / PHP 8.5 is to be verified in Phase 3 before installation. If it is unsupported, this ADR is revisited. The fallback is Vue admin pages built from the same component kit. +6. **Surface separation.** The two back-office surfaces do not overlap: + + | | Platform administration | Vendor dashboard | + |---|---|---| + | Route | `/admin` | `/vendor` | + | Technology | Filament (Livewire) | Vue 3 + Inertia.js | + | Who | Users with `platform_role` (staff) | Vendor members, resolved through `VendorContext` (ADR-0011) | + | Languages | English only | `en`, `ar` (RTL), `ms` | + + - No Filament panel, resource or Filament tenancy is used for vendors. + - A vendor member without `platform_role` is denied at `/admin`. + - Under ADR-0011 (Proposed), staff cannot hold a vendor membership, so they never use `/vendor`. + - Both surfaces call the same Application use cases and policies. Business rules are not duplicated in either UI. +7. **Language.** The admin interface is English-only in v2. Arabic, Malay and right-to-left requirements apply to the storefront and the vendor dashboard. ## Positive consequences diff --git a/docs/architecture/security-model.md b/docs/architecture/security-model.md index ec0377f..6f869b5 100644 --- a/docs/architecture/security-model.md +++ b/docs/architecture/security-model.md @@ -77,7 +77,7 @@ A `pending` vendor can manage its profile, shipping and draft products, but cann | Layer | Mechanism | |---|---| -| Routing | Route groups: `storefront` (web), `account` (auth + verified), `vendor` (auth + verified + `vendor.context`), `admin` (Filament panel + 2FA), `webhooks` (signature middleware, CSRF-exempt) | +| Routing | Route groups: `storefront` (web), `checkout` (entry step public, offering sign-in or inline registration; the place-order and payment-attempt routes need auth + verified, and the use cases re-check this, P-1 / ADR-0015 §1), `account` (auth + verified), `vendor` (auth + verified + `vendor.context`), `admin` (Filament panel + 2FA), `webhooks` (signature middleware, CSRF-exempt) | | Binding | Vendor routes use scoped bindings: `{vendorOrder}` resolves `where vendor_id = context`. Customer routes resolve `where customer_id = auth id`. | | Policies | One policy per aggregate; `before()` never grants a blanket allow, not even for Admin (Admin is granted explicitly per permission) | | Use cases | Accept `Actor` (user, role, vendor context, impersonator) and re-check the permission. Use cases may be called from Filament, jobs or controllers, so the check lives in the use case as well as in the controller. | diff --git a/docs/discovery/PHASE-1-REVIEW.md b/docs/discovery/PHASE-1-REVIEW.md index 6dddac7..ebf28b2 100644 --- a/docs/discovery/PHASE-1-REVIEW.md +++ b/docs/discovery/PHASE-1-REVIEW.md @@ -156,3 +156,46 @@ These are the significant ones, each with its reason. The trade-offs and alterna - [ ] Authorise (or not) committing this branch; decide D-15. **Phase 1 stops here. Phase 2 (Brand Identity and Design System) has not been started.** + +--- + +## Addendum A: owner decisions for Phase 2 readiness (2026-11-04) + +The Phase 1 decision briefing (2026-11-03) labelled its spec inconsistencies C1–C13. **Those labels are unrelated to Phase 0's code findings C1–C6** (§7) (for example Phase 0 C1, "payments never marked paid"). Here they are written as "briefing C*n*". + +### A.1 Decisions recorded + +| Decision | Outcome | Where it is recorded | +|---|---|---| +| ADR-0020: Filament for platform administration | **Accepted.** `/admin` for authorised platform staff only; vendor dashboard stays Vue 3 + Inertia; Filament uses Phase 2 design tokens; the admin is English-only; Filament delegates to use cases and never mutates status or financial data directly; Laravel 13 compatibility is verified in Phase 3, with the fallback retained | ADR-0020 (status line, "Owner decision", Decision §6–§7); `architecture/README.md` ADR index | +| P-1: account-required checkout | **Approved.** Guest browsing and guest cart are allowed; placing an order requires a signed-in account; inline registration at checkout; verified email required before order submission and payment initiation; checkout resumes after verification | PRD §6.3 P-1, §6.1; FR-ID-01, FR-CART-02, FR-CHK-01; journey CJ-1; ADR-0015 §1; security-model §5 | + +### A.2 Briefing C7 (P-1 inline sign-up vs FR-ID-01 email verification): resolved + +- **The contradiction:** + - P-1 promised inline sign-up on the checkout page. + - FR-ID-01 said unverified users are "redirected to verification" before checkout. + - The flow for a new customer was undefined. +- **The resolution:** verification is a step inside checkout, not a detour out of it. + - Inline registration signs the customer in and merges the guest cart. + - Checkout then shows a verification prompt with a resend action. + - The existing signed, 60-minute link (FR-ID-01) marks the email verified and returns the customer to checkout with the same cart. + - Placement and payment-attempt requests from unverified accounts are rejected before any write, in the routes and in the use cases. + - Stock is reserved only at placement, so verifying never uses up a reservation. +- **What was not changed:** the verification mechanism itself (signed link, 60-minute expiry). A one-time-code alternative was considered and not adopted. + +### A.3 Consistency edits that follow from ADR-0020 (briefing C13) + +ADR-0020 makes the admin English-only. Three statements implied translation of every page, so they are now scoped to the storefront and vendor dashboard: + +- PRD §6.1 (i18n row); +- PRD §7 gate 7; +- NFR-I18N-01. + +NFR-A11Y-01 was already scoped that way. + +### A.4 Still open + +- Every other §6 item: ADR-0005, ADR-0009–0019, ADR-0021, D-3, D-8–D-11, P-2–P-8, D-12 follow-up, D-13 step 3, D-15, tooling, and the React reference branch. +- ADR-0020 relies on `platform_role` and the permission enum from ADR-0011, which is still Proposed. +- The Phase 2 readiness items not decided here: provisional order-state vocabulary (ADR-0012), the late-payment transitions (briefing C1), and the Arabic numeral policy (ADR-0018 §3). diff --git a/docs/product/functional-requirements.md b/docs/product/functional-requirements.md index 7479161..9ee63f5 100644 --- a/docs/product/functional-requirements.md +++ b/docs/product/functional-requirements.md @@ -16,7 +16,7 @@ | ID | P | Requirement | Acceptance criteria | |---|---|---|---| -| FR-ID-01 | M | Visitors can register with name, email and password, and must verify their email before checkout | Unverified users are redirected to verification when they reach checkout; the verification link is signed and expires in 60 minutes | +| FR-ID-01 | M | Visitors can register with name, email and password, on the registration page or inline during checkout (P-1). The email must be verified before an order can be placed. A signed-in, unverified customer can request a new verification email. | **Registration:** both entry points apply the same validation and password rules (security-model §6). Success creates a customer account, signs the customer in, merges the guest cart (FR-CART-02) and sends one verification email. Inline registration returns the customer to checkout, not to a separate page.
**Verification:** the link is signed and expires in 60 minutes. Following a valid link marks the email verified. Following an expired or invalid link changes nothing; the page explains why and offers a resend.
**Resend:** available to a signed-in, unverified customer from the checkout verification prompt and from the account area. It is rate-limited (NFR-SEC-04). An already-verified customer is not sent another email.
**Enforcement:** an unverified customer cannot submit an order or initiate a payment (FR-CHK-01). | | FR-ID-02 | M | Login, logout, password reset, with rate limiting | Login is throttled to 5 attempts per minute per email+IP; reset tokens are single-use and expire | | FR-ID-03 | M | Customers manage profile and address book (Malaysian address format, zone derived from state) | Address state determines the shipping zone; at most one default address | | FR-ID-04 | M | Platform staff accounts are created by an Admin, require 2FA, and cannot register publicly | A staff login without 2FA confirmation cannot reach `/admin` | @@ -78,7 +78,7 @@ | ID | P | Requirement | Acceptance criteria | |---|---|---|---| | FR-CART-01 | M | Visitors and customers add variants from any number of vendors to one cart, grouped by vendor in the UI | | -| FR-CART-02 | M | A guest cart merges into the customer cart at login (quantities summed, capped at available) | Merge is idempotent | +| FR-CART-02 | M | A guest cart merges into the customer cart at login, including the automatic sign-in after registration (quantities summed, capped at available) | Merge is idempotent; after inline registration at checkout, the merged cart is the one shown in checkout | | FR-CART-03 | M | The cart revalidates on view: price changes, unavailable items and quantity above available are flagged, and checkout is blocked until acknowledged | | | FR-CART-04 | M | Quantity per line is limited to 1–20 (configurable) and to available stock | | | FR-CART-05 | M | A cart is owned by exactly one session or customer; no endpoint accepts a cart id from the client | Test: another user's cart cannot be read or checked out | @@ -87,7 +87,7 @@ | ID | P | Requirement | Acceptance criteria | |---|---|---|---| -| FR-CHK-01 | M | Checkout requires a verified customer account (P-1) | | +| FR-CHK-01 | M | Placing an order requires a signed-in customer whose email is verified (P-1). Guests can start checkout only by signing in or registering inline. Unverified customers are prompted to verify, and resume checkout once verified. | **Guest:** starting checkout offers sign-in or inline registration. The guest cart is kept and merged on sign-in (FR-CART-02).
**Unverified customer:** checkout shows that verification is required, with a resend action (FR-ID-01), and the place-order action is unavailable.
**Server enforcement:** a place-order request (ADR-0015 step 1) or a payment-attempt request (step 2) from a guest or an unverified account is rejected before any write. No OrderGroup, reservation, promotion redemption, points hold or payment attempt is created, and nothing is charged. This holds even if the UI is bypassed (for example a stale tab or a direct request).
**Resume:** after following a valid verification link, the customer returns to checkout with the same cart. If the link is opened on another device, the email is still verified. The original device, still signed in, can then place the order with the same cart. Any device the customer signs in on shows the same cart, because the cart belongs to the account.
**Reservation:** no stock is reserved before placement, so time spent registering or verifying never uses up a reservation (FR-INV-03). | | FR-CHK-02 | M | The customer selects a shipping address; the system quotes shipping per vendor and shows a breakdown per vendor and in total | | | FR-CHK-03 | M | The customer may apply one coupon and redeem points; the system shows the allocation as a total discount | Invalid coupons give a reason code | | FR-CHK-04 | M | Placing the order re-prices server-side; if the total differs from the confirmed total, nothing is charged and the customer reviews again | 409 test | diff --git a/docs/product/non-functional-requirements.md b/docs/product/non-functional-requirements.md index 0ee48de..2e6d9d5 100644 --- a/docs/product/non-functional-requirements.md +++ b/docs/product/non-functional-requirements.md @@ -60,7 +60,7 @@ These requirements are measurable. Where a target is an assumption rather than a | ID | Requirement | |---|---| -| NFR-I18N-01 | en, ar and ms have 100 % key coverage for the UI; CI fails on missing keys | +| NFR-I18N-01 | en, ar and ms have 100 % key coverage for the storefront and vendor dashboard UI; CI fails on missing keys. The platform admin is English-only in v2 (ADR-0020). | | NFR-I18N-02 | RTL uses CSS logical properties and direction-aware components (icons, carousels, steppers, progress); no physical `left/right` utilities in components (lint rule) | | NFR-I18N-03 | Currency, number and date formatting is locale-aware through `Intl`; dates display in MYT | | NFR-I18N-04 | Locale in the storefront URL (`/ms/...`, `/ar/...`; `en` unprefixed) for shareable links; `hreflang` alternates | diff --git a/docs/product/product-requirements.md b/docs/product/product-requirements.md index 4e1e2d8..53fcc90 100644 --- a/docs/product/product-requirements.md +++ b/docs/product/product-requirements.md @@ -80,7 +80,7 @@ The full permission matrix is in `docs/architecture/security-model.md`. - shipping rates; - team; - notifications. -3. **Platform administration** (`/admin`, Filament). Covers: +3. **Platform administration** (`/admin`, Filament, English-only, platform staff only; ADR-0020, accepted). Covers: - vendor applications and vendors; - catalogue taxonomy and moderation; - orders and returns arbitration; @@ -99,7 +99,7 @@ The full permission matrix is in `docs/architecture/security-model.md`. | Area | Notes | |---|---| -| Identity | Registration, login, email verification, password reset, profile, address book; staff 2FA | +| Identity | Registration (on its own page or inline during checkout), login, email verification with resend, password reset, profile, address book; staff 2FA | | Vendor onboarding | Application → platform review → vendor created (pending) → approved; suspension and reactivation | | Vendor team | Invite members with roles; one vendor per user | | Catalogue | Platform-owned category tree and attribute definitions; vendor products with variants (option combinations), images, descriptions; product status (draft, active, inactive); platform moderation (block or unblock) | @@ -107,7 +107,7 @@ The full permission matrix is in `docs/architecture/security-model.md`. | Inventory | On-hand and reserved per variant; reservations with TTL; adjustments; movement history; low-stock indicator | | Shipping | Per-vendor flat rates by zone (Peninsular Malaysia, East Malaysia); per-vendor free-shipping threshold; manual carrier and tracking entry | | Cart | Guest and customer cart, multi-vendor, grouped by vendor; merged on login; stock and price revalidation | -| Checkout | Account required; address; per-vendor shipping; coupon; points redemption; single card payment (Stripe test mode, 3-D Secure) | +| Checkout | Signed-in account with a verified email required to place an order (P-1); inline registration; address; per-vendor shipping; coupon; points redemption; single card payment (Stripe test mode, 3-D Secure) | | Orders | OrderGroup + VendorOrders; customer timeline per vendor; vendor fulfilment states; cancellation rules | | Returns and refunds | Customer return requests per VendorOrder line; vendor decision; platform arbitration; partial and full refunds; refunds tracked per VendorOrder | | Disputes | Ingest provider disputes (test mode), hold vendor funds, record evidence, record the outcome | @@ -118,7 +118,7 @@ The full permission matrix is in `docs/architecture/security-model.md`. | Wishlist and store follow | Customer wishlist; follow vendor stores; followed-store feed on the account page | | Notifications | Database (in-app) and email for order, return, refund, payout and application events | | Search | Full-text search with facets: category, price, vendor, rating, in stock. MySQL-backed (ADR-0019) | -| i18n | `en` (primary), `ar` (RTL), `ms` for all UI. Platform taxonomy translated. Vendor-entered content is single-language. | +| i18n | `en` (primary), `ar` (RTL), `ms` for all storefront and vendor dashboard UI. The platform admin is English-only in v2 (ADR-0020). Platform taxonomy translated. Vendor-entered content is single-language. | | Demo | Reproducible seeded environment with role logins | ### 6.2 Out of scope for v2 (with reason) @@ -141,7 +141,7 @@ The full permission matrix is in `docs/architecture/security-model.md`. | ID | Proposal | Rationale | |---|---|---| -| P-1 | Checkout requires an account (inline sign-up on the checkout page); guest browsing and cart allowed | Removes guest-ownership bugs; simpler order access | +| P-1 | **Approved by the owner, 2026-11-04.** The rules:
(a) Visitors may browse and search without an account.
(b) Visitors may keep a guest cart. It merges into the customer's cart at sign-in, including the automatic sign-in that follows registration (FR-CART-02).
(c) Placing an order requires a signed-in customer account; there is no guest checkout.
(d) New customers can register inline on the checkout page, without leaving checkout.
(e) The account's email must be verified before the order is submitted and before any payment is initiated. Unverified customers cannot complete checkout.
(f) After verifying, the customer resumes checkout with the same cart.
See FR-ID-01, FR-CHK-01 and journey CJ-1. | Removes guest-ownership bugs and keeps order access simple. Verification happens before placement, and no stock is reserved before placement, so time spent verifying never uses up a reservation. | | P-2 | Vendor products publish immediately once the vendor is approved; platform moderation is reactive (block or unblock) | Pre-moderation slows demos; reactive moderation still demonstrates control | | P-3 | Return window of 7 days after delivery; auto-deliver 10 days after shipping when not confirmed | No carrier integration exists | | P-4 | Reservation TTL of 15 minutes, with one extension while payment is processing | Brief §11 | @@ -158,7 +158,7 @@ The full permission matrix is in `docs/architecture/security-model.md`. 4. A disputed payment holds the affected vendors' funds and resolves according to the dispute rules. 5. A weekly payout run produces simulated payouts that equal each vendor's available balance, and the ledger reconciles. 6. Every scenario in `lifecycles/` passes on MySQL in CI, including the parallel concurrency tests. -7. Every page works in `en`, `ar` (RTL) and `ms`; WCAG 2.2 AA checks pass on the key journeys. +7. Every storefront and vendor dashboard page works in `en`, `ar` (RTL) and `ms` (the admin is English-only, ADR-0020); WCAG 2.2 AA checks pass on the key journeys. 8. The demo rebuilds from an empty database with one command, and the seeded data passes the ledger invariants. ## 8. Assumptions and constraints @@ -170,6 +170,6 @@ The full permission matrix is in `docs/architecture/security-model.md`. ## 9. Open questions -- P-1 to P-8 above. +- P-2 to P-8 above. P-1 was approved on 2026-11-04. - D-9, D-10, D-11 in ADR-0016. - D-15: commit author identity before the repository is public. diff --git a/docs/product/user-journeys.md b/docs/product/user-journeys.md index 86cc2f9..806e3f1 100644 --- a/docs/product/user-journeys.md +++ b/docs/product/user-journeys.md @@ -16,7 +16,11 @@ Each journey lists its steps, the requirements it exercises, and the failure bra - price and stock update per variant; - the vendor card links to the store page. 3. Adds three items from three vendors. The cart groups lines by vendor and shows per-vendor subtotals and shipping estimates. -4. Proceeds to checkout and is asked to sign in or create an account. The guest cart is merged (FR-CART-02). +4. Proceeds to checkout. A visitor is asked to sign in or create an account inline (P-1). Signing in or registering merges the guest cart (FR-CART-02). The visitor then follows one of these paths: + - **4a. Existing account, verified email:** signs in and continues to step 5. + - **4b. Existing account, unverified email:** signs in. Checkout shows that the email must be verified before the order can be placed, with a resend action. Placing is unavailable until the email is verified. Continues at 4d. + - **4c. New customer:** registers inline (name, email, password) without leaving checkout, and is signed in. A verification email is sent, and checkout shows the same verification prompt as 4b. Continues at 4d. + - **4d. Return after verification:** the customer follows the link, which marks the email verified, and is returned to checkout with the same cart. If they open the link on another device, the email is still verified, and checkout on the original device (still signed in) continues with the same cart. Nothing was reserved yet, so no reservation time was used. Continues to step 5. 5. Selects an address. Shipping is quoted per vendor, then the customer applies a coupon and redeems points. The summary shows goods, shipping per vendor, discount, points and total. 6. Places the order: - the stock is reserved; @@ -25,12 +29,16 @@ Each journey lists its steps, the requirements it exercises, and the failure bra 7. Sees "Confirming your payment…", then the confirmation page: group reference and three vendor shipments. 8. Receives one confirmation email. Each vendor gets a new-order notification. -**Requirements:** FR-SRCH-01, FR-CAT-05, FR-CART-01…04, FR-CHK-01…10. +**Requirements:** FR-SRCH-01, FR-CAT-05, FR-ID-01, FR-ID-02, FR-CART-01…04, FR-CHK-01…10. **Failure branches:** | Branch | Expected behaviour | |---|---| +| Verification email not received | Resend from the checkout prompt (rate-limited); cart kept | +| Verification link expired (60 minutes) or invalid | Email stays unverified; the page explains why and offers a resend; cart kept | +| Unverified customer submits the order anyway (stale tab or direct request) | Rejected before any write; nothing reserved or charged; the verification prompt is shown | +| Registration fails validation (for example the email is already registered) | Errors shown inline; the customer can sign in instead; cart kept | | Price changed between cart and placement | 409; review the new total | | Line out of stock at placement | Order not placed; short lines named | | Card declined | Retry with another card within TTL |