clean and refine product docs structure
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
# 8. Payments & Escrow
|
||||
|
||||
[← Business Requirements](index.md)
|
||||
|
||||
## (a) Business requirements
|
||||
- The family pays the **gross** booking price **through the platform** by card via a licensed PSP's IPG. The platform is the **merchant-of-record**; the payment lands net of provider/Shaparak fees.
|
||||
- **Escrow is an internal ledger state, not platform-held cash.** The platform models money state with a minimal **double-entry `ledger_entries`** ledger: each money event posts **balanced** legs grouped by a transaction group. Account types: `escrow_held`, `platform_revenue`, `nurse_payable`, `refund_payable`, `bnpl_fee_expense`, `nurse_clawback_receivable`. The ledger is the **single source of truth** for "how much is held," "how much do we owe nurses now," and "what is our commission income" — replacing fragile inference from scattered status booleans.
|
||||
- On a successful card payment: debit `escrow_held` (gross), credit `platform_revenue` (Balinyaar commission), credit `nurse_payable` (nurse payout).
|
||||
- **Settlement-sharing (تسهیم).** The compliant marketplace primitive is splitting one incoming card payment across multiple **registered IBANs** (the nurse's share and the platform's commission) at settlement, performed by Shaparak/the provider — the platform never touches the actual split. The internal ledger mirrors this split; the per-booking fee snapshot freezes it.
|
||||
- **Per-booking the three amounts are stored separately and never conflated:** `gross_price_irr` (what the customer is charged), `balinyaar_commission_irr` (platform's cut — drives the nurse payout), and (for BNPL) `bnpl_commission_irr` (the provider's merchant discount — a platform expense). `nurse_payout_amount = gross_price_irr − balinyaar_commission_irr`.
|
||||
- **Webhook idempotency is mandatory before money moves.** Every PSP/BNPL callback is stored raw and **deduplicated by a unique external event id** in `payment_webhook_events` before any money state mutates — preventing double-confirmed bookings and double-settlements from at-least-once, retried callbacks.
|
||||
- **Payment uniqueness:** at most one `succeeded` payment transaction per booking, and the Shaparak reference is unique — enforced so a retried success webhook cannot double-confirm.
|
||||
- **Multi-provider failover.** Provider settlement cut-offs are a real continuity risk (the Toman/Jibit Nov-2024 suspensions cut businesses off mid-cycle). The payment layer abstracts the provider behind configuration so a blocked provider can be swapped, and the reconciliation ledger survives a provider being cut off.
|
||||
|
||||
## (b) Iran-specific considerations
|
||||
- **The load-bearing legal constraint:** a پرداختیار may **not** hold customer deposits, run wallets, or move money between merchants; the Shaparak ban on inter-merchant/inter-facilitator transfers means the "delay the تسهیم and redistribute later from a platform pool" pattern is regulatory grey-to-prohibited. The compliant posture is: collect via the provider, model escrow as an **internal ledger over funds custodied at the licensed provider/partner bank**, and pay out by provider-side settlement to **verified, registered nurse IBANs**. A bank-grade escrow product (e.g., Vandar میندو / معاملات امن) is the only true hold/release/refund mechanism, and its EVV-triggered hold is unverified — so the platform never assumes it can lawfully custody the cash itself.
|
||||
- **PSP received ≠ cash in bank.** Iranian PAYA settlement is cyclic (T+0/T+1, holiday-deferred), so the ledger separates a clearing/receivable state from settled cash, making bank reconciliation possible.
|
||||
- Toman/PSP units differ from internal Rials; convert only at the API boundary. Amounts are BIGINT IRR internally to avoid float/rounding bugs.
|
||||
|
||||
## (c) MVP vs DEFERRED
|
||||
- **MVP:** card payment via one licensed PSP; internal double-entry `ledger_entries` escrow; per-booking three-way amount split; تسهیم-style commission/nurse-share modeling; `payment_webhook_events` idempotency; single-succeeded-transaction-per-booking guard; provider abstraction for failover.
|
||||
- **DEFERRED:** a nurse-facing wallet with on-demand withdrawal (facilitator wallet prohibition risk); multiple simultaneous live PSPs at launch (abstraction is built, second provider added later); bank-grade EVV-triggered escrow product integration.
|
||||
|
||||
## (d) Supporting database entities
|
||||
`payment_gateways`, `payment_transactions` (unique Shaparak ref, single-succeeded-per-booking), **`payment_webhook_events`**, **`ledger_entries`**, `bookings` (`gross_price_irr`, `balinyaar_commission_irr`, `platform_fee_rate`, `nurse_payout_amount`), `refunds`, `nurse_bank_accounts` (verified registered IBANs).
|
||||
|
||||
> **Related:** Deep fintech detail — [Escrow Ledger](../payments/escrow-ledger.md). Data model — [Payments Ledger & Refunds](../data-model/06-payments-ledger-and-refunds.md).
|
||||
Reference in New Issue
Block a user