Process multi-currency payments with fees
Company: Stripe
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates a software engineer's competency in precise integer arithmetic and rounding semantics, multi-currency FX conversion with basis-point fees, resource allocation across balances, and maintaining transactional correctness under ordered updates.
Part 1: Process Same-Currency Payments with Method Fees
Constraints
- 0 <= number of payments <= 100000
- 1 <= number of currencies <= 50
- 0 <= amount <= 10^18
- 0 <= method fee basis points <= 10000
- Every payment method appears in `method_fee_bps`
- All balances and total costs fit in signed 64-bit integers
Examples
Input: ({'USD': 10000, 'EUR': 5000}, {'card': 250, 'bank': 0}, ['p1', 'p2', 'p3'], [1000, 9000, 5000], ['USD', 'USD', 'EUR'], ['card', 'card', 'bank'])
Expected Output: (['SUCCESS', 'FAIL', 'SUCCESS'], {'USD': 8975, 'EUR': 0})
Explanation: p1 costs 1000 + ceil(2.5%) = 1025, so it succeeds. p2 costs 9225, but USD has only 8975 left, so it fails. p3 costs 5000 and uses all EUR.
Input: ({'USD': 101}, {'card': 1}, ['p1'], [100], ['USD'], ['card'])
Expected Output: (['SUCCESS'], {'USD': 0})
Explanation: The fee is ceil(100 * 1 / 10000) = 1, so the total cost is exactly 101.
Input: ({'USD': 500}, {'card': 100}, [], [], [], [])
Expected Output: ([], {'USD': 500})
Explanation: There are no payments, so the result list is empty and balances are unchanged.
Input: ({'USD': 100}, {'bank': 0}, ['p1'], [50], ['EUR'], ['bank'])
Expected Output: (['FAIL'], {'USD': 100})
Explanation: The wallet has no EUR balance, so the EUR payment fails even though USD exists.
Hints
- Use integer ceiling division: `ceil(a / b) = (a + b - 1) // b` for non-negative integers.
- Process payments one by one and mutate a copy of the balances only when a payment succeeds.
Part 2: Process Multi-Currency Payments with Direct FX Conversions
Constraints
- 0 <= number of payments <= 100000
- 1 <= number of currencies <= 50
- 0 <= amount <= 10^18
- 0 <= method fee basis points <= 10000
- 0 <= conversion fee basis points <= 10000
- FX rate numerators and denominators are positive integers
- Only direct FX rates may be used; no multi-hop conversion
- Every payment method appears in `method_fee_bps`
- All balances and final results fit in signed 64-bit integers; use wide integer arithmetic for products in fixed-width languages
Examples
Input: ({'USD': 10000, 'EUR': 1000}, {'card': 250, 'bank': 0}, ['p1', 'p2'], [1000, 9500], ['USD', 'USD'], ['card', 'bank'], {'EUR': {'USD': (2, 1)}}, 1000)
Expected Output: (['SUCCESS', 'SUCCESS'], {'USD': 0, 'EUR': 710})
Explanation: p1 costs 1025 USD and succeeds from USD. p2 needs 525 extra USD. EUR->USD is 2:1, so covering 525 USD needs ceil(525/2)=263 EUR principal. The 10% conversion fee is ceil(26.3)=27 EUR, so EUR is debited by 290.
Input: ({'USD': 100, 'EUR': 200, 'GBP': 300}, {'card': 100}, ['p1'], [500], ['USD'], ['card'], {'EUR': {'USD': (1, 1)}, 'GBP': {'USD': (2, 1)}}, 0)
Expected Output: (['SUCCESS'], {'USD': 0, 'EUR': 0, 'GBP': 197})
Explanation: The total USD cost is 505, so the shortage is 405. Sources are tried in lexicographic order: EUR covers 200, then GBP covers the remaining 205 using ceil(205/2)=103 GBP.
Input: ({'USD': 100, 'EUR': 100}, {'bank': 0}, ['p1', 'p2'], [500, 100], ['USD', 'EUR'], ['bank', 'bank'], {'EUR': {'USD': (1, 1)}, 'USD': {'EUR': (1, 1)}}, 0)
Expected Output: (['FAIL', 'SUCCESS'], {'USD': 100, 'EUR': 0})
Explanation: p1 cannot be funded because only 100 EUR can be converted toward the 400 USD shortage, so balances remain unchanged. p2 then succeeds using the existing 100 EUR.
Input: ({'USD': 0, 'EUR': 102}, {'card': 1}, ['p1'], [100], ['USD'], ['card'], {'EUR': {'USD': (1, 1)}}, 1)
Expected Output: (['SUCCESS'], {'USD': 0, 'EUR': 0})
Explanation: The method fee is ceil(100 * 1 / 10000) = 1, so 101 USD is needed. Converting 101 EUR principal has conversion fee ceil(101 * 1 / 10000) = 1, debiting all 102 EUR.
Input: ({'USD': 123}, {'card': 250}, [], [], [], [], {}, 100)
Expected Output: ([], {'USD': 123})
Explanation: There are no payments, so balances remain unchanged.
Hints
- For a source balance `b`, the largest source principal `x` you can convert satisfies `x + ceil(x * feeBps / 10000) <= b`. This is equivalent to `x * (10000 + feeBps) <= 10000 * b`.
- Plan conversions first without mutating balances. Only apply the planned debits if the payment can be fully covered.