# Domain: After-sales and Reviews (post-order lifecycle) ## Aftersales (`modules/aftersale/`) Per-order-item refund applications; the order's `refund_total_minor` is the authoritative sum of completed refunds. ``` refund_only: pending ──approve──► approved ──refund──► refunded return_refund: pending ──approve──► approved ──buyer tracking──► buyer_shipping ──confirm receipt──► merchant_confirmed ──refund──► refunded pending ──reject──► rejected ──reopen (once)──► pending any non-terminal ──buyer cancel──► cancelled platform arbitration: pending ──► refunded | rejected (terminal) ``` Invariants: - One active (non-terminal) aftersale per order item — partial unique index. - Amount ≤ line paid − already refunded, checked at apply time. - Refund completion is one transaction: guarded status flip + guarded order `refund_total_minor` increment (`≤ total_minor`) + one ledger credit to the customer's available balance in the **order currency** (the account row is created lazily when missing). Retries hit the status guard — no double credit. The `refund_completed` hook point is a `tracing` call today; notifications attach there later. - Eligibility: order paid/fulfilling/shipped/completed and updated within `AFTERSALE_WINDOW_DAYS` (15). - Messages are an append-only bilateral log (buyer/merchant/platform); readers are the customer, the owning shop, and platform admins. ## Reviews (`modules/review/`) One review per completed order line (unique index on `order_item_id`), rating 1–5 + bilingual content (at least one locale; display falls back) + image URLs; immutable after creation. Merchant replies once (guarded `WHERE reply IS NULL`). Platform moderation: `visible` → `hidden` (guarded) or delete. Public listing and the SQL rating summary (count/avg/star distribution) only see `visible` rows. `GET /me/reviewable` lists the customer's completed lines without reviews. ## Key files See `docs/code_index/post-order.md`.