NEW: ML Mock & Coaching now available

Questions

Payment System

StripePayPalOpenAI

Design Payment System - This system design covers payment processing, authorization, settlement, security with tokenization, exactly-once guarantees, and webhook delivery at scale.

45 min read

Challenge

Think Beyond the Happy Path

Payment systems aren't just about moving money — they're about exactly-once processing, PCI-compliant security, and reliable webhook delivery that merchants can depend on.

Before diving into the material, take a moment to ask yourself:

  • Do you know how the system stays secure — what's the role of tokenization, and how do you avoid storing raw card numbers while still supporting recurring payments?
  • Do you know how to guarantee exactly-once processing — what happens if a network timeout occurs after the bank approves but before you record the result?
  • Do you know how to design reliable webhook delivery — how do you handle merchant endpoints that are temporarily down without losing payment notifications?
  • Do you know how to handle partial failures in settlement — what happens when you've debited the customer but the merchant payout fails?

You don't need to answer all of these right away. A Senior/Staff+ Engineer doesn't stop at basic functionality — they anticipate edge cases, design for resilience, and push for production-grade reliability.

But great systems start with great questions. What would you ask next?

Problem Statement

Design a Stripe-like payment system that supports payment initiation, authorization, status tracking, and daily batch settlement. The system should:

  1. Allow merchants to submit payment requests.
  2. Allow customers to complete payments using a payment method.
  3. Provide real-time status tracking for merchants.
  4. Settle all authorized payments once daily. And the scale is up to 10,000 QPS sustained throughput.
What is Stripe?

Stripe is a modern payment processing platform that enables businesses to accept online payments quickly and securely. It abstracts the complexities of payment infrastructure—such as integrating with card networks, handling authorization and settlement, and complying with financial regulations—into a set of developer-friendly APIs.

Basic Payment Workflow in Stripe

  1. Payment Initiation: A user submits a payment request (e.g., credit card purchase) through a merchant's website or app.
  2. Authorization: Stripe forwards the request to an external payment provider (e.g., Visa, Mastercard, or a bank) to authorize the payment. If approved, the funds are held (not immediately transferred).
  3. Status Tracking: The merchant or user can check the status of the payment (e.g., authorized, declined, pending).
  4. Batch Settlement: Once per day, Stripe aggregates authorized payments and submits them to the payment network for actual fund transfer and final settlement.

Functional Requirements

FR1 – Merchant can submit a payment request

The system should allow a merchant to initiate a payment with amount, currency, and customer details.

FR2 – Customer can complete the payment using a payment method

The system should accept and process a customer's payment method and trigger authorization with the external provider.

FR3 – Merchant can view the current status of any payment

The system should return real-time or persisted status updates of a payment, including authorization outcome and settlement state.

FR4 – System should settle all authorized payments once daily

The system should batch authorized payments at the end of each day and submit them for final settlement with the external provider.

Non-Functional Requirements

NFR1 – Security — No confidential data leak

The system must protect all sensitive payment data by enforcing industry-standard security measures: encrypting all traffic with TLS 1.2 or higher, encrypting stored data with AES-256, and enforcing role-based access control and audit logging. This is essential to prevent data breaches, meet regulatory requirements, and build trust with users and financial partners.

NFR2 – Strong Consistency — Exactly-once processing, no duplicate settlement

The system must ensure that each payment is processed exactly once and moves through a well-defined state machine (e.g., pending → authorized → settled) without duplication or loss. Inaccurate payment states can lead to financial loss, double charges, or regulatory issues, which are unacceptable in a production payment system.

NFR3 – Durability — ≥ 99.9999999% (nine 9s) data durability

All accepted payments must be written to a durable, replicated data store before acknowledging the client. A payment system cannot lose data due to crashes, network failures, or region outages; persistence guarantees are foundational to user trust and financial correctness.

NFR4 – High Scalability — ≥ 10,000 QPS sustained throughput

The system must be able to ingest and process at least 10,000 payments per second at steady load, with the ability to scale horizontally across stateless services and background workers. This ensures the system can handle real-world traffic spikes, global usage, and future growth without becoming a bottleneck.

Requirement Summary

Functional Requirements (FRs)
NameDescription
1. Submit Payment RequestMerchant initiates a payment with amount, currency, and customer details.
2. Complete PaymentCustomer completes payment using a payment method, triggering authorization.
3. View Payment StatusMerchant views real-time status including authorization outcome and settlement state.
4. Daily SettlementSystem batches authorized payments daily and submits for final settlement.
Non-Functional Requirements (NFRs)
NameDescription
1. SecurityNo confidential data leak; TLS 1.2+, AES-256, RBAC, audit logging.
2. Strong ConsistencyExactly-once processing, no duplicate settlement.
3. Durability≥ 99.9999999% (nine 9s) data durability.
4. High Scalability≥ 10,000 QPS sustained throughput.

