product/prd.md

PRD — Salon Booking System

Sản phẩm: Salon Booking System Phiên bản: MVP 1.0 Ngày: 2026-04-06 (cập nhật mốc 2026-04-25) Dựa trên: Product Brief | Architecture


1. Tổng quan

Hệ thống Booking & quản lý salon mobile-first cho thị trường Na Uy. MVP tập trung beauty cluster (nail, salon, barbershop) với smartphone-as-POS approach.

Roles: Owner, Staff, Customer, Admin.

flowchart TD
    E1[Epic 1<br/>Tenant + Onboarding] --> E2[Epic 2<br/>Resource Management]
    E1 --> E3[Epic 3<br/>Service Catalog]
    E2 --> E4[Epic 4<br/>Booking Engine]
    E3 --> E4
    E4 --> E5[Epic 5<br/>Customer Management]
    E4 --> E6[Epic 6<br/>Payment]
    E4 --> E7[Epic 7<br/>Notifications]
    E5 --> E9[Epic 9<br/>Customer Portal]
    E6 --> E9
    E1 --> E8[Epic 8<br/>Admin Portal]
    E2 --> E10[Epic 10<br/>Loyalty]
    E5 --> E10

2. Epics & User Stories

Epic 1: Tenant & Onboarding ✅

Admin tạo và quản lý salon accounts.

US-1.1: Admin tạo tenant mới ✅

As Admin, I want to tạo salon account mới so that salon owner có thể bắt đầu sử dụng hệ thống.

Acceptance Criteria:

  • Tạo tenant với: name, slug (unique), industryType, settings
  • Slug dùng cho subdomain: {slug}.app.no
  • Default settings phù hợp với industryType
  • Owner account được tạo kèm tenant

API: POST /api/tenants

US-1.2: Owner customizes salon branding ✅

As Owner, I want to tùy chỉnh branding (logo, colors, cover) so that customer portal phản ánh thương hiệu salon.

Acceptance Criteria:

  • Upload logo, cover image
  • Chọn primary/secondary colors
  • Preview trước khi lưu
  • Branding áp dụng ngay trên customer portal

API: PATCH /api/tenants/:id (branding field)

US-1.3: Owner quản lý salon settings ✅

As Owner, I want to cấu hình salon settings so that hệ thống hoạt động đúng business model.

Acceptance Criteria:

  • Toggle: walkInEnabled, autoConfirm, posEnabled, depositEnabled
  • Đặt bookingMode: assigned_only | allow_unassigned
  • Cấu hình business hours (default schedule)
  • Cấu hình cancellation policy (cancellationHours)
  • maxBookingDaysInAdvance (default 30, hard cap 7 nếu deposit on — P1-11)

API: PATCH /api/tenants/:id (settings field)

US-1.4: Onboarding wizard 7-step ✅

7-step full-page flow (Welcome → Salon info → Business hours → Services → Staff → Booking policy → Review & Launch) với OnboardingGuard redirect OWNER chưa onboardedAt, stepper monotonic, deposit↔payment-config cross-check warning.


Epic 2: Resource Management (Staff) ✅

Owner quản lý staff — beauty layer map Staff vào Resource.

US-2.1: Owner thêm staff mới ✅

Acceptance Criteria:

  • Tạo resource (type=staff) với name, color, contact
  • Tạo staff kèm login account (email/phone + password + role OWNER|STAFF) — 1 transaction tạo User + Resource linked qua userId
  • Email/phone unique per-tenant (@@unique([email, tenantId]))

API: POST /api/resources

US-2.2: Owner quản lý staff skills ✅

  • Assign/remove services cho resource
  • Khi booking, chỉ hiển thị resource có skill phù hợp
  • Bulk assign cho nhiều resource cùng lúc

API: POST /api/resources/:id/skills, DELETE /api/resources/:id/skills/:serviceId

US-2.3: Owner quản lý staff schedule ✅

  • Set weekly recurring schedule (multi-slot per day, industry-standard)
  • Override cho ngày cụ thể (vacation, sick, special hours)
  • Time-off (multi-day, partial-day, multi-per-day)
  • Staff chỉ bookable trong schedule

