Skip to content

SRS — Booking

Module owner: Domain 5 — Booking. A Booking is the binding agreement created the moment a customer accepts a quote: it fixes the vendor, service scope, price and event date, collects the deposit through Payments, and — once the event has happened — unlocks the Review.

Related: Quotations · Events & Planner · Payments · Reviews

Purpose

Convert an accepted quote into a tracked commitment with clear money rules (deposit, cancellation windows, refunds) so both sides know exactly what is owed, when, and what happens if plans change.

Actors

Actor Role
Customer Pays deposit, requests cancellation/refund, confirms completion
Vendor Confirms booking, sets deposit % (within platform bounds), requests cancellation (exceptional), confirms delivery
Admin (finance/ops) Approves refunds, resolves disputes, force-transitions in edge cases (logged)
System Creates booking on quote acceptance, enforces payment deadline, triggers commission capture + vendor settlement on confirmation, runs the completion job

User stories

  • As a customer, I want my accepted quote to turn into a booking with a clear deposit amount and deadline, so that I know exactly what to pay to lock the date.
  • As a customer, I want to cancel within the allowed window and get my deposit back automatically, so that changing plans isn't a fight.
  • As a vendor, I want the booking confirmed only after the deposit is paid, so that I never block a date for free.
  • As a vendor, I want completed bookings to prompt the customer for a review, so that my profile grows with every job.
  • As an admin, I want every out-of-policy refund to require my approval, so that money movements are controlled.

Functional requirements

Creation & confirmation — MVP

ID Requirement Phase
SRS-BKG-001 Accepting a quote creates a Booking atomically (same transaction as SRS-RFQ-031) in state pending, copying an immutable snapshot: vendor, event, category, quote version, line items, total_amount (KWD 3 dp), service_date (= event date unless the quote specified another delivery date). MVP
SRS-BKG-002 Deposit rules: deposit percentage is set by the vendor per listing/quote within platform bounds 10–50% (default 20%). deposit_amount = round(total_amount × deposit_pct, 3) KWD, minimum 5.000 KWD. The deposit is collected on-platform via Payments, and the platform's commission is captured from it at confirmation (SRS-BKG-007). The remainder (balance_amount) is settled directly between customer and vendor off-platform in MVP (recorded as a note on the booking); on-platform balance collection arrives with automated escrow (Vision). MVP
SRS-BKG-003 On creation the customer gets a payment call-to-action with a deposit deadline of 48 hours (configurable per platform). Unpaid past deadline → booking auto-cancels (cancelled, reason deposit_timeout), the quote acceptance is voided, and the RFQ reopens to quoted if other live quotes remain. MVP
SRS-BKG-004 Successful deposit payment (webhook-confirmed, see SRS-PAY-030) transitions the booking to awaiting vendor confirmation; the vendor must confirm within 24 hours. Vendor confirmation → confirmed; both parties notified, event dashboard updated. MVP
SRS-BKG-005 If the vendor fails to confirm within 24 h or declines: booking → cancelled (reason vendor_no_confirm / vendor_declined), deposit auto-refunded in full without admin approval (system-initiated refund, see SRS-PAY-041), and the vendor's response-rate metric is penalized. MVP
SRS-BKG-006 Instant-booking listings (vendor opt-in flag) skip the manual confirmation step: deposit paid → confirmed immediately. MVP
SRS-BKG-007 Commission & settlement on confirmation: when the booking becomes confirmed, the platform captures its commission (5% of total_amount, configurable — see Confirmed Decisions) from the already-paid deposit and schedules the vendor's share (deposit_amount − commission) for settlement via Payments (SRS-PAY-053/054). The deposit always exceeds the commission, so the customer pays nothing extra; the remaining balance is settled directly with the vendor at the event (SRS-BKG-002). Settlement of a disputed booking stays frozen until Operations resolves it. MVP

Cancellation & refunds — MVP

