Merchant Balances After Chargebacks
MediumData Processing · Stripe
Compute merchant balances from a chronological stream of payment events.
Each event is [type, merchant_id, payment_id, amount] where type is "charge", "refund", or "chargeback". Refund and chargeback events always carry the same merchant_id as the original charge. Process events in order:
- charge: the first charge for a
payment_idaddsamountto the merchant's balance and opens the payment withamountremaining. A repeated charge for an existingpayment_idis ignored. - refund: subtracts
min(amount, remaining)from the merchant and from the payment's remaining amount. A refund larger than what is left only refunds what is left. - chargeback: subtracts
min(amount, remaining)plus a fixeddispute_fee, then closes the payment. - A refund or chargeback for an unknown
payment_id, or for a payment that is already closed, is ignored (no fee).
Return [merchant_id, balance] for every merchant whose final balance is non-zero, sorted by merchant_id. Balances may be negative.
Example 1
- Input:
- events = [["charge", "m1", "p1", 200], ["charge", "m2", "p2", 80], ["refund", "m1", "p1", 50], ["chargeback", "m1", "p1", 200], ["refund", "m1", "p1", 10]], dispute_fee = 15
- Output:
- [["m1", -15], ["m2", 80]]
- Explanation
- m1 is charged 200 and refunds 50, leaving 150 remaining. The chargeback asks for 200 but only 150 is left, so m1 loses 150 plus the 15 fee and ends at -15. The later refund hits a closed payment and is ignored. m2 keeps its 80.
Example 2
- Input:
- events = [["charge", "a", "x", 100], ["charge", "a", "x", 100], ["refund", "a", "x", 60], ["refund", "a", "x", 60]], dispute_fee = 25
- Output:
- []
- Explanation
- The second charge reuses payment
xand is ignored, so only 100 is added. The first refund takes 60 and the second can only take the remaining 40, leavingaat 0, which is not reported.
Example 3
- Input:
- events = [["chargeback", "b", "zz", 30], ["charge", "b", "y", 30]], dispute_fee = 10
- Output:
- [["b", 30]]
- Explanation
- The chargeback arrives before any charge for
zzexists, so it is ignored and no fee is taken. Only the charge of 30 counts.
Constraints
0 <= events.length <= 10^5typeis"charge","refund", or"chargeback"0 <= amount <= 10^9,0 <= dispute_fee <= 10^4- Refunds and chargebacks name the same merchant as their charge