Skip to content

Architecture Lab

Designing a Payment System

Key components and best practices.

From customer intent to financially trustworthy settlement

A payment system is often introduced as a single API operation:

POST /payments

That API hides the real workflow.

A production payment platform coordinates:

  • Customer intent
  • Payment-method collection
  • Authentication
  • Fraud and risk decisions
  • Payment processors
  • Acquiring and issuing banks
  • Asynchronous webhooks
  • Financial ledger entries
  • Refunds and disputes
  • Settlement
  • Reconciliation

Some stages complete in milliseconds. Others may remain unresolved for minutes, hours, or days.

The most important design principle is:

A payment is not one remote call. It is a durable sequence of financial state transitions.

The payment lifecycle

A typical payment moves through five broad stages.

  1. Create the payment intent.
  2. Collect a payment method.
  3. Authorize the payment.
  4. Capture the authorized amount.
  5. Settle the funds.

Figure 1: The main payment lifecycle, from customer intent to final settlement.

1. Create the payment intent

The payment intent represents the business intention to collect a specific amount from a customer.

It may contain:

Payment Intent ID
Merchant ID
Order ID
Customer ID
Amount
Currency
Payment status
Allowed payment methods
Capture strategy
Idempotency key
Creation time
Expiry time

Example:

{
  "paymentIntentId": "pi_9014",
  "orderId": "order_4821",
  "amount": 4250,
  "currency": "INR",
  "captureMethod": "MANUAL",
  "status": "REQUIRES_PAYMENT_METHOD"
}

The intent should exist before contacting a payment processor. This gives the platform an internal identifier and durable state that remain stable even if processor calls fail.

2. Collect the payment method

The customer selects a card, wallet, bank transfer, or another supported method.

The platform should avoid directly handling raw card credentials wherever possible.

A safer design uses:

  • Hosted payment fields
  • Processor-provided client libraries
  • Tokenization
  • Payment-method references
  • Short-lived authentication tokens

The application stores a token or reference rather than the underlying sensitive credentials.

3. Authorize

Authorization asks the issuer or payment provider whether the transaction may proceed.

Depending on the payment method, authorization may involve:

  • Balance or credit checks
  • Fraud screening
  • Strong customer authentication
  • One-time passwords
  • Device challenges
  • Risk rules
  • Merchant controls

An authorization does not always mean that funds have been fully transferred. It may represent an approval or reservation.

4. Capture

Capture confirms that the merchant wants to collect some or all of the authorized amount.

Capture may happen:

  • Immediately after authorization
  • When physical goods are shipped
  • After a service has been delivered
  • In multiple partial captures
  • After a manual review

5. Settlement

Settlement is the external financial process through which funds move through processor and banking networks.

The customer-facing payment may appear complete before the merchant’s funds have fully settled.

This distinction is important:

Customer payment succeeded
does not always mean
merchant settlement completed

Core payment-system architecture

A production design should separate customer experience, payment workflow state, processor communication, risk, accounting, and reconciliation.

Figure 2: Core services involved in a payment platform.

The main components are described below.

Checkout or client application

The checkout interface:

  • Collects customer intent
  • Initiates payment
  • Hosts or embeds secure payment fields
  • Handles authentication challenges
  • Displays the final customer-facing outcome

The client should not be responsible for deciding the authoritative payment state.

It should ask the payment service for that state.

Payment service

The Payment Service is the central domain service.

It owns:

  • Payment intents
  • Payment attempts
  • State transitions
  • Idempotency
  • Processor routing
  • Authorization
  • Capture
  • Cancellation
  • Refund initiation
  • Payment events

It should persist its decision before informing downstream systems that the payment succeeded.

Risk engine

The risk engine evaluates whether an operation should proceed.

Inputs may include:

  • Customer history
  • Merchant history
  • Device signals
  • Transaction velocity
  • Country
  • Currency
  • Payment method
  • Amount
  • Previous disputes
  • Processor feedback

