Invoice / Payment Reconciliation
Problem statement
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):
paymenthas the form"payment-id, amount, memo". Thememomay itself contain commas.amountis an integer number of cents.- each invoice has the form
"invoice-id, due-date, amount"wheredue-dateisYYYY-MM-DDandamountis an integer number of cents. forgivenessis a non-negative integer tolerance in cents (it may be0).
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
memocase-insensitively for the markerpaying for:orpaying 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
amountequals the paymentamountexactly. - 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 toforgivenessstill 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) → StringExamples
Example 1
payment = "pay_1, 100, paying for: INV-7"invoices = ["INV-7, 2026-03-01, 250", "INV-3, 2026-02-01, 100"]forgiveness = 0return = "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 = 0return = "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 = 2return = "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 = 0return = "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
paymentis 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"withdue-dateinYYYY-MM-DD. - All amounts are integers (cents).
forgiveness >= 0. - The fuzzy range is inclusive on both ends; a difference exactly equal to
forgivenessqualifies. - 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-dateusing lexicographic string comparison.