ID Requirement Phase
SRS-BKG-010 Platform cancellation policy (applied to the deposit; customer-initiated, relative to service_date): ≥ 30 days → 100% refund; 14–29 days → 50% refund; < 14 days → no refund. Vendors may only offer more generous terms (per listing setting); never stricter. The applicable policy is shown before deposit payment and frozen into the booking snapshot. MVP
SRS-BKG-011 Customer cancellation inside a 100%-refund window executes automatically: booking → cancelled, refund auto-issued. Any cancellation with a partial/zero refund, or any dispute ("vendor didn't show"), moves the booking to refund_requested and enters the admin refund-approval queue (SRS-PAY-040). MVP
SRS-BKG-012 Vendor-initiated cancellation of a confirmed booking always refunds the deposit 100%, notifies the customer with rebooking suggestions (same category, same date availability), flags the vendor account (3 vendor cancellations in 90 days → automatic admin review of the account). MVP
SRS-BKG-013 refund_requested resolves to cancelled (refund approved, full or partial per admin decision) or back to confirmed (refund denied). Every resolution requires a reason and is written to the audit log. MVP

Completion — MVP

ID Requirement Phase
SRS-BKG-015 Completion trigger: nightly job finds confirmed bookings with service_date passed, then asks the customer "Did {vendor} deliver?" (push + email). Customer confirms → completed. No customer response within 7 days and no dispute opened → auto-completed. Customer answers "there was a problem" → guided flow: open dispute (→ refund_requested) or contact vendor. MVP
SRS-BKG-016 A booking still pending (deposit unpaid) when service_date passes auto-cancels. A booking awaiting vendor confirmation when the date passes auto-cancels with full refund. MVP
SRS-BKG-017 On completed: the review window opens for the customer (SRS-REV-001), vendor completed-jobs counter increments, and the event dashboard reflects it. MVP

Vision

ID Requirement Phase
SRS-BKG-020 E-contracts: platform-generated bilingual service agreement attached to the booking, e-signed by both parties. Vision
SRS-BKG-021 Escrow: full amount held by platform, released to vendor on completion. Vision
SRS-BKG-022 Milestone payments: schedule of partial payments tied to timeline milestones (e.g. 20% book, 30% one week out, 50% after delivery). Vision

Business rules

  1. One booking per accepted quote; one accepted quote per RFQ. A customer may hold multiple bookings on one event (different categories/vendors).
  2. The booking snapshot is immutable — later edits to the vendor listing, quote or event never change total_amount, line items or the frozen cancellation policy. Date changes require a mutual-consent reschedule flow: customer proposes, vendor accepts, service_date updates with full history retained.
  3. Money only moves through Payments; the booking module never talks to the PSP directly.
  4. All amounts KWD, 3 decimal places, half-up rounding at the final step only.
  5. Refund deadlines compute against Asia/Kuwait midnight of service_date.
  6. Admin force-transitions are restricted to super admin + operations roles, always require a reason, and always hit the audit log.
  7. Platform commission (5% of total_amount, configurable) is captured from the deposit at confirmation and is never charged on top of it; the vendor's settled share is deposit_amount − commission, executed by Payments (SRS-PAY-053/054). Automated escrow and milestone releases remain Vision (SRS-BKG-021/022).

Validation rules

Field Rule
deposit_pct integer 10–50 (platform bounds, admin-configurable)
deposit_amount decimal(12,3) KWD, ≥ 5.000, = round(total × pct, 3)
service_date ≥ today at creation; reschedule target ≥ today + 1 day
Cancellation reason required on any cancellation; enum + optional free text ≤ 500 chars
Dispute description required to open a dispute, 20–2,000 chars, up to 5 photo/PDF attachments (≤ 10 MB each)
Vendor confirmation only from the vendor account owning the quote; only in the awaiting-confirmation window

Permissions matrix

Action Customer (owner) Vendor (party) Other users Admin ops Admin finance Admin super
View booking
Pay deposit
Confirm / decline booking
Cancel (policy applied) ✔ (full-refund rule)
Open dispute
Approve/deny refund
Force state transition ✔ (logged) ✔ (logged)
Propose / accept reschedule ✔ propose ✔ accept

State machine

