PracHub
QuestionsPremiumCoachesLearningGuidesInterview Prep

Quick Overview

This question evaluates skills in CSV parsing and input validation, numerical and timestamp parsing, rule-based fraud detection and behavioral feature matching, plus error prioritization and generation of fixed-width reports.

  • medium
  • Stripe
  • Coding & Algorithms
  • Software Engineer

Validate transactions with risk rules and reporting

Company: Stripe

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

You are building a small fraud-screening validator that reads transactions from a CSV file and outputs a human-readable report. ## Input ### 1) Transactions CSV Each row is one transaction with **exactly 6 fields**: 1. `transaction_id` (string) 2. `user_id` (string) 3. `timestamp` (string; assume ISO-8601 like `2026-01-22T13:45:00Z`) 4. `amount` (string that should parse to a decimal number) 5. `country` (string, e.g. `US`) 6. `payment_method` (string, e.g. `VISA`, `PAYPAL`) You may assume the first row is a header. ### 2) Risk configuration - `min_amount` and `max_amount` define the allowed inclusive range for `amount`. - `blocked_payment_methods` is a set/list of payment methods that are not allowed. ### 3) User behavior baseline For each `user_id`, you are given a baseline profile with these **3 behavioral features**: - `usual_countries`: a set/list of countries the user commonly transacts in - `usual_time_buckets`: a set/list of time-of-day buckets the user commonly transacts in (e.g. `NIGHT`, `MORNING`, `AFTERNOON`, `EVENING`) - `usual_amount_range`: an inclusive range `[low, high]` for typical transaction amounts You may assume every `user_id` in the transaction file exists in the baseline. ## Tasks (multi-part; later parts build on earlier ones) ### Part 1 — Verify transaction data integrity For each transaction, verify that **all 6 fields are non-empty** (after trimming whitespace). ### Part 2 — High-risk rule validation In addition to Part 1, mark a transaction as suspicious if **either** is true: - **Amount rule:** `amount` is outside `[min_amount, max_amount]` (or is not parseable as a number). - **Payment method rule:** `payment_method` is in `blocked_payment_methods`. ### Part 3 — User behavior matching Compute how well the transaction matches the user’s baseline across the 3 behavioral features. 1. **Feature extraction / normalization** - Map `timestamp` into one of the defined `usual_time_buckets` (you must define the bucketing rule, e.g. by hour ranges). 2. **Matching** - `country` matches if it is in `usual_countries`. - `time_bucket` matches if it is in `usual_time_buckets`. - `amount` matches if it is within `usual_amount_range`. 3. **Match ratio** - `match_ratio = matched_features / 3`. If `match_ratio < 0.5`, flag the transaction as suspicious due to behavior mismatch. ### Part 4 — Smart fraud error reporting Instead of outputting a vague status like `SUSPICIOUS`, output **specific error codes**. - Output **at most two** error codes per transaction. - Error codes must be chosen by priority (highest first). Use this priority order: 1. `MISSING_FIELD` 2. `AMOUNT_OUT_OF_RANGE` 3. `BLOCKED_PAYMENT_METHOD` 4. `BEHAVIOR_MISMATCH` - If there are **no** errors, output `OK`. ## Output Produce a report with fixed-width / aligned columns for readability. At minimum, include: - `transaction_id` - `user_id` - `result` (either `OK` or up to two comma-separated error codes) ### Notes / constraints - Implement so it can process many rows efficiently (streaming is acceptable). - Clearly define your time bucketing in code. - Trimming whitespace matters when checking for empty fields.

Quick Answer: This question evaluates skills in CSV parsing and input validation, numerical and timestamp parsing, rule-based fraud detection and behavioral feature matching, plus error prioritization and generation of fixed-width reports.

Part 1: Verify Transaction Data Integrity

You are given transaction CSV lines. The first line is a header and should be ignored. Each transaction row is intended to contain 6 fields: transaction_id, user_id, timestamp, amount, country, and payment_method. For every transaction, trim whitespace around each field and verify that all 6 fields are non-empty. Return a simple report row for each transaction.

Constraints

  • 0 <= number of transaction rows <= 100000
  • The first row, when present, is a header.
  • Each transaction row is intended to have 6 CSV fields.
  • Whitespace around fields must be ignored when checking emptiness.
  • Rows should be processed in input order.

Examples

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't1,u1,2026-01-22T13:45:00Z,20.50,US,VISA', 't2,u2,2026-01-22T14:00:00Z,5,CA,PAYPAL'],)