API: POST /api/resources/:id/schedules, PATCH /api/resources/:id/schedules/:scheduleId, POST /api/resources/:id/time-offs

US-2.4: Staff xem lịch cá nhân (mobile) ⏳

  • View daily/weekly schedule trên mobile app
  • Hiển thị: customer name, service, time, status
  • Real-time update khi có booking mới
  • Offline mode: xem được schedule đã sync

API: GET /api/bookings?resourceId={id}&date={date} (đã sẵn sàng)


Epic 3: Service Catalog ✅

US-3.1: Owner CRUD services ✅

  • CRUD service: name, description, duration (minutes), price (øre — smallest unit)
  • Organize theo category
  • Set sort order
  • Toggle active/inactive

API: POST /api/services, PATCH /api/services/:id

US-3.2: Owner quản lý service categories ✅

  • CRUD categories: name, sortOrder
  • Categories hiển thị trên customer portal theo sortOrder

API: POST /api/service-categories, PATCH /api/service-categories/:id


Epic 4: Booking Engine ✅

Core booking flow — tạo, assign, manage bookings.

US-4.1: Customer đặt booking online ✅

  • Chọn service → xem available time slots
  • Chọn resource (staff) hoặc "bất kỳ ai" (unassigned)
  • Chọn date + time slot
  • Nhập thông tin: name, phone, email (optional), notes
  • Nhận confirmation (theo tenant settings: autoConfirm hoặc PENDING)
  • Guest booking (không cần tạo account) — contact snapshot trên Booking

API: POST /public/tenants/:slug/bookings

US-4.2: Available time slots ✅

  • Tính dựa trên: resource schedule + existing bookings + time-off + service duration
  • Exclude: conflicting bookings (status != CANCELLED, NO_SHOW)
  • Nếu unassigned: slot available khi ≥1 qualified resource rảnh
  • Configurable slot interval (15/30/60 min)
  • Lead-time cap qua maxBookingDaysInAdvance (P1-11 hard cap với deposit)

API: GET /api/availability?serviceId={id}&date={date}&resourceId={id}

US-4.3: Owner/Staff tạo booking thủ công ✅

  • Chọn service + resource + time
  • Chọn existing customer hoặc tạo mới
  • Source = PHONE hoặc ADMIN
  • Conflict detection: cảnh báo nếu resource bận
  • Phone booking → paymentMode: IN_PERSON (P1-3) → skip PSP

API: POST /api/bookings

US-4.4: Walk-in handling ✅

  • Quick-create: chọn service, auto assign staff hiện tại
  • startTime = now, endTime = now + duration
  • Status auto = IN_PROGRESS
  • Source = WALK_IN, paymentMode: IN_PERSON (P0-3)
  • Optional: nhập customer info

API: POST /api/bookings/walk-in

US-4.5: Unassigned booking & Staff self-pick ⏳

  • Danh sách bookings có resourceId=null
  • Staff chỉ thấy bookings có skill phù hợp (Phase 5 audit)
  • Staff tap "Nhận" → resourceId = staff's resource (mobile, pending scaffold)
  • Nếu 2 staff nhận cùng lúc → first-come-first-served (optimistic lock)
  • Owner nhận notification khi staff self-pick

API: POST /api/bookings/:id/self-pick

US-4.6: Booking status management ✅

  • Status flow: PENDING → CONFIRMED → ARRIVED → IN_PROGRESS → COMPLETED
  • Alternative: → CANCELLED từ mọi non-terminal state
  • Alternative: CONFIRMED/ARRIVED → NO_SHOW
  • Owner/Admin update mọi booking với force=true + audit reason (P0-1)
  • Staff scoped tới booking thuộc mình + unassigned (Phase 2 audit)
  • Mỗi status change ghi audit log

API: POST /api/bookings/:id/status/:status

