docs: correct payment and fee semantics

This commit is contained in:
2026-08-20 16:53:59 +03:30
parent 96a27e1b98
commit c3f6061a21
7 changed files with 138 additions and 33 deletions

View File

@@ -0,0 +1,35 @@
# 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 challenge field `adjusted_fee` was transformed/name-adjusted for participant confidentiality, but the product domain treats it as the merchant's real fee. Profiling found its value constant within every session: 150,450 repeated attempt rows and zero fee 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.
Rename source `adjusted_fee` to canonical `fee_amount_rial`, store it once on the session, and present it as a real Rial 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.