All Data Labs

Machine learning

Payment Fraud Detection

hard4–5 hours2 datasets
Company
Razorpay
Job positions
Data ScientistML EngineerData Analyst
Topics
ClassificationImbalanced dataPrecision and recallData leakageThreshold tuning

The scenario

Razorpay: Build a classifier that flags fraudulent payments before they're approved, and pick a review threshold based on what fraud actually costs.

Razorpay processes online payments for businesses across India, from food delivery apps to airlines. Fraudsters use stolen cards and brand-new accounts, and every fraudulent payment that goes through comes back as a chargeback that costs the merchant the full amount plus a fee. You're a data scientist on the risk team, asked to build a model that sends suspicious payments to manual review before they're approved.

Your task

Train a classifier that scores each payment's fraud risk using only information available at payment time, choose a review threshold based on business cost, and score the most recent three weeks of payments.

Instructions

  1. 1Explore the training data: how rare fraud is, and how the fraud rate changes with payment method, merchant category, card type and time of day.
  2. 2Check every column for leakage. Leave out anything that is only known after a payment is approved, and explain why.
  3. 3Build a rules baseline, such as flagging international cards used from a different country, and measure its precision, recall and average precision.
  4. 4Engineer features that make sense at payment time, such as how unusual an amount is for its merchant category, and train at least two models. Handle the class imbalance, for example with class weights.
  5. 5Validate on the most recent weeks of the training data instead of a random split, since the model will score future payments.
  6. 6Choose a threshold for sending payments to review. Assume each missed fraud costs its full amount_inr plus a ₹500 chargeback fee, and each legitimate payment sent to review costs ₹40 of analyst time. Compare the total cost at several thresholds.
  7. 7Score every payment in transactions_test.csv and save predictions.csv with the columns transaction_id, fraud_score and flag_for_review (1 or 0).

Datasets

All files come in one download.

transactions_train.csv

About 20,000 payments from June 1 to August 10, 2025, with confirmed fraud labels.

20,026 rows · 15 columns · 1.7 MB

ColumnTypeDescription
transaction_idtextUnique ID of the payment.
created_atdatetimeWhen the payment was attempted, in UTC.
merchant_idintegerThe business receiving the payment.
merchant_categorytextThe merchant's business type.
amount_inrdecimalPayment amount, in Indian rupees.
payment_methodtextupi, card, netbanking or wallet.
card_networktextvisa, mastercard or rupay. Empty for payments not made by card.
is_international_cardtextWhether the card was issued outside India: yes or no. Empty for payments not made by card.
customer_account_age_daysintegerDays since the customer's account was created.
device_typetextandroid, ios or web.
customer_txns_last_24hintegerPayments the same customer attempted in the previous 24 hours.
failed_attempts_last_hourintegerFailed payment attempts by the same customer in the previous hour.
ip_country_matches_cardtextWhether the customer's IP address is in the card's country: yes or no.
chargeback_receivedtextWhether the customer's bank later reversed the payment: yes or no. This arrives weeks after the payment.
is_fraudinteger1 if the payment was confirmed as fraud, otherwise 0. This is what you predict.
Preview the first 5 rows
transaction_idcreated_atmerchant_idmerchant_categoryamount_inrpayment_methodcard_networkis_international_cardcustomer_account_age_daysdevice_typecustomer_txns_last_24hfailed_attempts_last_hourip_country_matches_cardchargeback_receivedis_fraud
pay_7000012025-06-01 00:01:511171utilities841.3cardvisano384android31yesno0
pay_7000022025-06-01 00:13:361275ecommerce271.24upiemptyempty587android21yesno0
pay_7000032025-06-01 00:18:391125education4247.66upiemptyempty34web00yesno0
pay_7000042025-06-01 00:20:081128ecommerce684.11upiemptyempty230android10yesno0
pay_7000052025-06-01 00:25:471188education7694.18netbankingemptyempty306android12yesno0

transactions_test.csv

About 6,000 payments from August 11 to 31, 2025, without labels.

5,974 rows · 13 columns · 476 KB

ColumnTypeDescription
transaction_idtextUnique ID of the payment.
created_atdatetimeWhen the payment was attempted, in UTC.
merchant_idintegerThe business receiving the payment.
merchant_categorytextThe merchant's business type.
amount_inrdecimalPayment amount, in Indian rupees.
payment_methodtextupi, card, netbanking or wallet.
card_networktextvisa, mastercard or rupay. Empty for payments not made by card.
is_international_cardtextWhether the card was issued outside India: yes or no. Empty for payments not made by card.
customer_account_age_daysintegerDays since the customer's account was created.
device_typetextandroid, ios or web.
customer_txns_last_24hintegerPayments the same customer attempted in the previous 24 hours.
failed_attempts_last_hourintegerFailed payment attempts by the same customer in the previous hour.
ip_country_matches_cardtextWhether the customer's IP address is in the card's country: yes or no.
Preview the first 5 rows
transaction_idcreated_atmerchant_idmerchant_categoryamount_inrpayment_methodcard_networkis_international_cardcustomer_account_age_daysdevice_typecustomer_txns_last_24hfailed_attempts_last_hourip_country_matches_card
pay_7200272025-08-11 00:01:211220utilities2736.32upiemptyempty12android21yes
pay_7200282025-08-11 00:01:301167utilities848.56cardvisano426android30no
pay_7200292025-08-11 00:03:221158utilities490.76upiemptyempty747android00yes
pay_7200302025-08-11 00:05:361220utilities967.39walletemptyempty114android01yes
pay_7200312025-08-11 00:21:191006gaming139.18upiemptyempty399android10yes

Hints

Accuracy is misleading when only about 1 in 75 payments is fraud: a model that flags nothing is almost 99% accurate. Use precision, recall and average precision instead.

from sklearn.metrics import average_precision_score
average_precision_score(y_valid, valid_scores)

Sort by time and hold out the last few weeks, so your validation looks like the real task of scoring future payments.

train = df[df["created_at"] < "2025-07-21"]
valid = df[df["created_at"] >= "2025-07-21"]

Deliverable

A public GitHub repo with your notebook or scripts, predictions.csv for the test payments, and a README covering your leakage checks, validation approach, model comparison, cost-based threshold and the strongest fraud signals.

When you're done, post your repo in the Solutions tab to share it with other learners.

What grading checks

Use this checklist to review your own work before you post and share it.

  • Submitted GitHub repo is public and reachable.
  • Repo contains at least one notebook or script file.
  • predictions.csv has a fraud_score and flag_for_review for every test payment.
  • fraud_score reaches an average precision of at least 0.35 on the test payments.
  • Under the cost assumptions in the brief, the flagged payments cost less than reviewing none.
  • The column only known after approval is left out of the model, with the reason explained.
  • Models are compared on a time-based validation set with precision, recall and average precision.