Architecting for Scale: How to Build a High-Concurrency Merchant Payout System

Contents

In a massive e-commerce platform, processing a user’s payment is only half the battle. The real engineering challenge begins behind the scenes: securely splitting that payment into platform commissions, shipping logistics fees, and the merchant’s net revenue, and then successfully disbursing those funds to a bank account.

Designing the backend mechanism for a merchant payout system—similar to the one powering TikTok Shop—requires extreme precision. The system must process order distributions safely, compute split logic, and handle high-concurrency wallet updates without a single cent going missing or being double-counted.

Here is an inside look at how to build a fault-tolerant, high-scale financial payout architecture.

The Financial State Machine

Money must flow through specific stages controlled by a centralized state machine. The boundary is highly event-driven; the system listens to ORDER_COMPLETED events to kickstart calculations rather than relying on synchronous, blocking HTTP REST calls.

The lifecycle operates in these strict phases:

  • ESCROW_HOLD: The state metadata is updated in the Payout Core Engine, while the Ledger Service immediately locks the buyer’s funds into an immutable append-only escrow account line item.
  • SPLIT_CALCULATION: Triggered as stateless, in-memory computation within the Payout Core Engine to calculate platform commissions, logistics fees, and merchant net margins before any physical money moves.
  • DISBURSEMENT_PROCESSING: Orchestrated via Kafka events to the Banking Gateway Wrapper, which handles the actual network execution and handshake with the External Bank API.

If a server crashes mid-flight, the centralized state machine ensures the system does not guess or re-run from scratch. It reads the last recorded immutable ledger line and safely resumes the exact phase, completely neutralizing duplicate transfers.

Core Architecture & The Double-Entry Ledger

To maintain strict financial auditing and high throughput, the read and write paths are heavily decoupled:

Component Primary Responsibility
API Gateway Captures incoming events and coordinates with a Redis Cache to check Idempotency Keys and short-circuit duplicate network retries.
Payout Core Engine The central coordinator acting as the Saga Orchestrator to drive the Financial State Machine.
Message Broker (Kafka) Asynchronously processes sub-orders partitioned by Merchant_ID (Vendor Sharding) to isolate vendor failures.
Ledger Service (PostgreSQL) An immutable relational database implementing Double-Entry Bookkeeping for strict financial auditing and ACID compliance.
Read-Optimized Wallet Cache (Redis) Serves the high-frequency read path directly from the API Gateway (e.g., getting a merchant’s balance) to offload the main PostgreSQL database.
Banking Gateway Wrapper Manages external bank API connectivity and executes rollback orchestration if the payment network drops.

Solving Distributed Race Conditions

What happens if a buyer requests a refund at the exact same millisecond the system triggers automatic disbursement to the merchant?

This is a critical distributed race condition. Standard database table locking (like SELECT FOR UPDATE) fails here because it causes catastrophic bottlenecks at a scale of millions of transactions.

The solution requires an Idempotency Key (e.g., Order_ID + Phase) combined with a Distributed Lock (like Redis Redlock). Whichever request enters first acquires the lock and shifts the state machine. If the disbursement wins, the state becomes DISBURSED, and the delayed refund request is rejected by the state validator and safely routed to Customer Service for manual investigation to ensure zero double-spending.

Surviving Mega Flash Sales (The Hot Partition Problem)

During a Mega Live Stream Flash Sale, a single hot merchant might receive 50,000 completed orders per minute. If thousands of worker threads try to update the exact same merchant’s wallet balance in the PostgreSQL ledger concurrently, it creates a massive database “hot partition” and resource starvation.

To prevent database row contention, the system uses a Write-Buffering or In-Memory Aggregation strategy. Instead of hitting the database immediately for every single order, workers use an in-memory counter (Redis) to accumulate the payout deltas for that specific merchant over a short time window (e.g., every 5 seconds or 500 records).

A scheduled batch worker then flushes the aggregated chunk to the main database ledger in a single transaction block, reducing 50,000 single updates down to a few highly optimized batch queries.

Multi-Vendor Cart Checkouts & Isolation

If a buyer purchases items from 3 different shops in one single checkout, how do you isolate the split-billing logic so a failure in one merchant’s bank connection doesn’t block payouts for the other two?

At the architectural boundary, the system breaks the single parent order into decoupled execution units. The parent Order_ID emits individual downstream tasks into Kafka per vendor sub-order (Sub_Order_ID). Each merchant’s payout runs inside an independent worker thread, completely removing shared cross-tenant blocking dependencies.

Handling Banking Failures: The Saga Pattern

External banking APIs will inevitably fail or time out. When this happens during disbursement, the system cannot simply roll back the database transaction immediately because the initial ledger split already happened.

Instead, the architecture utilizes the Distributed Saga Pattern. The system executes a Compensating Transaction: it transitions the status to DISBURSEMENT_FAILED, preserves the audit trail, and schedules an automated retry using an Exponential Backoff strategy with jitter.

To catch any data drift between internal records and the physical bank, a nightly, decoupled Reconciliation Pipeline(using MapReduce or Spark) ingests CSV/API bank statements and matches them line-by-line against the internal Ledger database to flag anomalies automatically.

 

See other interesting posts

Technology

Architecting for Scale: How to Build a Financial-Grade Ad Click Tracker

When you are processing ad clicks at a global scale, you aren’t just counting numbers—you are handling money. A distributed ad click tracking system, like …

Technology

Architecting for Scale: How to Build a Real-Time Geospatial Driver Matching System

When a user opens a ride-hailing app, they expect to see nearby cars moving smoothly on a map and to be matched with the closest …

Technology

Architecting for Scale: How to Design a Distributed Unique ID Generator Like Discord

Every day, billions of messages fly across massive real-time communication platforms like Discord. Displaying these messages in perfect chronological order requires a rock-solid system for …

Discover the valuable contents about tech

Get high quality contents