PracHub
QuestionsPremiumLearningGuidesCheatsheetNEWCoaches

Quick Overview

This question evaluates parsing and processing of structured transaction data, implementing multi-dimension fee computation rules (provider-, transaction-type-, and country-based), maintaining per-merchant state for discounts and volume tracking, and precise numeric handling including rounding.

  • medium
  • TikTok
  • Coding & Algorithms
  • Software Engineer

Calculate transaction fees from CSV records

Company: TikTok

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

You are given a CSV-like multi-line string representing transactions with columns: `id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status` You must compute and print fee lines in the format: `id, transaction_type, payment_provider, fee` with one output line per input row (excluding header), preserving order. ## Part 1 — Provider-based fee You are given a map `provider_fee_rules` describing how to compute a fee for each `payment_provider`. Rules: - If `status` is not a successful status (e.g., `payment_failed`), fee is `0`. - Otherwise fee is computed based on the provider’s rule (e.g., percentage + fixed, or tiered). The exact rule structure will be provided in the interview. ## Part 2 — Support multiple transaction types Extend Part 1 so that fee may depend on both `transaction_type` and `payment_provider`. ## Part 3 — Country-based fee schedule with volume discounts You are given: - A map `country_fee_rules[buyer_country] -> fee rule` (may override/extend provider rules). - A threshold `T` (number of successful transactions) after which a merchant receives a discount. - A discount definition (e.g., reduce percentage by X, or apply multiplier `d < 1`). Requirements: - Discounts are computed **per merchant** (`merchant_id`) based on that merchant’s cumulative successful transaction count (or volume) up to the current transaction. - Output the computed fee for each row. ## Notes - Specify how you parse `amount` (integer smallest unit) and how you round fees. - Aim for an efficient single pass over the rows while maintaining per-merchant aggregates.

Quick Answer: This question evaluates parsing and processing of structured transaction data, implementing multi-dimension fee computation rules (provider-, transaction-type-, and country-based), maintaining per-merchant state for discounts and volume tracking, and precise numeric handling including rounding.

Part 1: Provider-based fee calculation from CSV

You are given a CSV-like multiline string with header `id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status`. Return one fee line per data row in the format `id,transaction_type,payment_provider,fee`, preserving input order and skipping the header. The fee depends only on `payment_provider`. `amount` is already an integer in the smallest currency unit. If `status` is not in `successful_statuses`, the fee is `0`. Otherwise apply the provider's rule. Supported rule types are: `flat` -> constant fee; `percent_fixed` -> round-half-up(`amount * percent_bps / 10000`) + `fixed`; `tiered` -> choose the first tier whose `max_amount` is `None` or at least the transaction amount, and use that tier's flat fee.

Constraints

  • 0 <= number of data rows <= 100000
  • 0 <= amount <= 10^9
  • All transaction rows have exactly 10 comma-separated fields
  • No field contains embedded commas or quoted commas
  • Every `payment_provider` appearing in the CSV exists in `provider_fee_rules`
  • For `tiered` rules, tiers are ordered by increasing `max_amount` and at least one tier matches every amount

Examples

Input: ('id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status\n1,r1,1000,USD,2024-01-01,m1,US,payment,stripe,captured\n2,r2,2500,USD,2024-01-02,m1,US,payment,paypal,captured\n3,r3,700,USD,2024-01-03,m2,CA,refund,bank,payment_failed', {'stripe': {'type': 'percent_fixed', 'percent_bps': 290, 'fixed': 30}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 50), (5000, 120), (None, 250)]}, 'bank': {'type': 'flat', 'fee': 15}}, {'captured', 'settled'})

Expected Output: ['1,payment,stripe,59', '2,payment,paypal,120', '3,refund,bank,0']

Explanation: Stripe uses 2.90% + 30, PayPal uses tiers, and failed transactions always produce fee 0.

Input: ('id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status', {'stripe': {'type': 'percent_fixed', 'percent_bps': 290, 'fixed': 30}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 50), (5000, 120), (None, 250)]}, 'bank': {'type': 'flat', 'fee': 15}}, {'captured', 'settled'})

Expected Output: []

Explanation: Header only means there are no transaction rows to process.

