36 lines
2.9 KiB
Markdown
36 lines
2.9 KiB
Markdown
# ADR 0013: Model bank-paid, verified, and merchant-fee semantics
|
|
|
|
- Status: Accepted
|
|
- Date: 2026-08-20
|
|
|
|
## Context
|
|
|
|
The source exposes session states `Paid`, `Verified`, `Failed`, and `Reversed`, but names alone did not establish business meaning. Authoritative clarification from ZarinPal's CTO defines a two-step lifecycle: the buyer first completes payment at the bank, then the merchant calls verification. `Paid` means the bank payment succeeded and funds were deducted but merchant verification is incomplete. `Verified` means both steps completed. `Reversed` is possible but extremely rare; the dataset contains one such session.
|
|
|
|
The CTO data guide states that challenge `adjusted_fee` is uniformly scaled for commercial confidentiality and is not the actual ZarinPal tariff; relative ordering, trends, and fee share remain valid. The approved product contract nevertheless treats the canonical production field as the merchant's real fee because accepted production data will supply the real value. Profiling found the challenge value constant within every session: 150,450 repeated rows and zero conflicts.
|
|
|
|
## Decision
|
|
|
|
Use these canonical populations:
|
|
|
|
- bank-paid: session status in (`Paid`, `Verified`);
|
|
- verified/finalized: session status `Verified`;
|
|
- unverified paid: session status `Paid`;
|
|
- unsuccessful current outcome: `Failed` or `Reversed` for the MVP success-rate denominator.
|
|
|
|
Define payment success rate as bank-paid sessions divided by sessions in (`Paid`, `Verified`, `Failed`, `Reversed`). Keep unknown future statuses out of numerator/denominator and report their coverage until explicitly classified. Define verification completion rate as `Verified / (Paid + Verified)`.
|
|
|
|
Paid amount is the session amount summed once for (`Paid`, `Verified`). Also expose verified amount and unverified-paid amount as explicit subsets. Never call `Paid` a failure.
|
|
|
|
A retried session has more than one attempt. An eventually paid retry has final session status `Paid` or `Verified` and at least one lower-`try_seq` attempt whose status is neither `Paid` nor `Verified`; CSV row order is irrelevant.
|
|
|
|
Map challenge `adjusted_fee` into canonical `fee_amount_rial` for the demo and require production writers to provide the real Rial fee under that canonical contract. Store it once on the session and present the canonical product value as the real merchant fee. Paid fee totals sum it once for bank-paid sessions. Fee rate is `sum(paid session fee) / sum(paid session amount)` with numerator, denominator, period, and unit visible.
|
|
|
|
## Consequences
|
|
|
|
- Dashboard success combines `Paid` and `Verified`, while verification health separates them.
|
|
- The unverified-paid count/amount is an actionable merchant discrepancy, not a failed-payment metric.
|
|
- Retry recovery may end in either `Paid` or `Verified`.
|
|
- Fee aggregation uses session grain and cannot multiply fees by retry count.
|
|
- Unknown statuses require a metric-catalogue change rather than being guessed into success or failure.
|