A risk decision should record:

Decision
Policy or model version
Reason codes
Timestamp
Input reference
Manual-review result

Processor or acquirer integration

The processor connector translates the internal payment operation into a provider-specific request.

A common internal interface may expose:

Authorize
Capture
Void
Refund
Get Status

However, not every provider supports identical capabilities.

The platform should preserve provider-specific differences rather than forcing every processor into an inaccurate lowest-common-denominator model.

Webhook ingestion

Payment providers use webhooks to report asynchronous state changes.

Examples include:

  • Authentication completed
  • Authorization approved
  • Capture completed
  • Refund completed
  • Dispute created
  • Settlement updated

Webhook handling must be durable and idempotent.

Double-entry ledger

The ledger records the financial meaning of successful operations.

Payment state tells the system what operationally happened.

Ledger entries tell the system how money should be accounted for.

Reconciliation

Reconciliation compares:

  • Processor data
  • Internal payment attempts
  • Ledger entries
  • Settlement reports

Its purpose is to detect and repair divergence.

Model payment intent and payment attempt separately

A payment intent represents the business objective.

A payment attempt represents one execution against a payment method and processor.

One intent may have multiple attempts.

Example:

Payment Intent: pi_9014
Amount: ₹4,250

Attempt 1:
Card ending 7421
Processor A
Declined

Attempt 2:
UPI
Processor B
Succeeded

Keeping the objects separate allows the system to distinguish:

  • One customer payment from several retries
  • A processor decline from the overall intent
  • A new payment method from a repeated request
  • Failover from duplicate processing

Payment state machines

Payment state must be explicit.

Do not rely on loosely related boolean fields such as:

isPaid
isAuthorized
isCaptured
hasFailed

Those combinations can produce invalid states.

Use well-defined state transitions instead.

Figure 3: Payment intent and payment attempt have related but different lifecycles.

Example payment-intent states

CREATED
REQUIRES_PAYMENT_METHOD
REQUIRES_ACTION
PROCESSING
SUCCEEDED
FAILED
CANCELLED

Example payment-attempt states

INITIATED
SENT_TO_PROCESSOR
AUTHORIZED
DECLINED
UNKNOWN
CAPTURED
VOIDED
FAILED

Valid transitions

INITIATED → SENT_TO_PROCESSOR
SENT_TO_PROCESSOR → AUTHORIZED
AUTHORIZED → CAPTURED
AUTHORIZED → VOIDED

Invalid transitions

DECLINED → CAPTURED
CAPTURED → AUTHORIZED
VOIDED → CAPTURED

The service should validate every transition.

It should also retain state history:

Attempt initiated
Request sent to processor
Processor timed out
Status queried
Authorization confirmed
Capture requested
Capture completed

This timeline is valuable for:

  • Support investigations
  • Incident response
  • Dispute analysis
  • Reconciliation
  • Auditability

Authorization and capture strategies

Not every business captures money at the same point.

Immediate digital purchase

Authorize
   ↓
Capture immediately
   ↓
Grant digital access

Physical goods

Authorize at checkout
   ↓
Reserve inventory
   ↓
Ship order
   ↓
Capture payment

Marketplace

Collect from buyer
   ↓
Record platform and seller obligations
   ↓
Wait for fulfilment conditions
   ↓
Create seller payable

Hotel or rental

Authorize estimated amount
   ↓
Adjust final amount
   ↓
Capture later

The payment model should support the business workflow rather than force all products into one payment sequence.

Idempotency

Network failures cause clients to retry.

Suppose this sequence occurs:

Client sends authorization request
Payment succeeds
Response is lost
Client retries

Without idempotency, the second request may create another charge.

Every mutating operation should accept or derive a stable idempotency key.

Example:

checkout-83:authorize

Store:

  • Idempotency key
  • Merchant or tenant scope
  • Operation type
  • Request fingerprint
  • Current processing state
  • Final response
  • Creation and expiry times

If the same key is reused with different request details, reject it.

