Search
⌘K

Build a Payment Management System

Asked at:

Airbnb

Capital One

Capital One


DESCRIPTION

Build a payment management system that processes transactions with priority ordering (credit > credit card > PayPal) and generates refunds intelligently. For each payment type, the system should only refund the most recent transaction (latest timestamp) until the target refund amount is satisfied. For example, if you have payment1: credit $100 at t=5 and payment2: credit $50 at t=10, and need to refund $120, the system should first refund from payment2 ($50), then from payment1 ($70).

Input:

Payments:
- payment1: credit, $100, timestamp=5
- payment2: credit_card, $80, timestamp=3
- payment3: credit, $50, timestamp=10

Target refund: $120

Output:

Refunds:
- refund1: payment3, $50
- refund2: payment1, $70

Total refunded: $120


Explanation: Credit has highest priority. Among credit payments, payment3 (t=10) is most recent, so refund $50 first. Then refund $70 from payment1 (t=5). Credit card payment is never touched since credit payments satisfied the target.

Constraints:

  • Payment types have fixed priority: credit > credit_card > paypal
  • Within each payment type, process refunds from latest to earliest timestamp
  • Refund amounts cannot exceed the remaining balance of a payment
  • Stop processing once the target refund amount is reached

Understanding the Problem

The core challenge is multi-level sorting: first by payment type priority, then by timestamp within each type. You need to track partial refunds since a payment might be only partially refunded to meet the exact target amount. The system must maintain payment state (original amount vs. remaining balance) as refunds are processed. Additionally, you need to link refunds back to their source payments for audit trails.

Building Intuition

A naive approach would iterate through all payments for each refund request, checking types and timestamps repeatedly—this becomes inefficient with many transactions. A better approach uses a priority queue or sorted structure to pre-organize payments by (priority, -timestamp), allowing you to always pop the next eligible payment in O(log n) time. For example, with payments [credit@t=5, paypal@t=10, credit@t=8], pre-sorting gives [credit@t=8, credit@t=5, paypal@t=10] for instant access.

Real-world payment systems handle millions of transactions with complex business rules (chargebacks, fraud detection, accounting reconciliation). This problem teaches priority-based resource allocation, which applies to inventory management (fulfill orders by customer tier), task scheduling (process high-priority jobs first), and financial systems (settle transactions by risk level). Understanding how to efficiently track and modify transaction state is crucial for building reliable financial software.

Common Pitfalls

Implementation

Payment Data Model and Priority System

Define the core data structures to represent payments with their type, amount, timestamp, and remaining balance. Implement a priority mapping (credit=1, credit_card=2, paypal=3) to enable sorting. Create a Payment class that tracks both original and remaining amounts, allowing partial refunds. For example, a payment with original=$100 and remaining=$100 becomes remaining=$30 after a $70 refund.

Solution
from dataclasses import dataclass
from datetime import datetime
@dataclass
class Payment:
id: str
type: str
amount: float
timestamp: datetime
remaining: float = None
def __post_init__(self):
if self.remaining is None:
self.remaining = self.amount
@property
def priority(self) -> int:
priority_map = {'credit': 1, 'credit_card': 2, 'paypal': 3}
return priority_map.get(self.type, 999)
Payment Sorting and Selection Logic

Build the sorting mechanism that organizes payments by priority first, then by timestamp (descending) within each priority level. Use a custom comparator or tuple-based sorting like (priority, -timestamp, payment_id). This creates a refund queue where the first element is always the next payment to process. For example, [(credit, t=10), (credit, t=5), (credit_card, t=8)] ensures credit payments are exhausted before touching credit cards.

Solution
from typing import List
def sort_payments_for_refund(payments: List[Payment]) -> List[Payment]:
"""Sort payments by priority, then by timestamp descending."""
return sorted(
payments,
key=lambda p: (p.priority, -p.timestamp.timestamp(), p.id)
)
def get_refund_queue(payments: List[Payment]) -> List[Payment]:
"""Create refund queue with only latest payment per type."""
latest_by_type = {}
for payment in payments:
if payment.type not in latest_by_type or payment.timestamp > latest_by_type[payment.type].timestamp:
latest_by_type[payment.type] = payment
return sort_payments_for_refund(list(latest_by_type.values()))
Refund Processing Engine

Implement the refund generation algorithm that iterates through the sorted payment queue and allocates refunds. For each payment, calculate refund_amount = min(remaining_balance, remaining_target), create a refund record linking to the payment, and update both the payment's balance and the remaining target. Stop when remaining_target reaches zero. For example, if target=$120 and the first payment has $50, refund $50, update target to $70, and continue to the next payment.

Solution
@dataclass
class Refund:
id: str
payment_id: str
amount: float
def generate_refunds(payments: List[Payment], target_amount: float) -> List[Refund]:
"""Generate refunds from payment queue until target is met."""
refund_queue = get_refund_queue(payments)
refunds = []
remaining_target = target_amount
refund_counter = 1
for payment in refund_queue:
if remaining_target <= 0:
break
refund_amount = min(payment.remaining, remaining_target)
refunds.append(Refund(f"refund{refund_counter}", payment.id, refund_amount))
payment.remaining -= refund_amount
remaining_target -= refund_amount
refund_counter += 1
return refunds
Complete Payment Management System

Integrate the Payment model, sorting logic, and refund engine into a unified PaymentManager class. Provide methods like add_payment() to register transactions and process_refunds(target_amount) to generate refund records. The manager maintains the payment collection, handles sorting on-demand or via a priority queue, and returns a list of Refund objects with payment references and amounts. For example, manager.process_refunds(150) returns [Refund(payment3, $50), Refund(payment1, $100)] showing the complete refund breakdown.

Solution
class PaymentManager:
def __init__(self):
self.payments = []
def add_payment(self, payment: Payment) -> None:
"""Register a new payment transaction."""
self.payments.append(payment)
def process_refunds(self, target_amount: float) -> List[Refund]:
"""Generate refunds for the target amount using priority logic."""
return generate_refunds(self.payments, target_amount)

What We've Learned

  • Pattern: Use multi-level sorting (priority + timestamp) to create a deterministic processing order for complex business rules
  • Use Case: Apply to resource allocation systems where items have both category priorities and time-based ordering (inventory fulfillment, task scheduling, financial settlements)
  • Technique: Track mutable state (remaining balances) separately from immutable data (original amounts) to support partial operations and audit trails
  • Optimization: Consider priority queues (heaps) for dynamic scenarios where payments are added continuously during refund processing

Problems to Practice

Overview
Lesson
Heap

The payment management system requires processing transactions with priority ordering (credit, credit card, PayPal) and selecting items by timestamp. Heaps are ideal for priority-based processing and efficiently finding maximum/minimum elements, which directly applies to this problem's priority queue requirements.

This problem involves managing multiple sorted streams (similar to multiple payment types) and efficiently selecting elements based on priority. The pattern of using a heap with hashmap tracking is directly applicable to managing refunds across different payment types with timestamp ordering.

Overview
Lesson
Intervals

Payment transactions with timestamps can be modeled as intervals, and processing refunds for the latest timestamp involves interval manipulation. Understanding interval concepts helps with timestamp-based operations and managing overlapping or sequential payment events.

Question Timeline

See when this question was last asked and where, including any notes left by other candidates.

Early January, 2026

Capital One

Capital One

Senior

Early July, 2025

Airbnb

Senior

Mid June, 2025

Airbnb

Senior

Comments

Your account is free and you can post anonymously if you choose.