Match payments to invoices with fallback rules
StripeSoftware Engineer · Technical ScreenLast updated
This problem tests the ability to implement a multi-pass matching algorithm that prioritizes exact identifier matches before applying fuzzy numeric heuristics.
Problem Statement
You are building a reconciliation engine for a payment processor. Given a list of payments and a list of invoices, your goal is to match each payment to a corresponding invoice.
Each payment and invoice has an id and an amount. A payment can only be matched to one invoice, and an invoice can only be matched to one payment. Implement a function match_payments(payments, invoices) that follows these priority rules:
- Exact Match: Match by
idif IDs match exactly and amounts match exactly. - Amount-Only Match: If ID matching fails, match by
amountif there is a unique invoice with that amount. - Fuzzy Amount Match: If multiple invoices have the same amount, or no invoice matches the amount, try to match by a 'tolerance' window (e.g., if the payment is within +/- 1% of the invoice amount). If multiple fit the tolerance, prioritize the closest amount.
Function Signature
def match_payments(payments, invoices):
# payments: list of dicts {'id': str, 'amount': float}
# invoices: list of dicts {'id': str, 'amount': float}
# return: list of tuples (payment_id, invoice_id) for successful matches
Example
Input:
payments = [{'id': 'p1', 'amount': 100.0}, {'id': 'p2', 'amount': 205.0}]
invoices = [{'id': 'i1', 'amount': 100.0}, {'id': 'i2', 'amount': 200.0}]
Output:
[('p1', 'i1'), ('p2', 'i2')]
(p2 matches i2 because 205 is within a 3% tolerance window of 200)
Constraints
- Number of payments and invoices: up to 10,000 each.
- Amounts are non-negative floats.
- If a payment cannot be matched, exclude it from the final result.
Edge Cases
- Empty lists.
- Multiple invoices having the exact same amount.
- Floating point precision issues (use a small epsilon for comparisons).
Hints
Hint 1
Use HashMaps (dictionaries) to pre-index invoices by ID and by amount for O(1) lookups during the first two passes.
Hint 2
For the fuzzy matching phase, consider sorting the remaining unmatched invoices by amount to use binary search or a two-pointer approach to find the nearest value within the tolerance window.
Hint 3
Always remove matched objects from your index/pool immediately to ensure one-to-one mapping integrity.
Follow-up questions
- How would you modify your solution if the input lists were too large to fit in memory (e.g., millions of records)?
- How does your implementation handle potential floating-point arithmetic errors when comparing payment and invoice amounts?
- If the matching requirements changed to include a 'date' proximity constraint (e.g., invoice must be issued within 30 days of payment), how would that change your data structures?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.