FastPrepTransaction Fraud Report Part 3 — User Behavior Matching

Transaction Fraud Report Part 3 — User Behavior Matching

Stripe logoStripe● MediumNEW GRADPHONE SCREEN
Learn

Problem statement

Four-part sequence: This is Part 3 of one progressive transaction-fraud interview exercise. It retains Parts 1–2 and adds behavior-baseline matching.

  1. Part 1: Verify Transaction Data Integrity.
  2. Part 2: High-Risk Rule Validation.
  3. Part 3: User Behavior Matching (you are here).
  4. Part 4: Smart Fraud Error Reporting.

Validate each transaction, apply the global risk rules, and compare its behavior with the corresponding user's baseline.

Input

Each string in transactions is one unquoted CSV row with fields transaction_id,user_id,amount,payment_method,country,hour. Trim surrounding whitespace from every field.

Each row of baselines is [user_id,usual_country,start_hour,end_hour,min_amount,max_amount]. Every transaction whose fields are non-empty has exactly one matching baseline. Hour and amount intervals are inclusive and do not wrap.

Review rules

  1. If any transaction field is empty, mark the row SUSPICIOUS and stop evaluating that row.
  2. Otherwise, mark it SUSPICIOUS when its amount is outside the inclusive global interval [normalMin, normalMax] or its payment method is in the case-sensitive blockedMethods set.
  3. Compare three behavior features: exact country match, hour inside the baseline interval, and amount inside the baseline interval. At least two of the three features must match. Fewer than two matches makes the transaction SUSPICIOUS.
  4. A transaction is OK only when it passes every applicable rule.

Result

Return a two-column string table whose first row is ["transaction_id","status"]. Append one result row per transaction in input order.

Function

reviewTransactionBehavior(transactions: String[], baselines: String[][], normalMin: int, normalMax: int, blockedMethods: String[]) → String[][]

Examples

Example 1

transactions = ["tx1,u1,500,card,US,10","tx2,u1,50,cash,CA,20","tx3,u1,550,card,CA,10","tx4,u1,550,card,CA,20","tx5,u1,,card,US,10"]baselines = [["u1","US","9","17","400","600"]]normalMin = 100normalMax = 1000blockedMethods = ["cash"]return = [["transaction_id","status"],["tx1","OK"],["tx2","SUSPICIOUS"],["tx3","OK"],["tx4","SUSPICIOUS"],["tx5","SUSPICIOUS"]]

tx1 passes all rules. tx2 violates both global rules and matches no behavior feature. tx3 matches hour and amount, so it reaches the required two matches. tx4 matches only amount. tx5 stops at integrity validation.

Example 2

transactions = ["country,u2,100,Card,US,23","hour,u2,100,Card,CA,0","amount,u2,200,Card,CA,23"]baselines = [["u2","US","0","23","100","100"]]normalMin = 100normalMax = 200blockedMethods = ["card"]return = [["transaction_id","status"],["country","OK"],["hour","OK"],["amount","SUSPICIOUS"]]

country matches all three features. hour matches the inclusive hour and amount boundaries. amount matches only the hour boundary, so it is suspicious. Method matching is case-sensitive, so Card does not equal blocked method card.

Constraints

  • 1 <= transactions.length <= 10000; every row contains exactly six comma-separated fields and no quoted commas.
  • 1 <= baselines.length <= 10000; baseline user IDs are unique.
  • 0 <= normalMin <= normalMax <= 1000000000.
  • Every non-empty amount and baseline amount bound is an integer from 0 through 1000000000.
  • Every non-empty transaction hour and baseline hour bound is an integer from 0 through 23, and each baseline lower bound is at most its upper bound.
  • Every transaction with six non-empty fields has a matching user baseline. Labels and identifiers contain at most 100 characters.

More Stripe problems

See Stripe hiring insights
public String[][] reviewTransactionBehavior(String[] transactions, String[][] baselines, int normalMin, int normalMax, String[] blockedMethods) {
    // Write your code here
}
transactions["tx1,u1,500,card,US,10","tx2,u1,50,cash,CA,20","tx3,u1,550,card,CA,10","tx4,u1,550,card,CA,20","tx5,u1,,card,US,10"]
baselines[["u1","US","9","17","400","600"]]
normalMin100
normalMax1000
blockedMethods["cash"]
expected[["transaction_id", "status"], ["tx1", "OK"], ["tx2", "SUSPICIOUS"], ["tx3", "OK"], ["tx4", "SUSPICIOUS"], ["tx5", "SUSPICIOUS"]]
Checking account…