flows/status-matrix/README.md

Booking × Payment × Loyalty Status Matrix

Purpose: bức tranh toàn cảnh mọi transition + side effect cho Booking, Payment, Loyalty, ứng với mọi performer (CUSTOMER / STAFF / OWNER / ADMIN / SYSTEM). Dùng để rà soát gap giữa code và spec, track progress hoàn thiện.

Scope: tài liệu này mở rộng (không thay thế) bộ docs flows hiện có:

How to read

Mỗi file ghi theo cùng format:

  1. Transition / scenario — state machine mô tả
  2. Performer matrix — ai được phép, ai bị block
  3. Guards — pre-conditions (code-level + business-level)
  4. Events emitted — domain event publish vào outbox
  5. Side effects — payment, loyalty, notification, stats
  6. Real-world cases — scenario ở salon thật
  7. Implementation status — checkbox [x] = shipped + test, [~] = partial / gap, [ ] = chưa
  8. Known issues — link sang gaps-and-plan.md

File index

# File Covers
0 README.md Index + state machine overview
1 01-create.md Booking creation (admin / public / walk-in) + initial status + Payment init + Loyalty reserve
2 02-happy-path.md PENDING → CONFIRMED → ARRIVED → IN_PROGRESS → COMPLETED
3 03-cancel.md → CANCELLED từ mọi state × payment × performer
4 04-no-show.md → NO_SHOW + payment forfeit
5 05-payment-driven.md Payment events → Booking transitions (reverse direction) + depositStatus updates
6 06-loyalty-coupling.md Loyalty redemption lifecycle gắn với booking status
7 07-edge-cases.md Races, stuck states, webhook ordering, recovery
8 gaps-and-plan.md Consolidated issue checklist + P0/P1/P2 roadmap

Status legend

  • [x] — Shipped + tested (unit + e2e).
  • [~] — Partial: code chạy nhưng thiếu test / guard / feature flag / audit.
  • [ ] — Chưa implement hoặc docs nói một đằng code một nẻo.
  • [!]Gap cảnh báo: hành vi thực tế khác spec, có thể gây production incident.

Booking status machine (overview)

                     ┌──────────┐
  create (autoConfirm=false) ─▶ │ PENDING  │
                     └────┬─────┘
                          │ ┌──────── PaymentAuthorized (event-driven) ────────┐
                          │ │                                                  │
                          ▼ ▼                                                  │
                     ┌───────────┐                                             │
                     │ CONFIRMED │ ◀── create (autoConfirm=true)               │
                     └─┬────┬────┘                                             │
              arrive  │    │ start (skip ARRIVED)                              │
                      ▼    │                                                   │
                 ┌─────────┐│                                                  │
                 │ ARRIVED ││                                                  │
                 └────┬────┘│                                                  │
                  start│    │                                                  │
                       ▼    ▼                                                  │
                 ┌─────────────┐                                               │
                 │ IN_PROGRESS │ ◀── walk-in (new booking starts here)         │
                 └──────┬──────┘                                               │
                        │ complete                                             │
                        ▼                                                      │
                 ┌───────────┐                                                 │
                 │ COMPLETED │                                                 │
                 └───────────┘                                                 │
                                                                               │
   * ─── cancel (PENDING/CONFIRMED/ARRIVED) ──────▶  CANCELLED                 │
   *                                                                           │
   * ─── mark no-show (CONFIRMED/ARRIVED) ────────▶  NO_SHOW                   │
   *                                                                           │
   * ─── PaymentFailed / PaymentExpired (PENDING only) ─────▶ CANCELLED ───────┘

Source of truth: booking-api/src/core/booking/booking-status.constants.ts + booking-web/src/lib/booking-status.constants.ts. 2 file phải luôn sync.


Payment status machine (overview)

              ┌───────────┐
initiate() ─▶ │ INITIATED │
              └───┬───────┘
                  │   ├──── authorize() (MANUAL + webhook) ──▶ AUTHORIZED
                  │   ├──── capture()   (AUTO    + webhook) ──▶ CAPTURED
                  │   ├──── markFailed()                    ──▶ FAILED
                  │   └──── markExpired() (customer abandon)──▶ EXPIRED
                  │
              AUTHORIZED ──┬── capture(amount)   ───▶ CAPTURED
                           ├── void()            ───▶ VOIDED
                           ├── markExpired (7d)  ───▶ EXPIRED
                           └── markFailed (cap err)▶ FAILED
                  │
              CAPTURED ────┬── refund(partial) ──▶ PARTIALLY_REFUNDED
                           └── refund(full)    ──▶ REFUNDED
                  │
              PARTIALLY_REFUNDED ── refund(rest) ──▶ REFUNDED

Terminal: CAPTURED (nếu không refund), REFUNDED, PARTIALLY_REFUNDED (nếu stop), VOIDED, FAILED, EXPIRED.

Source of truth: booking-api/src/core/payment/domain/enums.ts + booking-api/src/core/payment/domain/policies/cancellation-refund-policy.ts.


Coupling diagram

         Booking Context                  Payment Context                Loyalty Context
         ────────────────                  ───────────────                ───────────────
            │                                  │                             │
  create ──▶│─ BookingCreated ────────────────▶│─ initiate() → INITIATED     │
            │                                  │                             │─ reserveInTx (if redemption)
            │                                  │                             │
            │◀─ PaymentAuthorized ─────────────│                             │
  PENDING   │                                  │                             │
  → CONF    │                                  │                             │
            │◀─ PaymentFailed/Expired ─────────│                             │
  PENDING   │                                  │                             │
  → CANCEL  │                                  │                             │
            │                                  │                             │
  arrive ──▶│─ BookingArrived ────────────────▶│─ capture() (MANUAL+AUTH)    │
            │                                  │                             │
  complete▶ │─ BookingCompleted ──────────────▶│─ capture fallback           │─ RESERVED → CONSUMED
            │                                  │                             │─ auto-earn stamps/points
            │                                  │                             │
  cancel ──▶│─ BookingCancelled ──────────────▶│─ decide VOID/REFUND/FORFEIT │─ RESERVED → CANCELLED (pre-cap)
            │                                  │                             │   + CLAWBACK for points
            │                                  │                             │
  no-show▶  │─ BookingMarkedNoShow ───────────▶│─ capture (AUTH = forfeit)   │─ RESERVED → CANCELLED
            │                                  │                             │
            │                                  │─ depositStatus reactions ◀──│ (Booking listens back)

Cross-context invariants

  1. Booking KHÔNG direct call Payment/Loyalty service. Tất cả đều qua DomainEventOutbox + EventBus.
  2. Loyalty reserve = intra-booking-tx. Booking + LoyaltyRedemption + Outbox commit atomic.
  3. Payment init = async post-commit. Booking commit trước, Payment listener fire sau qua OutboxPublisher.
  4. depositStatus trên Booking = read-only projection của Payment state. Không drive business logic.
  5. Role SYSTEM = event-driven transitions (listener auto-confirm / auto-cancel). Bypass customer-protection guards nhưng không bypass state machine validity.
  6. Role OWNER/ADMIN = admin override. Bypass state machine (isAdminTransition) nhưng hiện chưa bypass cancellation window — xem gaps-and-plan.md.

Maintenance

  • Sửa code status transition → update file tương ứng + re-tick checkbox.
  • Thêm event mới → update README.md coupling diagram + file liên quan.
  • Khi đóng 1 P0/P1 issue → đổi [!]/[~] thành [x] trong file gốc + mark done trong gaps-and-plan.md.