This analysis demonstrates how payment platforms and ecommerce businesses can use transaction-level data to balance approval rates and loss exposure when dealing with disputed transactions.
Payments platforms face a constant tradeoff:
Approve more transactions to drive revenue
Limit fraud and disputes to protect margins and network health
At the same time, not all merchants perform equally well. Some experience rising decline rates or loss exposure over time and require targeted intervention.
Core business questions:
How should a transaction risk score be used to define approval policy?
What score threshold achieves high approval while containing losses?
Which merchants show early warning signs of deteriorating performance?
2 Data & Definitions
The analysis uses order-level data with the following key fields:
amount: transaction value
decision: historical approve / decline outcome
dispute_flag: indicator of post-approval disputes (“chargebacks”)
score_validation <-dbGetQuery(con, "SELECT CASE WHEN ip_risk_score < 0.2 THEN '<0.2' WHEN ip_risk_score < 0.4 THEN '0.2–0.4' WHEN ip_risk_score < 0.6 THEN '0.4–0.6' WHEN ip_risk_score < 0.8 THEN '0.6–0.8' ELSE '0.8–1.0' END AS score_band, COUNT(*) AS n, AVG(CASE WHEN decision = 'declined' THEN 1 ELSE 0 END) AS decline_rate, AVG(CASE WHEN chargeback_flag = 1 THEN 1 ELSE 0 END) AS chargeback_rateFROM ordersGROUP BY 1ORDER BY 1;")score_validation
score_validation %>%pivot_longer(cols =c(decline_rate, chargeback_rate),names_to ="metric",values_to ="rate") |>ggplot(aes(x = score_band, y = rate, fill = metric)) +geom_col(position ="dodge") +labs(title ="Decline and Chargeback Rates by Risk Score Band",x ="Risk Score Band",y ="Rate" )
3.1 Interpretation
Both decline rates and dispute rates rise sharply at higher score ranges, particularly above 0.6. This confirms that higher ip_risk_score values correspond to higher transaction risk.
4 Approval Policy Threshold Simulation
We next simulate approval policies based on score thresholds via the following logic:
Approve transactions with ip_risk_score ≤ threshold
Decline transactions above the threshold
A “90% approval” policy corresponds approximately to the 90th percentile of the score distribution.
threshold_results <-dbGetQuery(con, "WITH thresholds AS ( SELECT 0.70 AS q, quantile_cont(ip_risk_score, 0.70) AS threshold FROM orders UNION ALL SELECT 0.80 AS q, quantile_cont(ip_risk_score, 0.80) AS threshold FROM orders UNION ALL SELECT 0.90 AS q, quantile_cont(ip_risk_score, 0.90) AS threshold FROM orders UNION ALL SELECT 0.95 AS q, quantile_cont(ip_risk_score, 0.95) AS threshold FROM orders),sim AS ( SELECT t.q, t.threshold, AVG(CASE WHEN o.ip_risk_score <= t.threshold THEN 1 ELSE 0 END) AS approval_rate, SUM(CASE WHEN o.ip_risk_score <= t.threshold AND o.chargeback_flag = 1 THEN o.amount ELSE 0 END) * 1.0 / NULLIF(SUM(CASE WHEN o.ip_risk_score <= t.threshold THEN o.amount ELSE 0 END), 0) AS loss_per_approved_dollar FROM thresholds t CROSS JOIN orders o GROUP BY 1,2)SELECT *FROM simORDER BY q;")
ggplot(threshold_results,aes(x = approval_rate, y = loss_per_approved_dollar)) +geom_point(size =3) +geom_line() +labs(title ="Approval Rate vs Loss per Approved Dollar",x ="Approval Rate",y ="Loss per Approved Dollar" )
4.1 Interpretation
Higher approval targets increase loss exposure non-linearly. The curve highlights a region where marginal approvals come at sharply higher cost. This provides a quantitative basis for selecting an operating threshold based on risk tolerance.
5 Merchant Health Monitoring
Beyond global policy, we monitor merchant-level performance over time. We use monthly merchant KPIs to detect:
Rising dispute rates
Falling approval rates
Sufficient volume to be operationally meaningful
review_queue <-dbGetQuery(con, "SELECT merchant_name, industry, order_month, orders, approval_rate, chargeback_rate_on_approved, loss_per_approved_dollar, cb_rate_delta, approval_rate_deltaFROM review_queueWHERE cb_rate_delta IS NOT NULLORDER BY cb_rate_delta DESCLIMIT 10;")review_queue