# 7. Cancellation & Refunds

← Business Requirements

## (a) Business requirements #

- Cancellation/refund rules are tiered and structured, not a single blunt "default 100%". The platform defines `cancellation_policies` tiers by lead time and initiating actor:
  - Free cancellation more than 24h before start.
  - Partial refund (e.g., 50%) under 24h.
  - Customer no-show: up to 100% charge.
  - Nurse no-show: full refund to the customer and a penalty/forfeiture for the nurse.
- The applicable policy is snapshotted onto the booking at booking time (mirroring the per-booking fee-rate snapshot), so later policy edits never rewrite history. The resolved cancellation fee / refund percentage is recorded on the refund event.
- For multi-session engagements, cancellation is per remaining session: cancelling mid-engagement refunds only the un-started sessions, while completed-and-verified sessions remain payout-eligible.
- Refunds are admin-only — there is no customer self-service refund. A refund is initiated by an admin and must be linked to a support ticket (`tickets`) that holds the conversation and dispute evidence.
- A refund decomposes across the two fee legs — how much of the platform commission and how much of the nurse payout is being reversed — because the booking gross is `platform commission + nurse payout`.

## (b) Iran-specific considerations #

- A flat percentage is too blunt for شبانه‌روزی live-in engagements and Iranian holiday-period bookings; tiered, snapshotted policy reduces dispute load.
- The refund money path depends on whether the nurse has already been paid (Section 8/10): pre-payout it is a clean reversal; post-payout it becomes a platform-funded refund plus a nurse clawback, because an Iranian bank transfer to a nurse's IBAN is effectively irreversible.
- For BNPL bookings, the refund never goes nurse→customer or Balinyaar→customer directly — it is initiated through the BNPL provider's revert/cancel API (Section 8/9).

## (c) MVP vs DEFERRED #

- MVP: tiered `cancellation_policies`; per-booking policy snapshot; admin-only, ticket-linked refunds; per-session cancellation for engagements; nurse-no-show vs customer-no-show handling; fee-leg decomposition on refunds.
- DEFERRED: automated nurse no-show penalty (manual admin action at launch); self-service partial-refund UI; holiday-specific cancellation overrides.

## (d) Supporting database entities #

`cancellation_policies`, `bookings` (policy snapshot, `dispute_window_ends_at`), `refunds` (admin-only, `ticket_id`, fee-leg decomposition, `refund_channel`), `tickets`, `nurse_clawbacks` (post-payout case), `ledger_entries`.

## (e) As built (backend-phase-11) #

Decisions fixed while building the refund/clawback/invoice engine (config-driven where possible):

- Refund channel canonical code: the out-of-band bank refund is stored and served as `manual` (the wire

code), not `manual_bank` — they are the same channel. Full set: `psp_card` | `bnpl_revert` | `manual`.

- Ticket link is config-gated until b15. `refunds.ticket_id` is a nullable column with no FK yet

(`tickets` arrives in b15). "A refund must link a ticket" is enforced by the `refund_ticket_required` config flag, default `false` so admin refunds are testable today; b15 wires the FK and flips it on.

- BNPL refund ETA is `now + config(bnpl_refund_eta_business_days, default 10)` business days (Fridays skipped),

surfaced as `expected_customer_refund_eta`; the refund sits in `processing` until reconciled. Card refunds are immediate (`succeeded`, no ETA). Card and BNPL post the same reversal ledger legs.

- Pre-payout vs post-payout fork is decided by whether the nurse was already paid — derived from the booking's

`dispute_window_ends_at` close until b13 ships `nurse_payouts` (a `refund_assume_nurse_paid` override exists). Clawback recovery/netting is b13; b11 only opens the `pending` receivable + supports admin write-off.

> Related: Data model — Payments Ledger & Refunds.

↑ Back to top