Input: ('id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status\n1,r1,1001,USD,2024-01-01,m1,US,payment,paypal,captured\n2,r2,333,USD,2024-01-02,m1,US,payment,stripe,settled\n3,r3,0,USD,2024-01-03,m2,CA,payment,stripe,captured', {'stripe': {'type': 'percent_fixed', 'percent_bps': 290, 'fixed': 30}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 50), (5000, 120), (None, 250)]}, 'bank': {'type': 'flat', 'fee': 15}}, {'captured', 'settled'})

Expected Output: ['1,payment,paypal,120', '2,payment,stripe,40', '3,payment,stripe,30']

Explanation: This checks a tier boundary, percentage rounding half up, and a zero-amount successful transaction.

Input: ('', {'stripe': {'type': 'percent_fixed', 'percent_bps': 290, 'fixed': 30}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 50), (5000, 120), (None, 250)]}, 'bank': {'type': 'flat', 'fee': 15}}, {'captured', 'settled'})

Expected Output: []

Explanation: An empty input string should return an empty result.

Hints

  1. Write one helper that evaluates a single fee rule for a given amount.
  2. You can solve this in one pass over the CSV rows without storing parsed transactions.

Part 2: Fees by transaction type and payment provider

You are given the same transaction CSV schema as in Part 1, but now the fee depends on both `transaction_type` and `payment_provider`. Return one output line per data row in the format `id,transaction_type,payment_provider,fee`, preserving order and skipping the header. If `status` is not in `successful_statuses`, the fee is `0`. Otherwise look up the rule in `fee_rules[transaction_type][payment_provider]` and evaluate it. Supported rule types are `flat`, `percent_fixed`, and `tiered`. `amount` is an integer in the smallest currency unit. For `percent_fixed`, round the percentage component half up before adding the fixed part.

Constraints

  • 0 <= number of data rows <= 100000
  • 0 <= amount <= 10^9
  • All transaction rows have exactly 10 comma-separated fields
  • No field contains embedded commas or quoted commas
  • Every `(transaction_type, payment_provider)` pair in the CSV exists in `fee_rules`
  • For `tiered` rules, tiers are ordered by increasing `max_amount` and at least one tier matches every amount

Examples

Input: ('id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status\n1,r1,1000,USD,2024-01-01,m1,US,payment,stripe,captured\n2,r2,3000,USD,2024-01-02,m1,US,refund,stripe,processed\n3,r3,500,USD,2024-01-03,m2,CA,refund,paypal,processed\n4,r4,2000,USD,2024-01-04,m2,CA,payment,paypal,payment_failed\n5,r5,900,USD,2024-01-05,m3,GB,payout,paypal,settled', {'payment': {'stripe': {'type': 'percent_fixed', 'percent_bps': 250, 'fixed': 20}, 'paypal': {'type': 'flat', 'fee': 70}}, 'refund': {'stripe': {'type': 'flat', 'fee': 15}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 20), (None, 35)]}}, 'payout': {'stripe': {'type': 'flat', 'fee': 40}, 'paypal': {'type': 'percent_fixed', 'percent_bps': 100, 'fixed': 10}}}, {'captured', 'settled', 'processed'})

Expected Output: ['1,payment,stripe,45', '2,refund,stripe,15', '3,refund,paypal,20', '4,payment,paypal,0', '5,payout,paypal,19']

Explanation: The same provider can have different fees for different transaction types.

Input: ('id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status', {'payment': {'stripe': {'type': 'percent_fixed', 'percent_bps': 250, 'fixed': 20}, 'paypal': {'type': 'flat', 'fee': 70}}, 'refund': {'stripe': {'type': 'flat', 'fee': 15}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 20), (None, 35)]}}, 'payout': {'stripe': {'type': 'flat', 'fee': 40}, 'paypal': {'type': 'percent_fixed', 'percent_bps': 100, 'fixed': 10}}}, {'captured', 'settled', 'processed'})

Expected Output: []

Explanation: Header only is an important edge case.