Core Entities

To tie these requirements together into a coherent architecture, we begin with three foundational building blocks: the Merchant, the Payment, and the Transaction.

Merchant — The Business Entity

Represents a business integrating with the payment platform to accept payments.

Merchants Table
FieldTypeKeyDescription
merchant_idUUIDPKUnique ID for the merchant.
nameStringNOT NULLMerchant or business name.
api_keyStringNOT NULLCredential used to authenticate API calls.
statusEnumNOT NULLACTIVE, SUSPENDED, INACTIVE.
created_atTimestampNOT NULLWhen the merchant was onboarded.

Payment — The Business Intent

Represents a payment request initiated by a merchant and fulfilled by a customer.

Payments Table
FieldTypeKeyDescription
payment_idUUIDPKUnique ID for the payment.
merchant_idUUIDFK → merchants(merchant_id)Merchant receiving the payment.
amountDecimalNOT NULLPayment amount.
currencyStringNOT NULLISO code (e.g., USD).
statusEnumNOT NULLPENDING, AUTHORIZED, DECLINED, SETTLED.
customer_infoObjectNULLABLEOptional customer metadata (e.g., ID, email, session token).
created_atTimestampNOT NULLWhen the payment was initiated.
payment_infoObjectNULLABLEPayment Raw Data, including credit card number, cvc etc.
[Common Pitfall]

Many candidates include fields like this in their Payment entity:

"payment_info": {
  "card_number": "4242 4242 4242 4242",
  "exp_month": 12,
  "exp_year": 2026,
  "cvc": "123"
}

This instantly implies that your backend:

  • Receives raw card data (puts you in PCI DSS Level 1 scope)
  • Might store or log that data somewhere (a major compliance breach)
  • Doesn't properly separate data handling responsibilities

[Great Answer]

We use tokenization to avoid handling raw payment data. The customer submits their card directly to Stripe (or another vault provider), and our backend receives a token like pm_12345abc, which we store in the payments table. That ensures we never touch sensitive data directly. (Please check Deep Dive 1 later for updated schema)

Transaction — A Specific Processing Step

Represents a discrete step taken to process a payment — such as authorization or settlement.

Transactions Table
FieldTypeKeyDescription
transaction_idUUIDPKUnique ID for the transaction.
payment_idUUIDFK → payments(payment_id)Which payment this transaction belongs to.
typeEnumNOT NULLAUTHORIZATION, SETTLEMENT, REFUND.
statusEnumNOT NULLSUCCESS, FAILED, RETRYING.
provider_idStringNULLABLEExternal payment provider reference (e.g., auth code).
actor_typeEnumNOT NULLCUSTOMER, MERCHANT, SYSTEM — who triggered the action.
response_codeStringNULLABLEProvider result code or message.
processed_atTimestampNULLABLETimestamp of when this transaction was processed.
Payment Terminology

Many candidates don't have experience in payment system development, so we provide you some fundamental understandings on the core concepts. Still we use real-world analogy, i.e. Stripe

  • Payment = The "PaymentIntent" in Stripe — a complete business unit initiated by a merchant.
  • Transaction = A processing event like an authorization, a capture, or a refund attempt.

Relationship

  • Each Payment can have many Transactions (1 to N)
  • Transactions are always tied to a specific payment_id
  • Transactions allow retries, multiple steps, and eventual settlement tracking without modifying the original Payment intent

Comparison

Payment vs Transaction
CategoryPaymentTransaction
DefinitionA business-level intent to transfer fundsA processing-level record of an action taken on a payment
GranularityOne payment per customer/merchant eventOne or more transactions per payment
ExamplesCharge $120 to customer for an orderAuthorize $120, Settle $120, Refund $120
LifespanCreated once per user requestMay have multiple entries (e.g., retries, stages, failures)
StatusPENDING, AUTHORIZED, DECLINED, SETTLEDSUCCESS, FAILED, RETRYING
PurposeRepresents the logical flow the merchant/user cares aboutCaptures the actual steps taken to fulfill the payment
Who cares?Exposed to external clients (e.g., merchants, UIs, APIs)Mostly internal (for processing, retries, audit, reconciliation)

Sign in to continue reading

"Payment System" requires a free account to access.

Sign in to continue