Allocate refund across payments
Company: Airbnb
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Onsite
##### Question
Given a list of completed payment transactions (each with payment method, date, and amount) and a refund amount R,
write an algorithm that issues refunds according to the rules:
Always refund in full from one payment before moving to the next.
Prefer payment methods in the priority order CREDIT → CREDIT_CARD → PAYPAL.
Within the same method, refund the most recent payment first.
Return the list of refund allocations (payment id, method, amount). Explain the algorithm’s time complexity and data structures.
Quick Answer: This question evaluates algorithm design and data-structure selection skills, focusing on greedy allocation, ordering by priority and recency, aggregation of transaction amounts, and analysis of time complexity.
You are given a list of completed payment transactions and a refund amount R. Each payment is a dictionary with fields: id (string or integer), method (one of CREDIT, CREDIT_CARD, PAYPAL), ts (integer UNIX timestamp), and amount (integer in smallest currency units, e.g., cents). Allocate the refund according to these rules: (1) Prefer methods in the priority order CREDIT → CREDIT_CARD → PAYPAL; (2) Within the same method, refund the most recent payment first (larger ts first); (3) Always refund a payment in full before moving to the next; if the remaining refund is less than the next payment amount, refund the remaining amount partially from that payment and stop; (4) Do not exceed the total available amount across all payments. Return the list of allocations in the order processed, where each allocation is a dictionary with id, method, and amount fields indicating how much was refunded from that payment.
Constraints
- 1 <= len(payments) <= 200000
- 0 <= refund <= 10^12
- For each payment: method ∈ {CREDIT, CREDIT_CARD, PAYPAL}
- For each payment: amount is an integer (0 <= amount <= 10^12)
- For each payment: ts is an integer UNIX timestamp (0 <= ts <= 10^12)
- Inputs are not pre-sorted
- If refund exceeds total available amount, refund only the available total
- Return allocations in the order they are applied
Solution
def allocate_refund(payments, refund):
priority = {'CREDIT': 0, 'CREDIT_CARD': 1, 'PAYPAL': 2}
# Filter to recognized methods and positive amounts
filtered = [p for p in payments if p.get('method') in priority and p.get('amount', 0) > 0]
# Sort by method priority, then by timestamp descending (most recent first)
filtered.sort(key=lambda p: (priority[p['method']], -p['ts']))
remaining = refund
allocations = []
for p in filtered:
if remaining <= 0:
break
amt = p['amount']
if amt <= 0:
continue
take = amt if amt <= remaining else remaining
if take > 0:
allocations.append({'id': p['id'], 'method': p['method'], 'amount': take})
remaining -= take
return allocations