clean and refine product docs structure

This commit is contained in:
hamid
2026-06-24 01:32:46 +03:30
parent be07c703ec
commit 1df3cd9f64
113 changed files with 6078 additions and 4973 deletions
+36
View File
@@ -0,0 +1,36 @@
# Domain 8 — BNPL / Installments
[← Database Model](index.md)
**Related:** business requirements — [Installments / BNPL](../business/09-installments-bnpl.md). Settlement and reversal detail are explained in depth in the payments docs — [BNPL landscape](../payments/bnpl-landscape.md) and [Cancellation & payout](../payments/cancellation-and-payout.md).
**Resolved.** Because verified research shows Iranian provider-financed BNPL settles the **full amount to the merchant in one lump** (provider owns the customer's installments and default risk), a BNPL order is — in our books — a card payment that lands net-of-fee. The original `installment_plans` + `installment_entries` subsystem (which tried to track the customer's repayment schedule and default) is **deleted**: it modeled a receivable Balinyaar never owns and a risk it never bears.
### `bnpl_transactions` [MVP] — **NEW (replaces `installment_plans`)**
**Role:** One row per BNPL order, 1:1 with its `payment_transaction` — the single inbound settlement to reconcile, plus the revert path. **Why one row, not a plan+entries tree:** there is nothing to amortize on our side; we track the settlement, the provider's commission, and the reversal.
| Field | Type | Notes |
|---|---|---|
| `id` | BIGINT PK | |
| `payment_transaction_id` | BIGINT FK UNIQUE | 1:1 |
| `provider_code` | NVARCHAR(50) | `snapppay` / `digipay` / `tara` / `torobpay` |
| `merchant_of_record` | NVARCHAR(40) | Balinyaar entity or partner center |
| `external_payment_token` | NVARCHAR(200) | For verify/settle/revert |
| `external_transaction_id` | NVARCHAR(200) | |
| `eligibility_status` | NVARCHAR(30) | |
| `order_amount_irr` | BIGINT | Gross order |
| `settled_amount_irr` | BIGINT | Net of provider commission actually received |
| `bnpl_commission_irr` | BIGINT | Provider's merchant discount = platform **expense** (never the nurse's) |
| `currency` | NVARCHAR(5) | `IRR`/`TOMAN` at boundary; convert in |
| `installment_count` | TINYINT | Informational (default 4) — owned by the provider |
| `status` | NVARCHAR(30) | State machine: `eligible`/`token_issued`/`verified`/`settled`/`reverted`/`cancelled`/`failed` |
| `settled_at` | DATETIME2 NULL | **Per-transaction** — timing is contract-defined (daily/T+1-3/weekly), never assumed instant |
| `revert_transaction_id`, `reverted_amount_irr`, `reverted_at` | … | Reversal path |
| `refund_channel` | NVARCHAR(20) | |
| `callback_payload_json` | NVARCHAR(MAX) | Raw verify/settle payload |
| timestamps | … | |
**Relations:** 1:1 → `payment_transactions`. **State-machine guard** on `status` for idempotency.
### `bnpl_settlement_entries` [DEFERRED]
**Role/Why:** Only needed if a future provider uses **tranched** settlement (pays the platform over time). No mainstream Iranian provider does today, so it's modeled-but-inactive; adding it later is a purely additive migration.