Input: ('id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status\n1,r1,1000,USD,2024-01-01,m1,US,refund,paypal,processed\n2,r2,1001,USD,2024-01-02,m1,US,refund,paypal,processed\n3,r3,0,USD,2024-01-03,m2,CA,payment,stripe,settled', {'payment': {'stripe': {'type': 'percent_fixed', 'percent_bps': 250, 'fixed': 20}, 'paypal': {'type': 'flat', 'fee': 70}}, 'refund': {'stripe': {'type': 'flat', 'fee': 15}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 20), (None, 35)]}}, 'payout': {'stripe': {'type': 'flat', 'fee': 40}, 'paypal': {'type': 'percent_fixed', 'percent_bps': 100, 'fixed': 10}}}, {'captured', 'settled', 'processed'})

Expected Output: ['1,refund,paypal,20', '2,refund,paypal,35', '3,payment,stripe,20']

Explanation: This checks a tier boundary and a zero-amount payment whose fee still includes the fixed component.

Input: ('', {'payment': {'stripe': {'type': 'percent_fixed', 'percent_bps': 250, 'fixed': 20}, 'paypal': {'type': 'flat', 'fee': 70}}, 'refund': {'stripe': {'type': 'flat', 'fee': 15}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 20), (None, 35)]}}, 'payout': {'stripe': {'type': 'flat', 'fee': 40}, 'paypal': {'type': 'percent_fixed', 'percent_bps': 100, 'fixed': 10}}}, {'captured', 'settled', 'processed'})

Expected Output: []

Explanation: An empty CSV string should produce no output lines.

Hints

  1. Keep the same rule-evaluation helper from Part 1; only the rule lookup changes.
  2. Use `fee_rules[transaction_type][payment_provider]` to avoid a long chain of conditionals.

Part 3: Country overrides and merchant volume discounts

You are given the same transaction CSV schema, plus base fee rules by `(transaction_type, payment_provider)`, optional country-specific fee rules, a merchant discount threshold, and a discount multiplier. Return one output line per data row in the format `id,transaction_type,payment_provider,fee`, preserving input order and skipping the header. For each successful transaction: first compute the base fee from `base_fee_rules[transaction_type][payment_provider]`; then, if `buyer_country` exists in `country_fee_rules`, apply that country's rule. Country rule modes are `override` (replace the base fee with the country rule fee) and `add` (add the country rule fee to the base fee). After that, update the merchant's successful transaction count. If the updated count is greater than threshold `T`, apply the discount multiplier `multiplier_num / multiplier_den` to the whole fee and round half up to the nearest integer. Failed transactions always have fee `0` and do not increase a merchant's successful count.

Constraints

  • 0 <= number of data rows <= 100000
  • 0 <= amount <= 10^9
  • 0 <= threshold <= 10^9
  • 1 <= multiplier_num <= multiplier_den <= 10^6
  • All transaction rows have exactly 10 comma-separated fields
  • No field contains embedded commas or quoted commas
  • Every `(transaction_type, payment_provider)` pair in the CSV exists in `base_fee_rules`
  • For every `tiered` rule, tiers are ordered by increasing `max_amount` and at least one tier matches every amount

Examples

Input: ('id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status\n1,r1,1000,USD,2024-01-01,m1,US,payment,stripe,captured\n2,r2,2000,USD,2024-01-02,m1,BR,payment,stripe,captured\n3,r3,500,USD,2024-01-03,m1,DE,refund,paypal,processed\n4,r4,3000,USD,2024-01-04,m2,BR,payment,paypal,payment_failed\n5,r5,1500,USD,2024-01-05,m2,JP,payment,paypal,settled', {'payment': {'stripe': {'type': 'percent_fixed', 'percent_bps': 300, 'fixed': 30}, 'paypal': {'type': 'flat', 'fee': 60}}, 'refund': {'stripe': {'type': 'flat', 'fee': 20}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 25), (None, 40)]}}}, {'DE': {'mode': 'override', 'rule': {'type': 'flat', 'fee': 10}}, 'BR': {'mode': 'add', 'rule': {'type': 'percent_fixed', 'percent_bps': 100, 'fixed': 0}}, 'JP': {'mode': 'add', 'rule': {'type': 'flat', 'fee': 5}}}, 2, {'multiplier_num': 1, 'multiplier_den': 2}, {'captured', 'settled', 'processed'})

