Files
zarinpal-analytics-backend/docs/METRIC-CATALOGUE.md

123 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Analytics Metric Catalogue
- Status: Approved specification — implementation authorized 2026-08-20
- Version: 0.5.0
- Last updated: 2026-08-22
Every merchant-facing number identifies its grain, formula, source fields, filters, Tehran-local period, unit, missing-data behavior, trace path, and definition version.
## Physical source mapping
- Session-grain metrics read `payment_sessions`, scoped by `merchant_id` and the approved half-open `created_at` period.
- Attempt-grain diagnostics and timings join `payment_attempts.payment_session_id` to the already merchant-scoped session set.
- Money uses `payment_sessions.amount_rial` and `payment_sessions.fee_amount_rial` exactly once per session.
- Terminal filters join the session's `terminal_id`; peer cohorts join the merchant's `category_id` and exclude the active merchant.
- The sole completed `import_runs` row identifies the immutable MVP dataset snapshot included in cache/result metadata.
- `calendar_dates` defines Saturday–Friday weeks, Jalali months, and Persian seasons; it intentionally has no holiday dimension. Growth reports use their own reviewed, versioned event-evidence catalog under ADR 0015.
## Bank-paid sessions
- Canonical id: `bank_paid_sessions`
- Grain: distinct payment session
- Formula: count sessions where `session_status IN ('Paid', 'Verified')`
- Meaning: the buyer completed the bank payment and funds were deducted
- Unit: sessions
- Time attribution: session `created_at` in `Asia/Tehran`
- Traceability: merchant-scoped session identifiers and attempt histories
## Payment success rate
- Canonical id: `payment_success_rate`
- Numerator: `bank_paid_sessions`
- Denominator: sessions where `session_status IN ('Paid', 'Verified', 'Failed', 'Reversed')`
- Formula: numerator / denominator; undefined when denominator is zero
- Unknown statuses: excluded from both sides and disclosed as coverage/quality evidence until classified
- Unit: percentage
## Verified sessions
- Canonical id: `verified_sessions`
- Formula: count sessions where `session_status = 'Verified'`
- Meaning: bank payment and merchant verification are complete; the payment is finalized
- Unit: sessions
## Unverified paid sessions
- Canonical id: `unverified_paid_sessions`
- Formula: count sessions where `session_status = 'Paid'`
- Meaning: bank payment succeeded and funds were deducted, but merchant verification is incomplete
- Presentation: actionable verification gap; never label as failure
- Unit: sessions
## Verification completion rate
- Canonical id: `verification_completion_rate`
- Formula: `verified_sessions / bank_paid_sessions`; undefined when no bank-paid sessions exist
- Unit: percentage
## Transaction status analytics
- `total_sessions`: count all merchant-scoped sessions in the selected half-open reporting period.
- `bank_success_rate`: sessions with status `Paid` or `Verified` divided by `total_sessions`; undefined when the total is zero.
- `failure_rate`: sessions with status `Failed` divided by `total_sessions`; undefined when the total is zero.
- Status distribution reports `Paid`, `Verified`, `Failed`, and `Reversed` separately as count and share of `total_sessions`.
- Trend grain is hourly for a day, daily for a week or Jalali month, and Saturday-aligned weekly for a Persian season. Empty buckets are returned as zeroes.
## Paid volume
- Canonical id: `bank_paid_amount_rial`
- Formula: sum session `amount_rial` once where `session_status IN ('Paid', 'Verified')`
- Unit: Rial
- Subsets: `verified_amount_rial` uses `Verified`; `unverified_paid_amount_rial` uses `Paid`
## Retried sessions and eventual payment
- `retried_sessions`: count sessions containing more than one payment attempt
- `eventually_paid_after_retry_sessions`: count retried sessions whose final session status is `Paid` or `Verified` and which contain a lower-`try_seq` attempt whose status is neither `Paid` nor `Verified`
- `retry_recovery_rate`: `eventually_paid_after_retry_sessions / retried_sessions`; undefined when no retried sessions exist
- Grain/unit: distinct sessions
- Traceability: ordered merchant-scoped attempt history; never depend on CSV row order
## Payment-stage diagnostics
- `no_attempt_sessions`: count sessions with zero normalized payment attempts. During challenge bootstrap this corresponds to the source marker `try_seq = 0 AND try_status = 'NoAttempt'`; the marker itself is not an attempt.
- `reached_bank_sessions`: count distinct sessions containing at least one attempt with `try_status IN ('InBank', 'Paid', 'Verified', 'Reversed')`.
- `in_bank_without_result_sessions`: count sessions whose latest positive-sequence attempt is `InBank` and which have no later `Paid`, `Verified`, `Failed`, or `Reversed` attempt.
- `failed_sessions`: count sessions with final `session_status = 'Failed'`; drill-down may distinguish zero-attempt, reached-bank, and other observable stages without inventing undocumented failure causes.
- Grain/unit: distinct payment sessions; attempt-stage counts must deduplicate by session when presented as sessions.
## Processing time
- Report median (p50) and p95 separately for `init_time_ms` and `verify_time_ms`
- Include only non-null values for the relevant field
- Always show contributing sample count and coverage percentage
- Do not replace missing timing with zero
- Unit: milliseconds, with human-readable seconds allowed as an additional presentation
## Merchant fee
- Canonical field: `fee_amount_rial`, sourced from CSV `adjusted_fee` and stored once per session
- `paid_fee_amount_rial`: sum session fee once where `session_status IN ('Paid', 'Verified')`
- `paid_fee_rate`: `paid_fee_amount_rial / bank_paid_amount_rial`; undefined when paid amount is zero
- Meaning/presentation: real merchant fee in Rial; do not label it obfuscated or relative-only in the product
- Retry rule: never sum the repeated source value per attempt
## Common behavior
- Rates are recomputed from summed numerator/denominator components for every selected day, Saturday–Friday week, Jalali month, and Persian season.
- Zero, undefined, missing, suppressed peer value, and calculation failure remain distinct states.
- `Paid` and `Verified` are both bank-payment successes, but only `Verified` is finalized.
- `Reversed` is not in the success numerator; display it separately when non-zero.
- `NoAttempt` is a source session marker, not a failed payment attempt. `InBank` means the user reached a bank gateway but no later outcome was recorded for that attempt; do not call it a bank rejection.
## Peer comparison
- Cohort: eligible merchants in the active merchant's category for the same selected reporting period; always exclude the active merchant.
- Availability: return a typed suppressed state unless at least 10 other merchants and 1,000 combined peer payment sessions contribute. Suppression never hides the active merchant's own metrics.
- Weighting: calculate each eligible peer merchant's metric independently, then use the median of those merchant values. Every merchant contributes one value regardless of payment volume.
- Rationale: merchant volume is extremely skewed (median 78 attempts, p95 8,915, maximum 1,138,592). A transaction-weighted average would mostly describe the largest merchants, while a merchant-weighted median represents a typical peer and resists extreme values.
- Output: expose the cohort median and the active merchant's performance percentile; never expose an individual peer's identity, value, or rank.
- Percentile direction: a higher displayed percentile always means better performance. Use the natural direction for payment success, verification completion, and retry recovery; invert the ranking direction for processing time and paid fee rate, where lower is better.
- Benchmarked metrics: `payment_success_rate`, `verification_completion_rate`, `retry_recovery_rate`, `init_time_ms` p50/p95, `verify_time_ms` p50/p95, and `paid_fee_rate`.
- Periods: calculate benchmarks for the same selected day, Saturday–Friday week, Jalali month, or Persian season as the active merchant value. Never compare unlike periods.