Invoice / Payment Reconciliation
Reported by candidates from Stripe's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.
Stripe reported this one in September 2026, and the problem text never states an input size. That's the tell. The invoice list is small, so brute force isn't the danger. The danger is parsing and priority order. You reconcile one payment against open invoices using three tiers: ID in the memo, exact amount, then fuzzy amount within forgiveness. It's a string-parsing simulation with a tie-break on due date. If you blank on the details during the OA, StealthCoder runs invisibly as a safety net and reads the problem for you. Know the tier order cold and you're mostly done.
The problem
You are reconciling a single incoming payment against a list of open invoices. Decide which invoice the payment settles by applying matching rules in strict priority order, and return a human-readable result string.
Input format (all values are CSV-style strings):
payment has the form "payment-id, amount, memo". The memo may itself contain commas. amount is an integer number of cents.
each invoice has the form "invoice-id, due-date, amount" where due-date is YYYY-MM-DD and amount is an integer number of cents.
forgiveness is a non-negative integer tolerance in cents (it may be 0).
Parsing the payment: split payment on the separator ", ". The first field is payment-id, the second is amount, and everything after that, re-joined with ", ", is the memo (so a comma inside the memo does not corrupt it).
Matching rules (apply strictly in this order; the first tier that yields a candidate wins):
ID match (highest priority): scan the memo case-insensitively for the marker paying for: or paying off:. If present, the invoice id is the text after the marker, trimmed (preserve its original casing). If an invoice has that id, it matches even if the amount disagrees.
Exact amount match: if no id match, match invoices whose amount equals the payment amount exactly.
Fuzzy amount match (lowest priority, only when forgiveness > 0): match invoices whose amount lies within the inclusive range [amount - forgiveness, amount + forgiveness]. A difference exactly equal to forgiveness still qualifies. Skip any invoice whose amount equals the payment exactly (those belong to the exact tier).
Within whichever tier produces candidates, if more than one invoice qualifies, pick the one with the earliest due-date. Because dates are YYYY-MM-DD, ordinary string comparison orders them correctly.
Output: on a match return "Payment {payment-id} paid {amount} for invoice {invoice-id} due on {date}". If no tier matches, return "Payment {payment-id} could not be matched to any invoice".
Function
reconcilePayment(payment: String, invoices: String[], forgiveness: int) → String
Examples
Example 1
payment = "pay_1, 100, paying for: INV-7"
invoices = ["INV-7, 2026-03-01, 250", "INV-3, 2026-02-01, 100"]
forgiveness = 0
return = "Payment pay_1 paid 100 for invoice INV-7 due on 2026-03-01"
The memo contains the marker 'paying for:' followed by INV-7, so the ID tier wins immediately. The ID match settles INV-7 even though its amount (250) does not equal the payment amount (100).
Example 2
payment = "pay_2, 100, March rent, thanks"
invoices = ["INV-9, 2026-04-01, 100", "INV-4, 2026-01-15, 100"]
forgiveness = 0
return = "Payment pay_2 paid 100 for invoice INV-4 due on 2026-01-15"
There is no marker in the memo, so the ID tier is skipped. Both invoices match the amount exactly (100). The tie is broken by earliest due-date, so INV-4 (2026-01-15) wins over INV-9 (2026-04-01). Note the memo contained a comma and was parsed as a single memo field.
Example 3
payment = "pay_3, 98, bank transfer"
invoices = ["INV-1, 2026-05-01, 100", "INV-2, 2026-06-01, 130"]
forgiveness = 2
return = "Payment pay_3 paid 98 for invoice INV-1 due on 2026-05-01"
No marker and no exact-amount invoice (none equal 98). With forgiveness = 2, INV-1 qualifies because its amount 100 is within [96, 100]; the difference is exactly 2, which is inclusive. INV-2 (130) is outside the range. INV-1 wins.
Example 4
payment = "pay_4, 500, no matching invoice here"
invoices = ["INV-1, 2026-05-01, 100", "INV-2, 2026-06-01, 130"]
forgiveness = 0
return = "Payment pay_4 could not be matched to any invoice"
No marker, no exact-amount match, and forgiveness is 0 so the fuzzy tier never runs. Nothing matches, so the unmatched message is returned.
Constraints
payment is formatted as "payment-id, amount, memo"; the memo may contain ", " separators and should be re-joined.
Each invoice is formatted as "invoice-id, due-date, amount" with due-date in YYYY-MM-DD.
All amounts are integers (cents). forgiveness >= 0.
The fuzzy range is inclusive on both ends; a difference exactly equal to forgiveness qualifies.
Tiers are evaluated strictly in order ID > exact > fuzzy; an earlier tier short-circuits later tiers.
Within a tier, ties are broken by the earliest due-date using lexicographic string comparison.Reported by candidates. Source: FastPrep
Pattern and pitfall
The trick is to treat it as three sequential filters, not one scoring pass. Parse the payment by splitting on ", ", taking the first two fields, and re-joining the rest as the memo. Then find the marker "paying for:" or "paying off:" case-insensitively, but keep the invoice id's original casing from the memo. If an invoice has that id, return it even when the amount disagrees. Otherwise collect exact amount matches. Otherwise, only if forgiveness is above 0, collect invoices within the inclusive range and skip exact matches. In each tier, pick the earliest due date with plain string comparison. Common pitfalls: lowercasing the id before lookup, using a strict inequality on the fuzzy boundary, and letting an ID that doesn't exist block the later tiers. If it doesn't exist, fall through. StealthCoder is the hedge if the live OA scrambles your memory of the tier rules.
If you see this problem in your OA tomorrow, the play is to recognize the pattern in 30 seconds. StealthCoder buys you that recognition.
You can drill Invoice / Payment Reconciliation cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Built by an Amazon engineer who passed his OA cold and still thinks the filter is broken.
Get StealthCoderRelated leaked OAs
You've seen the question.
Make sure you actually pass Stripe's OA.
Stripe reuses patterns across OAs. Built by an Amazon engineer who passed his OA cold and still thinks the filter is broken. Works on HackerRank, CodeSignal, CoderPad, and Karat.
Invoice / Payment Reconciliation FAQ
How hard is the Stripe invoice reconciliation question really?+
It's easy on algorithms and tricky on details. There's no clever data structure. You lose points on parsing the memo with commas, the tier order, inclusive fuzzy bounds, and the exact output string. Read the examples carefully and it's a medium-easy simulation.
What's the trick to the matching order?+
Run the tiers in strict order and stop at the first tier that yields a candidate. ID first, then exact amount, then fuzzy only if forgiveness is above 0. Within a tier, sort or scan for the earliest due date. Don't score everything together.
How do I parse the payment when the memo has commas?+
Split on the two-character separator ", ". Field 0 is the payment id, field 1 is the amount as an integer, and everything from index 2 onward gets joined back with ", " to form the memo. Never assume exactly three parts.
What edge cases should I test before submitting?+
Test an ID that matches an invoice with a different amount. Test an ID that matches nothing, which should fall through to the amount tiers. Test a difference exactly equal to forgiveness, forgiveness of 0, an exact-amount invoice that must be skipped in the fuzzy tier, and ties on amount with different due dates.
How do I prepare for this in 48 hours?+
Write the solution once from scratch in your strongest language. Practice splitting strings, case-insensitive marker search that preserves original casing, and tie-breaking by min date. Then run all four examples by hand. That covers nearly everything this problem can throw at you.