stateDiagram-v2
    [*] --> Pending : quote accepted (booking created)
    Pending --> Pending : deposit paid → awaiting vendor confirmation (substate)
    Pending --> Confirmed : vendor confirms (or instant booking) after deposit paid
    Pending --> Cancelled : deposit timeout 48h / vendor declines or no confirm 24h (full refund) / customer aborts
    Confirmed --> Completed : service date passed + customer confirms or 7-day auto
    Confirmed --> RefundRequested : customer cancels (partial/zero window) or opens dispute
    Confirmed --> Cancelled : customer cancels in 100% window (auto refund) / vendor cancels (full refund)
    RefundRequested --> Cancelled : admin approves refund (full or partial)
    RefundRequested --> Confirmed : admin denies refund
    Completed --> [*] : review window opens
    Cancelled --> [*]

Substates of Pending tracked via payment_status (unpaiddeposit_paid) and vendor_confirmation (awaitingconfirmed/declined) — kept as columns, not extra top-level states, so client status chips stay simple.

Edge cases

  • Deposit webhook arrives after the 48 h auto-cancel already ran: payment is auto-refunded in full (system refund, no approval), customer notified with "booking expired — you have not been charged" messaging.
  • Vendor confirms at the same moment the 24 h no-confirm job fires: transitions are guarded by row lock + state precondition; whichever commits first wins, the loser is a no-op.
  • Customer opens a dispute on a booking already auto-completed (e.g. day 8): allowed up to 14 days after service_date; booking stays completed but a dispute case is created for admin (refund can still be issued from the payment record without reversing the state).
  • Reschedule proposed but vendor never responds: proposal expires after 72 h; original date stands; customer notified with cancel-policy reminder.
  • Vendor account suspended while bookings are confirmed: bookings remain valid; admin ops decides per case (fulfil or cancel-with-full-refund); new bookings blocked.
  • Partial refund math: 50% window refunds round half-up to 3 dp (e.g. deposit 33.333 → refund 16.667 KWD).

Empty / loading / error states

Surface Empty Loading Error
Customer bookings list "No bookings yet — accepted quotes appear here" + link to open RFQs Skeleton rows Retry banner with cached data
Vendor booking inbox "New bookings land here the moment a customer accepts your quote" Skeleton rows Retry banner
Deposit payment return screen "Confirming your payment…" poll state (webhook may lag) up to 60 s If unconfirmed after 60 s: "We're verifying — you'll get a notification. You have not been double-charged."
Cancellation flow Policy computation inline Server recomputes on submit; mismatch → refreshed policy shown for re-confirm

Acceptance criteria

  • Quote acceptance creates exactly one booking with correct snapshot fields and deposit math (property-based test across random totals/percentages, KWD 3 dp).
  • Unpaid booking auto-cancels at 48 h and its RFQ reopens when other live quotes exist.
  • Vendor no-confirm at 24 h refunds the full deposit with no human intervention, verified against the PSP sandbox.
  • Cancellation at service_date − 31 days, − 20 days, − 5 days yields 100% / 50% / 0% refund paths respectively; the two latter route through the admin queue unless 100%.
  • Completion job transitions eligible bookings and opens review windows; re-running the job is idempotent.
  • Every admin force-transition and refund decision appears in the audit log with actor, reason, before/after state.
  • All booking screens correct in Arabic RTL, including the state chips and policy table.

Data entities touched

bookings, booking_line_items (snapshot), booking_status_history, booking_cancellation_policies (frozen snapshot), booking_reschedules, disputes, plus reads/writes on quotes, quotation_requests, events, payments, refunds, reviews (window open), vendor_metrics (response rate, completed jobs, cancellation flags); confirmation emits domain events that create commissions/vendor_settlements in Payments.

GET    /api/v1/bookings                       # role-scoped list (customer or vendor)
GET    /api/v1/bookings/{id}
POST   /api/v1/bookings/{id}/deposit-intent   # hands off to Payments
POST   /api/v1/bookings/{id}/confirm          # vendor
POST   /api/v1/bookings/{id}/decline          # vendor
POST   /api/v1/bookings/{id}/cancel           # either party, policy applied
POST   /api/v1/bookings/{id}/complete         # customer confirms delivery
POST   /api/v1/bookings/{id}/dispute
POST   /api/v1/bookings/{id}/reschedule       # propose
POST   /api/v1/bookings/{id}/reschedule/{proposalId}/accept
GET    /api/v1/bookings/{id}/policy           # frozen cancellation policy + live refund preview
# admin
GET    /api/v1/admin/bookings
POST   /api/v1/admin/bookings/{id}/transition # force, reason required