Merchant Fraud Risk Scoring
Reported by candidates from Stripe's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.
Stripe's Merchant Fraud Risk Scoring question, reported in July 2026, looks like a wall of rules, but it's a simulation with hash maps. Three passes over at most 1000 transactions, each updating a merchant score in order. The catch is the third-transaction trigger: when a (customer, merchant) pair hits three, you retroactively add the first three additive factors. Candidates who read it fast miss that and fail example 3. If you blank during the live OA, StealthCoder runs invisibly and gives you a working solution while you sort out the rules.
The problem
You are given the transactions for one day, a list of merchants with their starting fraud-risk scores, and one rule row corresponding to each transaction. Compute the final fraud-risk score of every merchant. Input representation transactions[i] = [customerId, merchantId, hour, amount], where hour and amount are base-10 strings. merchantIds[j] identifies the merchant whose initial score is baseScores[j]. rules[i] = [minTransactionAmount, multiplicativeFactor, additiveFactor, penalty] applies to transactions[i]. Initialize each merchant's currentScore to its base score. Then perform the following three passes in order. Each pass scans the complete transaction list from left to right. Amount pass: If a transaction's amount is strictly greater than its rule's minTransactionAmount, multiply that merchant's currentScore by the rule's multiplicativeFactor. Repeat-customer pass: Track transactions by the pair (customerId, merchantId). When a pair reaches its third transaction, add the additiveFactor values of its first, second, and third transactions to that merchant's score. For every later transaction of the same pair, add only that transaction's additiveFactor. Same-hour pass: Track transactions by (customerId, merchantId, hour). Once a group reaches its third transaction, apply the first three penalty values together. For every later transaction in that group, apply only its own penalty. Add penalties when the hour is from 12 through 17, inclusive. Subtract penalties when the hour is from 9 through 11 or from 18 through 21, inclusive. For every other hour, do not change the score. Return the final scores in the same order as merchantIds. Function calculateMerchantFraudScores(transactions: String[][], merchantIds: String[], baseScores: long[], rules: long[][]) → long[] Examples Example 1 transactions = [["c1","m1","13","120"],["c1","m1","13","80"],["c1","m1","13","90"],["c2","m2","9","50"]] merchantIds = ["m1","m2"] baseScores = [10,5] rules = [[100,2,3,4],[100,3,5,6],[100,2,7,8],[40,2,1,9]] return = [53,10] During the amount pass, m1 changes from 10 to 20, and m2 changes from 5 to 10. The three (c1, m1) transactions add 3 + 5 + 7 = 15. Because all three also occur during hour 13, their penalties add another 4 + 6 + 8 = 18. The final scores are [20 + 15 + 18, 10] = [53, 10]. Example 2 transactions = [["c","m","10","50"],["c","m","10","50"],["c","m","10","50"]] merchantIds = ["m"] baseScores = [4] rules = [[100,2,1,10],[100,2,2,20],[100,2,3,30]] return = [-50] No amount exceeds its threshold. The third transaction activates the repeat-customer rule, adding 1 + 2 + 3 = 6. Because hour 10 is in the subtractive range, the same-hour pass subtracts 10 + 20 + 30 = 60. The final score is 4 + 6 - 60 = -50. Example 3 transactions = [["c","m","12","101"],["c","m","12","101"],["c","m","12","101"],["c","m","12","101"]] merchantIds = ["m"] baseScores = [2] rules = [[100,2,1,1],[100,3,1,2],[100,2,1,3],[100,2,5,4]] return = [66] The amount pass changes the score from 2 to 48. The repeat-customer pass adds 1 + 1 + 1 when the third transaction is reached and then adds 5 for the fourth. The same-hour pass adds 1 + 2 + 3 at the third transaction and 4 at the fourth, giving 48 + 8 + 10 = 66. Constraints 1 <= transactions.length <= 1000 transactions.length == rules.length Every transactions[i] has exactly four fields, and every rules[i] has exactly four values. 1 <= merchantIds.length == baseScores.length <= 200000 Merchant IDs are unique, and every transaction references a merchant in merchantIds. Customer and merchant IDs are non-empty ASCII strings of length at most 40. 0 <= hour <= 23 0 <= amount, minTransactionAmount <= 10^9 1 <= multiplicativeFactor <= 10^6 0 <= baseScores[i], additiveFactor, penalty <= 10^9 Every intermediate and final score fits in a signed 64-bit integer.
Reported by candidates. Source: FastPrep
Pattern and pitfall
The pattern is simulation backed by hash tables. Build a map from merchantId to its index or running score. Pass one multiplies when amount is strictly greater than the threshold. Pass two keeps a map keyed by (customer, merchant) holding a list of additive factors. At count three, add all three. At count four or more, add only the current one. Pass three does the same with key (customer, merchant, hour), but the sign depends on the hour: add for 12-17, subtract for 9-11 and 18-21, nothing otherwise. Pitfalls: parsing hour and amount from strings, using 64-bit longs, and keeping the three passes separate, because the multiply must finish before any additions. Don't interleave them. Nothing is asymptotically hard here, it's about carefulness. If you freeze on the grouping logic, StealthCoder is the hedge during the live OA, giving you the structure so you only verify it against the three examples.
If this hits your live OA and you blank, StealthCoder solves it in seconds, invisible to the proctor.
You can drill Merchant Fraud Risk Scoring cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Built by an Amazon engineer who would have shipped this the night before his JPMorgan OA if he'd had it.
Get StealthCoderRelated leaked OAs
You've seen the question.
Make sure you actually pass Stripe's OA.
Stripe reuses patterns across OAs. Built by an Amazon engineer who would have shipped this the night before his JPMorgan OA if he'd had it. Works on HackerRank, CodeSignal, CoderPad, and Karat.
Merchant Fraud Risk Scoring FAQ
How hard is the Stripe Merchant Fraud Risk Scoring question really?+
Algorithmically easy, since it's a linear simulation over 1000 transactions. The difficulty is reading comprehension. Three passes, a retroactive third-transaction rule, and signed penalties by hour range. Most failures come from misreading, not from missing a data structure.
What's the trick to the repeat-customer and same-hour passes?+
Store a count and the first three values per key. When the count reaches exactly three, add the sum of those three values. After that, add only the current value. Use a tuple key, such as customer|merchant for repeats and customer|merchant|hour for same-hour groups.
Why does the order of the three passes matter?+
The amount pass multiplies the score, and the other passes add or subtract. Multiplying after adding gives a different answer. Example 1 shows it: 10 doubles to 20 first, then 15 and 18 are added. Run each pass fully before starting the next.
What edge cases should I test before submitting?+
Test hours outside the penalty ranges (0-8, 22-23) so nothing changes. Test amount equal to the threshold, which shouldn't trigger the multiply. Test negative final scores like example 2, and four or more transactions in one group like example 3. Use long for everything.
How do I prepare for this in 48 hours?+
Practice hash-map grouping with composite keys and simulation problems that have ordered phases. Code the three examples as tests first. Then write each pass as its own loop with clear variable names. Keeping the passes separate makes bugs easy to find.