US-4.7: Booking conflict detection ✅

  • Check overlap: startTime < existingEnd AND endTime > existingStart
  • Exclude: CANCELLED, NO_SHOW bookings
  • Return 409 Conflict nếu trùng
  • Cho phép override (allowDoubleBooking=true per tenant)

Epic 5: Customer Management ✅

US-5.1: Customer database ✅

  • Auto-create customer khi booking lần đầu (match by phone)
  • CRUD: name, phone, email, notes
  • View booking history per customer (cross-tenant qua customer portal)
  • Unique constraint: phone + tenantId, email + tenantId
  • Guest vs auth booking separation (guest = snapshot, không auto-merge)

API: GET /api/customers, POST /api/customers, PATCH /api/customers/:id

US-5.2: Customer check-in ✅

  • Booking status: CONFIRMED → ARRIVED khi customer arrive
  • Push notification tới owner (mobile pending)
  • Hiển thị trên dashboard real-time

API: POST /api/bookings/:id/status/ARRIVED

US-5.3: TenantCustomer per-tenant metrics ✅

  • visitCount, lastVisit, totalSpent auto-update khi BookingCompleted
  • Notes/tags STAFF sửa được; metrics chỉ OWNER+ADMIN

Epic 6: Payment 🚧 Bambora live, Loyalty L4 pending

DDD + Hexagonal + CQRS Payment Context. MVP = Bambora Classic (Norway SMB). Worldline Direct giữ trong providers/worldline-direct/ cho enterprise. Stripe / Vipps / Nets / Adyen — drop-in via PaymentProviderPort.

Xem chi tiết docs/architecture/payment-architecture.md + docs/flows/payment-fundamentals.md + 20 scenarios trong docs/flows/payment-flow.md.

US-6.1: Tenant config payment provider ✅

  • Bambora Classic adapter live (4 fields: merchantNumber + accessToken + secretToken + md5Key)
  • T/P prefix derive test/prod mode
  • Health-check trên Verify/Activate
  • Rotate credentials với tx-safe

US-6.2: Customer trả deposit khi book ✅

  • depositEnabled setting (percent/fixed)
  • Manual capture: authorize on book → capture trên BookingArrived (primary) hoặc BookingCompleted (fallback)
  • Auto cancel nếu PaymentExpired (P1-11) hoặc retry exhausted (P1-4)

US-6.3: Admin refund / void / capture ✅

  • Admin Payment list + detail drawer (Track D2)
  • Refund/Void/Capture dialog với two-step confirm
  • Payment lifecycle SMS tới customer (refund/void/partial — P1-10)

US-6.4: Customer retry sau PaymentFailed ✅

  • P1-4: TRANSIENT vs PERMANENT classification
  • 3-attempt cap với PAYMENT_RETRY_CAP
  • Customer-authed retry endpoint + email/SMS nudge với deep-link

US-6.5: Remaining payment QR ✅

  • Track E1: Admin "Krev resterende" QR modal — customer scan + Bambora checkout

US-6.6: Loyalty discount applied to booking 🚧

  • L1-L3 shipped (schema, helper, BookingService.create wiring)
  • L4 lifecycle listeners (CONSUMED/CANCELLED on Booking lifecycle)
  • L5 public API + customer UI
  • L6 admin UX + E2E

Epic 7: Notifications 🚧 SMS only

US-7.1: Booking confirmation notification ⏳

  • SMS gửi khi booking status = CONFIRMED
  • Template Norwegian: "Hei {name}! Din time hos {salon} er bekreftet for {date} kl {time}"
  • Include: service name, staff name (nếu assigned), salon address

US-7.2: Payment lifecycle notifications ✅

  • Refund / partial refund / void SMS (P1-10) với template Norwegian
  • Failed retry email + SMS với deep-link (P1-4)
  • EmailProvider port + LogEmailProvider default — production SMTP defer (p1-4-smtp-vendor)

US-7.3: Owner/Staff push notifications ⏳

  • New booking → notify owner + assigned staff (mobile pending)
  • Unassigned booking → notify all active staff
  • Walk-in check-in → notify owner

