Skip to content
Low Level Design Mastery Logo
LowLevelDesign Mastery

Design a Payment Processor

Design a secure, idempotent payment gateway with multi-provider support and state management.

Design a payment processor system that supports multiple payment methods (credit card, PayPal, bank transfer), manages transaction states, ensures idempotency to prevent duplicate charges, and implements retry logic for handling transient failures. The system should handle concurrent transactions safely and be extensible for adding new payment methods.

In this problem, you’ll design a secure orchestration layer that coordinates between your application and various 3rd-party providers while maintaining a strict audit trail and absolute consistency.


Design a robust backend system that accepts payment requests, routes them to the correct provider, and ensures the transaction reaches a final state accurately.

Functional Requirements:

  • Multi-method Support: Process Credit Cards, PayPal, and Bank Transfers.
  • State Management: Track transactions through Pending, Completed, and Failed states.
  • Idempotency: Prevent double-charging using unique idempotency keys.
  • Retries: Automatically retry transient failures with backoff.
  • Refunds: Support full and partial refunds for completed transactions.
  • Audit Logging: Maintain a permanent record of every state change.

Non-Functional Requirements:

  • Absolute Consistency: No race conditions in state transitions.
  • Security: Sensitive data should never be logged or stored long-term.
  • Availability: The system must stay responsive even if one provider is down.
  • Extensibility: Easy to add new strategies (like Apple Pay or Crypto).

The processor acts as a secure buffer between the CheckoutService and external ProviderAPIs.

Diagram
classDiagram
    class PaymentProcessor {
        -Map~String, Transaction~ cache
        -ProviderFactory providers
        +execute(request)
    }
    
    class Transaction {
        -String id
        -String idempotencyKey
        -double amount
        -TransactionState state
        +transition(nextState)
    }
    
    class TransactionState {
        <<interface>>
        +handle()
    }

    class PaymentProvider {
        <<interface>>
        +authorize(amount)
        +capture(amount)
    }

    PaymentProcessor --> Transaction
    Transaction --> TransactionState
    PaymentProcessor --> PaymentProvider

Diagram

If a user clicks “Pay” and the network times out, the mobile app might retry. You cannot charge them twice.

Solution: Use an Idempotency Key (usually a UUID generated by the client). Before processing, the PaymentProcessor checks the database for that key. If found, it returns the previous result instead of starting a new transaction.

What if Stripe is down for 5 seconds?

Solution: Use the Command Pattern with Retry Logic. Queue the payment request. If the provider returns a “503 Service Unavailable,” the system uses an exponential backoff strategy to retry. If it returns a “402 Card Declined,” do NOT retry—this is a permanent failure.

A transaction might be “Authorized” in Stripe but your database fails to update.

Solution: Webhooks & Reconciliation. Implement a background “Reconciliation Job” that polls the provider’s API for any “Authorized” payments that don’t have a matching “Completed” state in your DB, ensuring the two systems eventually reach consistency.


By solving this problem, you’ll master:

  • Idempotency Mechanisms - Building bulletproof distributed logic.
  • State Machines - Handling non-linear lifecycles (Refunds, Chargebacks).
  • Strategy & Factory Patterns - Abstracting away complex third-party SDKs.
  • Financial Reliability - Designing for zero-data-loss scenarios.

Ready to see the full implementation? Open the interactive playground to access:

  • 🎯 Step-by-step guidance through the 8-step LLD approach
  • 📊 Interactive UML builder to visualize your design
  • 💻 Complete Code Solutions in Python, Java, C++, TypeScript, JavaScript, C#
  • 🤖 AI-powered review of your design and code

After mastering the Payment Processor, try these similar problems: