Bird
0
0
LLDsystem_design~7 mins

Payment handling in LLD - System Design Guide

Choose your learning style9 modes available
Problem Statement
Handling payments directly without proper abstraction leads to tightly coupled code that is hard to maintain and extend. If payment logic is scattered, adding new payment methods or handling failures becomes error-prone and slows down development.
Solution
Payment handling uses a strategy pattern to separate payment methods into interchangeable components. This allows the system to select and execute different payment processes dynamically without changing the core logic. It also centralizes error handling and logging for payments.
Architecture
PaymentClient
PaymentProcessor
CreditCardPay

This diagram shows a PaymentClient using a PaymentProcessor interface to delegate payment execution to different payment method implementations like CreditCardPay, PayPalPay, and CryptoPay.

Trade-offs
✓ Pros
Enables easy addition of new payment methods without changing existing code.
Improves code maintainability by isolating payment logic.
Centralizes error handling and logging for all payment types.
Supports dynamic selection of payment methods at runtime.
✗ Cons
Introduces additional abstraction layers which may increase initial complexity.
Requires careful interface design to cover all payment method needs.
May add slight performance overhead due to indirection.
Use when your system supports multiple payment methods or expects to add more in the future, especially if payment logic is complex or needs to be isolated for security and compliance.
Avoid if your system only supports a single, simple payment method with minimal logic, as the abstraction overhead may not justify the benefits.
Real World Examples
Stripe
Stripe uses a modular payment processing system to support many payment methods like cards, wallets, and bank debits, allowing seamless integration and extension.
PayPal
PayPal abstracts payment methods to handle credit cards, PayPal balance, and other funding sources uniformly, simplifying transaction management.
Uber
Uber uses a payment handling system that dynamically selects payment methods based on user preferences and region, ensuring smooth ride payments.
Code Example
The before code mixes payment logic in one method, making it hard to add new methods. The after code uses the strategy pattern to separate payment methods into classes implementing a common interface. PaymentProcessor delegates payment to the selected strategy, enabling easy extension and cleaner code.
LLD
### Before: tightly coupled payment handling
class PaymentHandler:
    def pay(self, method, amount):
        if method == 'credit_card':
            # process credit card payment
            print(f"Processing credit card payment of {amount}")
        elif method == 'paypal':
            # process paypal payment
            print(f"Processing PayPal payment of {amount}")
        else:
            raise ValueError("Unsupported payment method")

handler = PaymentHandler()
handler.pay('credit_card', 100)


### After: strategy pattern for payment handling
from abc import ABC, abstractmethod

class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

class CreditCardPayment(PaymentStrategy):
    def pay(self, amount):
        print(f"Processing credit card payment of {amount}")

class PayPalPayment(PaymentStrategy):
    def pay(self, amount):
        print(f"Processing PayPal payment of {amount}")

class PaymentProcessor:
    def __init__(self, strategy: PaymentStrategy):
        self.strategy = strategy

    def pay(self, amount):
        self.strategy.pay(amount)

processor = PaymentProcessor(CreditCardPayment())
processor.pay(100)

processor.strategy = PayPalPayment()
processor.pay(200)
OutputSuccess
Alternatives
Monolithic Payment Logic
All payment methods are handled in a single code block without abstraction.
Use when: Choose when the system only supports one payment method and simplicity is paramount.
Event-Driven Payment Processing
Payments are processed asynchronously via events and queues rather than direct method calls.
Use when: Choose when high scalability and decoupling of payment steps are required.
Summary
Payment handling separates payment logic to manage multiple payment methods cleanly.
Using the strategy pattern allows dynamic selection and easy extension of payment methods.
This approach improves maintainability, error handling, and supports future growth.