Epic 8: Admin Portal (Web) ⏳ Pending

Super-admin quản lý toàn hệ thống.

US-8.1: Admin dashboard ⏳

  • List tenants: name, industry, active status, booking count
  • Search/filter tenants
  • Quick stats

US-8.2: Admin support access ⏳

  • Login as tenant (impersonate) — với audit log
  • View/edit tenant settings
  • Mandatory audit trail cho mọi admin action

Epic 9: Customer Portal (Web) ✅

Subdomain booking page cho mỗi salon.

US-9.1: Salon booking page ✅

  • Hiển thị salon branding (logo, colors, cover)
  • Service catalog (industry-standard category tabs)
  • Opening hours timezone-aware, location map
  • Single-page Calendly-style booking flow (multi-service)
  • SEO optimized (SSR)
  • 13/13 Playwright E2E green

US-9.2: Customer auth + portal ✅

  • Google OAuth + customer JWT (separate cookies)
  • /account: profile edit, booking history, loyalty, URL-based tabs
  • My Bookings server-side pagination
  • Customer self-cancel (P1-1) với refund preview (P1-8) + out-of-window dialog (P1-2)
  • Retry payment CTA (P1-4) với email deep-link

Epic 10: Loyalty 🚧

US-10.1: Stamp + Points cards ✅

  • Admin CRUD card (visit-based + points-based)
  • Auto-stamp on completed booking
  • Auto-earn points on completed booking
  • Customer dashboard hiển thị stamps/points

US-10.2: Reward redemption 🚧

  • Admin redeem stamp → reward (FREE_SERVICE / DISCOUNT_AMOUNT / DISCOUNT_PERCENT)
  • L1-L3: discount apply vào booking.total + Payment.amount
  • L4: lifecycle listeners
  • L5: customer self-redeem qua public API + UI

3. Non-functional Requirements

Performance

  • API response time: < 200ms (p95)
  • Customer portal load time: < 2s (LCP)
  • Support 100 concurrent bookings per tenant

Security

  • JWT authentication + refresh tokens (token versioning trên User + Customer)
  • Tenant isolation: mọi query filter by tenantId (Prisma scope)
  • RLS (Row Level Security) ở PostgreSQL — deferred Phase 8+
  • Rate limiting: 100 req/min per IP (10 req/min cho login)
  • Helmet + sameSite strict + HttpOnly cookies
  • OWASP Top 10 compliance
  • Payment credentials AES-256-GCM at rest, key version rotation

Reliability

  • 99.9% uptime target
  • Offline mode cho mobile app (WatermelonDB sync — pending scaffold)
  • Outbox publisher với retry + dead-letter
  • Webhook inbox với idempotency

Localization

  • Primary: Norwegian Bokmål (nb-NO)
  • UI text feel native, không phải bản dịch
  • Date/time format: dd.MM.yyyy, HH:mm
  • Currency: NOK, display với kr
  • Phone: Norwegian format (+47)
  • Display snapshot trên booking để bảo vệ historic bookings khỏi tenant đổi timezone/currency

Audit

  • Mọi mutation ghi audit log: who, what, when, before/after JSON
  • Admin actions có mandatory audit trail
  • Force override (P0-1) yêu cầu reason
  • SYSTEM transitions ghi reason vào audit (e.g. AUTHORIZATION_EXPIRED, PAYMENT_RETRY_EXHAUSTED)
  • Retention: 12 months minimum

4. Data Model Reference

Xem Architecture cho full schema. Payment Architecture cho DDD payment domain.

Core entities: Tenant, User, Resource, ResourceSkill, ResourceSchedule, ResourceTimeOff, Service, ServiceCategory, Booking, BookingItem, BookingAuditLog, Customer, TenantCustomer, LoyaltyCard, LoyaltyStamp, LoyaltyPointTransaction, LoyaltyRedemption, Payment, PaymentEvent, PaymentConfig, PaymentWebhookInbox, DomainEventOutbox.