Expected Output: ['1,payment,stripe,60', '2,payment,stripe,110', '3,refund,paypal,5', '4,payment,paypal,0', '5,payment,paypal,65']

Explanation: This case combines base rules, country `add` and `override`, per-merchant counting, and a discount that starts on the 3rd successful transaction for merchant `m1`.

Input: ('id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status\n1,r1,1000,USD,2024-01-01,m9,US,payment,stripe,payment_failed\n2,r2,1000,USD,2024-01-02,m9,US,payment,stripe,captured\n3,r3,100,USD,2024-01-03,m9,JP,payment,paypal,settled\n4,r4,500,USD,2024-01-04,m9,DE,refund,paypal,processed', {'payment': {'stripe': {'type': 'percent_fixed', 'percent_bps': 300, 'fixed': 30}, 'paypal': {'type': 'flat', 'fee': 60}}, 'refund': {'stripe': {'type': 'flat', 'fee': 20}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 25), (None, 40)]}}}, {'DE': {'mode': 'override', 'rule': {'type': 'flat', 'fee': 10}}, 'BR': {'mode': 'add', 'rule': {'type': 'percent_fixed', 'percent_bps': 100, 'fixed': 0}}, 'JP': {'mode': 'add', 'rule': {'type': 'flat', 'fee': 5}}}, 1, {'multiplier_num': 1, 'multiplier_den': 2}, {'captured', 'settled', 'processed'})

Expected Output: ['1,payment,stripe,0', '2,payment,stripe,60', '3,payment,paypal,33', '4,refund,paypal,5']

Explanation: The failed transaction does not count toward the threshold. The third row becomes 65 before discount, then rounds half up to 33 after a 1/2 multiplier.

Input: ('id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status\n1,r1,800,USD,2024-01-01,m1,DE,refund,paypal,processed', {'payment': {'stripe': {'type': 'percent_fixed', 'percent_bps': 300, 'fixed': 30}, 'paypal': {'type': 'flat', 'fee': 60}}, 'refund': {'stripe': {'type': 'flat', 'fee': 20}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 25), (None, 40)]}}}, {'DE': {'mode': 'override', 'rule': {'type': 'flat', 'fee': 10}}, 'BR': {'mode': 'add', 'rule': {'type': 'percent_fixed', 'percent_bps': 100, 'fixed': 0}}, 'JP': {'mode': 'add', 'rule': {'type': 'flat', 'fee': 5}}}, 0, {'multiplier_num': 1, 'multiplier_den': 2}, {'captured', 'settled', 'processed'})

Expected Output: ['1,refund,paypal,5']

Explanation: With threshold 0, every successful transaction is discounted immediately.

Input: ('id,reference,amount,currency,date,merchant_id,buyer_country,transaction_type,payment_provider,status', {'payment': {'stripe': {'type': 'percent_fixed', 'percent_bps': 300, 'fixed': 30}, 'paypal': {'type': 'flat', 'fee': 60}}, 'refund': {'stripe': {'type': 'flat', 'fee': 20}, 'paypal': {'type': 'tiered', 'tiers': [(1000, 25), (None, 40)]}}}, {'DE': {'mode': 'override', 'rule': {'type': 'flat', 'fee': 10}}, 'BR': {'mode': 'add', 'rule': {'type': 'percent_fixed', 'percent_bps': 100, 'fixed': 0}}, 'JP': {'mode': 'add', 'rule': {'type': 'flat', 'fee': 5}}}, 2, {'multiplier_num': 1, 'multiplier_den': 2}, {'captured', 'settled', 'processed'})

Expected Output: []

Explanation: Header only means there are no rows to price.

Hints

  1. Keep a dictionary `merchant_id -> successful_count` while scanning rows once from top to bottom.
  2. Apply the country adjustment before the discount, and remember that failed rows do not increment the merchant count.
Last updated: May 11, 2026

Loading coding console...

PracHub

Master your tech interviews with 7,500+ 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

  • Parse a nested list from a string - TikTok (medium)
  • Implement stacks, streaming median, and upward path sum - TikTok (easy)
  • Maximize sum with no adjacent elements - TikTok (medium)
  • Implement stack variants and path-sum check - TikTok (medium)
  • Find the longest palindromic substring - TikTok (easy)