Katkat

Calculate transaction fees from CSV records

StripeSoftware Engineer · Technical Screen

Last updated

Implement a fee calculation engine that parses CSV transaction records, applies multi-attribute pricing rules and merchant volume discounts, and accurately rounds fee amounts in cents.

Problem Statement

Payment processors charge merchants transaction fees based on transaction characteristics, card brands, cross-border status, and merchant processing volume. You are tasked with implementing a fee calculation engine that processes an ordered list of CSV records.

Each input record is a comma-separated string with the following columns: transaction_id,merchant_id,amount_cents,card_brand,merchant_country,card_country

Fee Rules

For each transaction, compute the total fee in cents according to the following logic:

  1. Base Percentage Fee:
    • Domestic (merchant_country == card_country): 2.0%
    • Cross-Border (merchant_country != card_country): 3.0%
  2. Card Brand Surcharge:
    • AMEX: add 1.0% to the percentage fee.
    • VISA and MASTERCARD: add 0.0%.
  3. Volume Discount:
    • If the merchant's cumulative processed volume (sum of amount_cents of previously processed transactions in the current run) is at least 1,000,000 cents ($10,000), deduct 0.5% from the percentage fee.
    • The discount applies to subsequent transactions, not the transaction that crossed the threshold.
  4. Fixed Fee:
    • A flat fee of 30 cents applies to every transaction.
  5. Calculation & Rounding:
    • percentage_fee = amount_cents * (effective_rate / 100.0)
    • Round percentage_fee to the nearest integer cent using half-up rounding (i.e., floor(percentage_fee + 0.5)).
    • total_fee = rounded_percentage_fee + 30

Implement the function calculate_transaction_fees(records: list[str]) -> list[tuple[str, int]] returning a list of (transaction_id, fee_cents) in the same order as the input.

Interface

def calculate_transaction_fees(records: list[str]) -> list[tuple[str, int]]:
    pass

Example

Input

records = [
    "txn_1,m_1,10000,VISA,US,US",
    "txn_2,m_1,5000,AMEX,US,CA",
    "txn_3,m_2,20000,MASTERCARD,GB,GB"
]

Output

[
    ("txn_1", 230),
    ("txn_2", 230),
    ("txn_3", 430)
]

Explanation

  • txn_1: Domestic VISA. Rate = 2.0%. Prior volume = 0. Fee = round(10000 * 0.02) + 30 = 200 + 30 = 230. Merchant m_1 cumulative volume becomes 10000.
  • txn_2: Cross-border AMEX. Rate = 3.0% + 1.0% = 4.0%. Prior volume = 10000 < 1000000, so no discount. Fee = round(5000 * 0.04) + 30 = 200 + 30 = 230.
  • txn_3: Domestic MC for m_2. Rate = 2.0%. Fee = round(20000 * 0.02) + 30 = 400 + 30 = 430.

Constraints

  • 1 <= len(records) <= 100,000
  • 1 <= amount_cents <= 100,000,000 ($1,000,000.00)
  • card_brand is one of {"VISA", "MASTERCARD", "AMEX"}.
  • All country codes are 2-letter uppercase ISO strings.
  • Transaction IDs are unique.
  • The input contains no header row.

Hints

Hint 1

Track running volume per merchant using a hash map before calculating each transaction's applicable rate, updating it only after the fee is computed.

Hint 2

Be careful to represent percentage rates accurately; working with basis points (1% = 100 bps) or standard floating-point numbers can avoid precision drift before the half-up rounding step.

Hint 3

Half-up rounding for non-negative numbers can be implemented as `int(math.floor(val + 0.5))` or using Python's `decimal` module with `ROUND_HALF_UP`.

Follow-up questions

  • How would you refactor the fee rules to be easily configurable via external rule definitions (e.g., JSON or database) without modifying application code?
  • How would your design change if transactions could arrive out of order and include an event timestamp?
  • If transaction volume spans multiple months, how would you handle resetting merchant tiers at the start of each billing cycle?