Expected Output: [['t1', 'u1', 'OK'], ['t2', 'u2', 'OK']]

Explanation: Both transaction rows have all 6 fields present after trimming.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', ' t3 , u3 , 2026-01-22T13:45:00Z , , US , VISA ', ' , u4 , 2026-01-22T13:45:00Z , 10 , US , VISA'],)

Expected Output: [['t3', 'u3', 'MISSING_FIELD'], ['', 'u4', 'MISSING_FIELD']]

Explanation: The first row has an empty amount after trimming. The second row has an empty transaction_id.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method'],)

Expected Output: []

Explanation: There are no transaction rows after the header.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't5,u5,2026-01-22T13:45:00Z,30,US,'],)

Expected Output: [['t5', 'u5', 'MISSING_FIELD']]

Explanation: The payment_method field is present but empty.

Hints

  1. Skip the header before validating transaction rows.
  2. Apply strip() to every field before checking whether it is empty.

Part 2: Validate High-Risk Transaction Rules

You are given transaction CSV lines, an inclusive allowed amount range, and a list of blocked payment methods. The first CSV line is a header and should be ignored. A transaction is suspicious if any required field is missing after trimming, if amount is not parseable as a finite decimal number, if amount is outside the inclusive range [min_amount, max_amount], or if payment_method is blocked.

Constraints

  • 0 <= number of transaction rows <= 100000
  • min_amount <= max_amount
  • The allowed amount range is inclusive.
  • Payment method matching is exact after trimming the transaction field.
  • Amount values should be treated as decimal numbers, not integers only.

Examples

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't1,u1,2026-01-22T10:00:00Z,50,US,VISA', 't2,u2,2026-01-22T10:00:00Z,150,US,VISA'], 10, 100, ['PAYPAL'])

Expected Output: [['t1', 'u1', 'OK'], ['t2', 'u2', 'SUSPICIOUS']]

Explanation: t1 is within range and uses an allowed method. t2 has amount greater than max_amount.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't3,u3,2026-01-22T10:00:00Z,abc,US,VISA', 't4,u4,2026-01-22T10:00:00Z,20,US,PAYPAL'], 0, 100, ['PAYPAL'])

Expected Output: [['t3', 'u3', 'SUSPICIOUS'], ['t4', 'u4', 'SUSPICIOUS']]

Explanation: t3 has an unparseable amount. t4 uses a blocked payment method.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't5,u5,2026-01-22T10:00:00Z,10,US,VISA', 't6,u6,2026-01-22T10:00:00Z,100,US,VISA'], 10, 100, [])

Expected Output: [['t5', 'u5', 'OK'], ['t6', 'u6', 'OK']]

Explanation: The min_amount and max_amount boundaries are inclusive.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method'], 0, 100, ['PAYPAL'])

Expected Output: []

Explanation: There are no transaction rows to validate.

Hints

  1. Convert blocked_payment_methods to a set for O(1) lookups.
  2. Treat an unparseable amount the same as an out-of-range amount.

Part 3: Match Transactions Against User Behavior Baselines

You are given transaction CSV lines and user behavior baselines. For each transaction, determine how many of 3 behavioral features match the user's baseline: country, time-of-day bucket, and amount range. Use this bucketing rule based on the UTC hour in the ISO timestamp: NIGHT is 00:00-05:59, MORNING is 06:00-11:59, AFTERNOON is 12:00-17:59, and EVENING is 18:00-23:59. The match ratio is matched_features / 3. Flag a transaction with 'BEHAVIOR_MISMATCH' if match_ratio < 0.5.

Constraints

  • 0 <= number of transaction rows <= 100000
  • Every non-empty user_id in the transactions exists in the baseline maps.
  • usual_amount_ranges[user_id] contains exactly two numeric values [low, high] with low <= high.
  • Amount range checks are inclusive.
  • If a transaction amount or timestamp cannot be interpreted, that feature does not match.

Examples

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't1,u1,2026-01-22T09:45:00Z,25,US,VISA'], {'u1': ['US', 'CA']}, {'u1': ['MORNING']}, {'u1': [10, 50]})

Expected Output: [['t1', 'u1', '1.00', 'OK']]

Explanation: Country, MORNING bucket, and amount all match.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't2,u2,2026-01-22T13:00:00Z,30,MX,VISA'], {'u2': ['US']}, {'u2': ['AFTERNOON']}, {'u2': [10, 50]})