Key relationships:

  • Tenant → has many Resources, Services, Bookings, Customers, TenantCustomers
  • Resource → has many Skills, Schedules, TimeOffs
  • Booking → belongs to Tenant, Customer (optional), Resource (optional = unassigned)
  • Booking → has many BookingItems (multi-service)
  • Booking ↔ Payment via events (BookingCreated → Payment.initiate; PaymentAuthorized → Booking.confirm)

5. API Overview

Xem api-design.md cho conventions, response envelope, error codes.

Base URL: {api-domain}/api Auth: Bearer JWT (admin) hoặc @CustomerAuth() (customer)

Endpoint Methods Mô tả
/tenants GET, POST List/create tenants (admin)
/tenants/:id GET, PATCH Get/update tenant
/public/tenants/:slug GET Resolve tenant by subdomain (@Public())
/resources GET, POST List/create resources
/resources/:id/skills POST, DELETE Manage resource skills
/resources/:id/schedules GET, POST, PATCH Manage schedules
/resources/:id/time-offs GET, POST, PATCH, DELETE Manage time-offs
/services GET, POST List/create services
/service-categories GET, POST List/create categories
/bookings GET, POST, PATCH Manage bookings
/bookings/:id/status/:status POST Update booking status (force/reason)
/bookings/:id/cancel-preview GET Cancel refund preview (P1-8)
/bookings/:id/audit-log GET Audit history
/availability GET Available time slots
/customers GET, POST, PATCH Manage customers
/admin/payments GET List + filter payments (Track D2)
/admin/payments/:id GET Payment detail
/admin/payments/:id/{refund,void,capture} POST Admin payment ops
/admin/payments/remaining POST Initiate remaining QR (Track E1)
/admin/payment-configs GET, POST, PATCH Provider config CRUD + rotate + activate + health-check
/api/webhooks/payments/:provider/:tenantId GET, POST Provider webhooks
/public/tenants/:slug/bookings POST Guest hoặc auth booking
/public/tenants/:slug/bookings/:id/cancel POST Customer self-cancel (P1-1)
/public/tenants/:slug/bookings/:id/payment/retry POST Customer retry (P1-4)
/auth/login, /auth/register, /auth/refresh POST Admin auth
/auth/customer/google POST Customer Google OAuth

6. MVP Milestones (cập nhật 2026-04-25)

Phase 1: Foundation ✅

  • Project setup (repos, Docker, NestJS scaffold)
  • Prisma schema & core modules
  • Docker Compose running
  • Database migration
  • Auth module (JWT + token versioning)

Phase 2: Core API ✅

  • Availability engine
  • Booking conflict detection
  • Resource schedule + time-off management
  • Resource skills management
  • Customer auto-create on booking
  • TenantCustomer bridge

Phase 3: Customer Portal (Web) ✅

  • Next.js setup + OpenAPI codegen
  • Subdomain routing
  • Booking flow UI (single-page, multi-service)
  • Salon branding
  • 13/13 Playwright E2E

Phase 4: Payment + Loyalty 🚧

  • Bambora Classic adapter (Worldline Direct fallback)
  • DDD + Hexagonal Payment Context
  • Outbox publisher + Webhook inbox
  • Status-matrix P0/P1 series shipped
  • Loyalty L4-L6
  • Reconciliation cron (P2-13)

Phase 5: Notifications & Real-time ⏳

  • Payment lifecycle SMS (P1-10) + email retry (P1-4)
  • Booking confirmation SMS
  • WebSocket live booking updates
  • Push notifications (Expo)
  • Production SMTP vendor (defer p1-4-smtp-vendor)

Phase 6: Mobile App ⏳

  • Role audit Phase 1-5 done — mobile blocker cleared
  • Expo setup + OpenAPI codegen
  • Owner screens (dashboard, schedule, staff management)
  • Staff screens (schedule, self-pick, check-in, payment)
  • Offline sync (WatermelonDB)

Phase 7: Admin Portal ⏳

  • Admin dashboard (cross-tenant)
  • Tenant management
  • Support access + audit + impersonate flow