For example, do not silently accept the same key for both:

₹4,250 INR

and:

₹8,500 INR

That represents conflicting business intent.

Unknown outcomes

A timeout does not always mean the processor failed.

The processor may have:

  1. Never received the request.
  2. Received it but not completed it.
  3. Completed it but lost the response.

The correct internal status may therefore be:

UNKNOWN

rather than:

FAILED


Figure 4: After a timeout, query the existing operation before retrying.

Place Figure 4 after explaining the three possible timeout outcomes.

The recovery process is:

Send authorization
        ↓
      Timeout
        ↓
Mark attempt UNKNOWN
        ↓
Query using operation ID
      /             \
Authorized        Not found
    ↓                ↓
Continue         Retry safely

Treating every timeout as failure can create duplicate charges.

Treating every timeout as success can create unpaid orders.

Unknown outcomes must be resolved through:

  • Provider status APIs
  • Idempotency identifiers
  • Webhooks
  • Reconciliation
  • Manual investigation when necessary

Processor routing

A payment platform may use multiple processors.

Routing may consider:

  • Country
  • Currency
  • Payment method
  • Merchant configuration
  • Historical approval rate
  • Processor health
  • Cost
  • Risk policy
  • Data-residency requirements

Persist the selected route with the payment attempt.

Example:

{
  "attemptId": "pa_7702",
  "processor": "PROCESSOR_A",
  "routingRuleVersion": "route-v12",
  "paymentMethod": "CARD",
  "country": "IN"
}

A retry should not automatically switch processors unless the business explicitly allows the creation of a new attempt.

Otherwise, one customer action could produce charges through two different providers.

Webhook processing

Provider callbacks are a core part of the payment lifecycle.

They are not optional notification messages.

A safe webhook flow should:

  1. Receive the callback.
  2. Verify its signature.
  3. Persist the raw event.
  4. Deduplicate it.
  5. Return success quickly.
  6. Process it asynchronously.
  7. Apply the state transition idempotently.
  8. Record the result.
Figure 5: Authenticate and persist a webhook before processing its business effect.

Why persist the raw event?

Persisting the original callback helps with:

  • Replay
  • Provider disputes
  • Debugging
  • Schema changes
  • Security investigations
  • Reconciliation

Do not assume webhook ordering

A system may receive:

Payment Captured

before:

Payment Authorized

because of network delays or provider behaviour.

Use:

  • Attempt versions
  • Provider timestamps
  • Valid state-transition checks
  • Status reconciliation

Do not assume one delivery

The same webhook may arrive several times.

Deduplicate using a provider event ID or an equivalent stable identifier.

Double-entry ledger

Operational payment records alone are not sufficient for financial accounting.

A payment may be marked SUCCEEDED, but the platform still needs to represent:

  • Processor receivable
  • Merchant payable
  • Platform fee
  • Tax
  • Refund liability
  • Chargeback adjustment

Use a double-entry ledger.

Every financial transaction produces balanced debit and credit entries.

Figure 6: A ₹1,000 payment is represented through balanced financial entries.

Place Figure 6 after introducing double-entry accounting.

Suppose a customer pays ₹1,000 and the platform fee is ₹30.

The merchant receives ₹970.

Debit  Processor Receivable    ₹1,000
Credit Merchant Payable          ₹970
Credit Platform Fee Revenue       ₹30

The transaction balances:

Total debits = Total credits

Ledger principles

Ledger entries should be:

  • Immutable
  • Currency-aware
  • Linked to a business operation
  • Created idempotently
  • Timestamped
  • Auditable

Never edit financial history in place.

If a previous entry must be corrected, create a reversing entry and then post the corrected transaction.

Ledger and payment state are different

Payment state answers:

Did the processor authorize or capture the payment?

Ledger state answers:

Which accounts gained or lost value because of that operation?

Both are required.

Refunds and reversals

A refund is not simply changing:

payment.status = REFUNDED