Expected Output: [['t2', 'u2', '0.67', 'OK']]

Explanation: Time bucket and amount match, but country does not. Ratio is 2/3, which is not below 0.5.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't3,u3,2026-01-22T23:00:00Z,30,MX,VISA'], {'u3': ['US']}, {'u3': ['MORNING']}, {'u3': [10, 50]})

Expected Output: [['t3', 'u3', '0.33', 'BEHAVIOR_MISMATCH']]

Explanation: Only amount matches, so the ratio is 1/3, which is below 0.5.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't4,u4,2026-01-22T05:59:00Z,100,JP,VISA'], {'u4': ['JP']}, {'u4': ['NIGHT']}, {'u4': [100, 100]})

Expected Output: [['t4', 'u4', '1.00', 'OK']]

Explanation: 05:59 is NIGHT, and amount range boundaries are inclusive.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method'], {}, {}, {})

Expected Output: []

Explanation: There are no transactions after the header.

Hints

  1. Convert each user's usual countries and usual time buckets to sets before processing rows.
  2. The behavior score is just the count of three boolean matches.

Part 4: Report Prioritized Fraud Error Codes

You are given transaction CSV lines, high-risk configuration, and user behavior baselines. For each transaction, collect all applicable error codes and output at most two, chosen by priority. The priority order is MISSING_FIELD, AMOUNT_OUT_OF_RANGE, BLOCKED_PAYMENT_METHOD, BEHAVIOR_MISMATCH. If no errors apply, output OK. Behavior matching uses the same 3 features and time buckets as Part 3: NIGHT 00:00-05:59, MORNING 06:00-11:59, AFTERNOON 12:00-17:59, EVENING 18:00-23:59.

Constraints

  • 0 <= number of transaction rows <= 100000
  • min_amount <= max_amount and all configured ranges are inclusive.
  • A missing amount can trigger both MISSING_FIELD and AMOUNT_OUT_OF_RANGE.
  • Return at most two error codes per transaction.
  • Rows must be processed in input order.

Examples

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't1,u1,2026-01-22T06:00:00Z,10,US,VISA'], 10, 100, ['PAYPAL'], {'u1': ['US']}, {'u1': ['MORNING']}, {'u1': [10, 10]})

Expected Output: [['t1', 'u1', 'OK']]

Explanation: All integrity, risk, and behavior checks pass. Boundary amounts are inclusive.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't2,u2,2026-01-22T13:00:00Z,999,US,PAYPAL'], 0, 100, ['PAYPAL'], {'u2': ['CA']}, {'u2': ['NIGHT']}, {'u2': [0, 10]})

Expected Output: [['t2', 'u2', 'AMOUNT_OUT_OF_RANGE,BLOCKED_PAYMENT_METHOD']]

Explanation: Amount, payment method, and behavior all fail, but only the top two priority errors are returned.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't3,u3,2026-01-22T13:00:00Z, ,US,PAYPAL'], 0, 100, ['PAYPAL'], {'u3': ['US']}, {'u3': ['AFTERNOON']}, {'u3': [0, 100]})

Expected Output: [['t3', 'u3', 'MISSING_FIELD,AMOUNT_OUT_OF_RANGE']]

Explanation: The blank amount is missing and unparseable. The blocked payment method is lower priority and is omitted due to the two-error cap.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method', 't4,u4,2026-01-22T23:00:00Z,50,MX,VISA'], 0, 100, [], {'u4': ['US']}, {'u4': ['MORNING']}, {'u4': [0, 100]})

Expected Output: [['t4', 'u4', 'BEHAVIOR_MISMATCH']]

Explanation: Only one of the three behavior features matches, so the behavior ratio is below 0.5.

Input: (['transaction_id,user_id,timestamp,amount,country,payment_method'], 0, 100, ['PAYPAL'], {}, {}, {})

Expected Output: []

Explanation: There are no transaction rows to report.

Hints

  1. Collect errors in a set, then scan the priority list to choose the first two present errors.
  2. Compute behavior mismatch independently, even if other risk errors are already present; the priority cap decides what is displayed.
Last updated: Jun 19, 2026

Loading coding console...

PracHub

Master your tech interviews with 8,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Assign Reviewers from Changed Files - Stripe (medium)
  • Generate Account Email Notifications - Stripe (medium)
  • Calculate Transaction Fees - Stripe (medium)
  • Build an Account Transfer Ledger - Stripe (medium)
  • Implement Validation and String Compression - Stripe (hard)