FastPrepMerchant Fraud Thresholds and Disputes

Merchant Fraud Thresholds and Disputes

Stripe logoStripe● MediumINTERNOA
Learn

Problem statement

Process merchant charges and disputes, then return the merchant IDs that are fraudulent after every event has been processed. Each merchant belongs to a merchant category code (MCC), and each MCC has its own fraud threshold.

Inputs

  • Each row of mccThresholds is [mcc, threshold].
  • Each row of merchants is [merchantId, mcc].
  • fraudulentCodes and nonFraudulentCodes are disjoint code lists. Every charge code belongs to exactly one list.
  • A charge event is ["CHARGE", transactionId, merchantId, code]. A dispute event is ["DISPUTE", transactionId].

Charge and dispute rules

  1. A new transaction ID records one charge and increments that merchant's total charge count. Increment its fraud count only if the charge code is in fraudulentCodes.
  2. A repeated CHARGE uses the same merchant and code as the original and has no effect.
  3. Every DISPUTE refers to an earlier charge. The first dispute removes that charge from the merchant's fraud count if it was fraudulent. It does not remove the charge from the total count: the transaction still occurred.
  4. A dispute of a non-fraudulent charge and every repeated dispute have no effect. A repeated charge cannot restore disputed fraud.

Threshold rules

  • A threshold without a decimal point, such as 2, is an integer fraud-count threshold. The merchant is fraudulent when its remaining fraud count is at least that value.
  • A threshold with a decimal point, such as 0.5 or 1.0, is a fraud-ratio threshold. The merchant is fraudulent when remaining fraud count / total charge count is at least that exact decimal value.
  • A merchant with no charges is not fraudulent. Reevaluate thresholds using the final counts; a merchant may stop being fraudulent after a dispute or an additional non-fraudulent charge.

Return every fraudulent merchant ID exactly once, sorted in ascending lexicographic order. The threshold comparison, duplicate handling, preceding-charge requirement, dispute denominator, and final-state output are the practice rules for this exercise.

Function

fraudulentMerchants(mccThresholds: String[][], merchants: String[][], fraudulentCodes: String[], nonFraudulentCodes: String[], events: String[][]) → String[]

Examples

Example 1

mccThresholds = [["retail","2"],["grocery","0.5"]]merchants = [["A","retail"],["B","grocery"]]fraudulentCodes = ["F"]nonFraudulentCodes = ["N"]events = [["CHARGE","a1","A","F"],["CHARGE","a2","A","F"],["CHARGE","b1","B","F"],["CHARGE","b2","B","N"],["DISPUTE","a1"]]return = ["B"]

A has one remaining fraudulent charge, below its count threshold of 2. B has one fraudulent charge out of two total charges, exactly its ratio threshold of 0.5.

Example 2

mccThresholds = [["count","1"],["ratio","1.0"]]merchants = [["countShop","count"],["ratioShop","ratio"]]fraudulentCodes = ["F"]nonFraudulentCodes = ["N"]events = [["CHARGE","c1","countShop","F"],["CHARGE","c2","countShop","N"],["CHARGE","r1","ratioShop","F"],["CHARGE","r2","ratioShop","N"]]return = ["countShop"]

The integer threshold requires one fraudulent charge, so countShop qualifies. The decimal threshold requires a fraud ratio of 1.0; ratioShop has only 1/2.

Example 3

mccThresholds = [["mcc","1"]]merchants = [["store","mcc"]]fraudulentCodes = ["F"]nonFraudulentCodes = ["N"]events = [["CHARGE","t1","store","F"],["DISPUTE","t1"],["DISPUTE","t1"],["CHARGE","t1","store","F"]]return = []

The first dispute reverses the one fraudulent charge. A repeated dispute cannot decrease the count again, and a repeated charge cannot restore it.

Constraints

  • 1 <= mccThresholds.length, merchants.length <= 10^3.
  • Every merchant ID and MCC is unique within its table; every merchant references a listed MCC.
  • 0 <= events.length <= 10^4. Every event has one of the stated row shapes and every charge references a listed merchant.
  • Each code list contains at most 10^3 unique codes. The lists are disjoint and every charge code is in exactly one list.
  • IDs and codes contain 1 through 40 ASCII letters, digits, or underscores. Comparisons are case-sensitive.
  • Integer thresholds are decimal integers from 1 through 10^5. Ratio thresholds are decimal strings in (0, 1] with one through six digits after the decimal point.
  • A repeated charge ID repeats the original merchant and code. Every dispute references a preceding charge; disputes may repeat.
  • The combined length of all input strings is at most 2 * 10^5 characters. These bounds and validity guarantees define the practice input domain.

More Stripe problems

See Stripe hiring insights
public String[] fraudulentMerchants(String[][] mccThresholds, String[][] merchants, String[] fraudulentCodes, String[] nonFraudulentCodes, String[][] events) {
    // Write your code here.
}
mccThresholds[["retail","2"],["grocery","0.5"]]
merchants[["A","retail"],["B","grocery"]]
fraudulentCodes["F"]
nonFraudulentCodes["N"]
events[["CHARGE","a1","A","F"],["CHARGE","a2","A","F"],["CHARGE","b1","B","F"],["CHARGE","b2","B","N"],["DISPUTE","a1"]]
expected["B"]
Checking account…