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+Resourcelinked quauserId - 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:
autoConfirmhoặ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 =
PHONEhoặcADMIN - 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:
→ CANCELLEDtừ 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=trueper 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 → ARRIVEDkhi 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,totalSpentauto-update khiBookingCompleted - 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 viaPaymentProviderPort.
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 ✅
-
depositEnabledsetting (percent/fixed) - Manual capture: authorize on book → capture trên
BookingArrived(primary) hoặcBookingCompleted(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)
-
EmailProviderport +LogEmailProviderdefault — 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