It is a separate financial operation.

A refund may be:

  • Full
  • Partial
  • Pending
  • Failed
  • Reversed
  • Split across several captures

A payment can also have multiple partial refunds.

Example:

Captured: ₹1,000
Refund 1: ₹200
Refund 2: ₹150
Remaining captured amount: ₹650

The system must ensure:

Total successful refunds ≤ Refundable captured amount

Refund requests require their own:

  • Idempotency key
  • State machine
  • Processor operation ID
  • Ledger entries
  • Webhook handling
  • Reconciliation

Reconciliation

Even a well-designed payment system can diverge from external financial reality.

Common causes include:

  • Missing webhooks
  • Duplicate callbacks
  • Processor-side manual actions
  • Timeouts
  • Partial captures
  • Partial refunds
  • Settlement adjustments
  • Chargebacks
  • Software defects
  • Currency or fee differences

Reconciliation compares external and internal records.

Figure 7: Reconciliation compares processor reports, internal attempts, and ledger entries.

Typical mismatch categories

External success, internal missing

The processor completed the payment, but the internal attempt remains unknown or failed.

Internal success, external missing

The system believes the payment succeeded, but the processor has no matching transaction.

Amount mismatch

The internal amount differs from the processor amount.

Currency mismatch

The internal and external transactions use different currencies.

Settlement missing

The payment succeeded, but expected settlement has not appeared.

Fee mismatch

Processor fees differ from the expected fee calculation.

Refund mismatch

A refund exists externally but not internally, or vice versa.

Reconciliation outcomes

A reconciliation item may be classified as:

MATCHED
MISMATCH
INVESTIGATING
REPAIR_PENDING
RESOLVED
MANUAL_REVIEW

Every mismatch should record:

  • Owner
  • Evidence
  • Classification
  • Financial value
  • Repair action
  • Final resolution
  • Audit history

Reconciliation is not a reporting feature added later.

It is how the system proves that its financial records match the outside world.

Reliability patterns

Payment reliability depends on explicit patterns.

Transactional outbox

When payment state changes, the system may need to publish an event.

These are two writes:

  1. Update the database.
  2. Publish the event.

Use a transactional outbox so the state change and event intent commit together.

Idempotent consumers

Downstream services must tolerate duplicate events such as:

Payment Succeeded

Fulfilment must not ship the same order twice.

Bounded retries

Retry only operations known to be safe.

Use:

  • Exponential backoff
  • Jitter
  • Retry budgets
  • Operation IDs

Circuit breakers

Stop sending requests to a failing processor when continued attempts would cause more damage.

A circuit breaker should not hide unknown outcomes. Existing ambiguous operations still require resolution.

Bulkheads

Isolate:

  • Processors
  • Payment methods
  • Merchants
  • Worker pools

One degraded integration should not consume every thread, connection, or retry worker.

Security and financial trust

Payment security is not one gateway feature. It applies across the entire system.

      

Figure 8: Reliability protects execution; security and reconciliation protect financial trust.

Important controls include:

Tokenization

Store payment-method tokens rather than raw sensitive credentials.

Webhook authentication

Verify callback signatures and reject unauthenticated requests.

Encryption

Protect sensitive information:

  • In transit
  • At rest
  • In backups
  • In logs and operational exports

Least privilege

Limit access for:

  • Services
  • Operators
  • Support agents
  • Reconciliation teams
  • Data pipelines

Log hygiene

Never place sensitive payment credentials into:

  • Application logs
  • Traces
  • Error messages
  • Analytics events
  • Support exports

Auditability

Record:

  • Who initiated an action
  • Which service executed it
  • Which policy allowed it
  • Which state changed
  • Which amount and currency were involved
  • Which processor operation was referenced

API design

A payment API should expose business operations rather than generic updates.

Example endpoints:

POST /payment-intents
POST /payment-intents/{id}/confirm
POST /payment-intents/{id}/capture
POST /payment-intents/{id}/cancel
POST /payments/{id}/refunds
GET  /payment-intents/{id}
GET  /payments/{id}/timeline

Avoid an endpoint such as:

PATCH /payment/{id}
{
  "status": "SUCCEEDED"
}

Clients should not be able to arbitrarily assign authoritative payment states.

Useful response fields

Payment intent ID
Amount
Currency
Current state
Required next action
Payment attempt summary
Version
Safe failure category
Created time
Updated time

Do not expose raw processor responses as the public API contract.

That creates coupling and can reveal sensitive details.

Observability

A payment platform must measure both technical performance and financial correctness.

Technical metrics

  • API latency
  • Processor latency
  • Webhook age
  • Retry rate
  • Timeout rate
  • Queue backlog
  • Worker failures
  • Circuit-breaker state

Payment metrics

  • Authorization approval rate
  • Payment success rate
  • Capture success rate
  • Unknown-outcome rate
  • Duplicate-prevention count
  • Refund completion time

Financial metrics

  • Reconciliation mismatch count
  • Reconciliation mismatch value
  • Missing settlement value
  • Ledger imbalance count
  • Unresolved refund value
  • Manual adjustment value

Ledger imbalance should remain zero.

Useful dimensions

Segment metrics by:

  • Merchant
  • Country
  • Currency
  • Processor
  • Payment method
  • Client version
  • Risk decision
  • Failure category

A global success rate may hide a serious problem affecting one processor or one region.

Failure scenarios to test

A production payment system should be tested against failures deliberately.

Test:

  • Client retry after timeout
  • Processor succeeds but response is lost
  • Duplicate authorization request
  • Duplicate capture request
  • Webhook arrives before API response
  • Duplicate webhook
  • Out-of-order webhook
  • Partial capture
  • Partial refund
  • Processor outage
  • Risk-engine timeout
  • Ledger transaction failure
  • Event publication failure after database commit
  • External payment with no internal attempt
  • Internal success with no external payment
  • Refund succeeds externally but internal processing crashes
  • Reconciliation repair fails
  • Processor routing changes during a retry

The payment system is trustworthy only when its ambiguous and failure states are designed as carefully as its happy path.

Key design trade-offs

Decision Benefit Cost
Separate intent and attempt Clear retries and processor history More domain objects
Explicit state machines Prevent invalid transitions More transition logic
Idempotency Prevent duplicate financial effects Requires durable operation records
Asynchronous webhooks Handles delayed provider outcomes Ordering and duplication complexity
Double-entry ledger Strong financial auditability Additional accounting model
Multiple processors Coverage and resilience Routing and reconciliation complexity
Event-driven integration Decouples downstream services Eventual consistency
Reconciliation Detects financial drift Operational investment
Strong security boundaries Reduces exposure and abuse More controls and review

Design review checklist

Before approving a payment design, ask:

  • Is payment intent separate from processor attempt?
  • What is the authoritative state machine?
  • What is the idempotency boundary?
  • How are conflicting requests detected?
  • How are unknown outcomes represented?
  • How does the system query existing processor operations?
  • How are webhooks verified and deduplicated?
  • What happens when webhooks arrive out of order?
  • Which system is financially authoritative?
  • Are ledger entries immutable and balanced?
  • How are refunds and partial captures represented?
  • How does reconciliation detect and repair drift?
  • Can one processor outage be isolated?
  • Which operations require manual approval?
  • Which metrics prove financial correctness.

Final perspective

A payment platform is a consistency, evidence, and recovery system.

Its job is not simply to send an authorization request to a processor.

It must:

  • Preserve customer and merchant intent
  • Prevent duplicate financial effects
  • Represent ambiguous outcomes honestly
  • Validate every state transition
  • Account for every unit of money
  • Process asynchronous external updates
  • Detect divergence
  • Recover when internal and external state disagree

The strongest design principle is:

Every payment outcome must be durable, explainable, idempotent, and financially reconcilable

Back to Architecture Lab