Checkout Service Low-Level Design: Cart Locking, Order Creation, and Payment Orchestration

Checkout Service Low-Level Design

The checkout service orchestrates the final steps of a purchase: reserving inventory, creating an order record, and driving payment to completion. It must handle concurrent shoppers racing for limited stock, partial failures in downstream services, and user-visible latency requirements typically under two seconds.

Requirements

Functional

  • Lock cart items against concurrent purchase for the duration of checkout.
  • Create an order atomically once payment authorization succeeds.
  • Orchestrate payment, inventory deduction, and fulfillment trigger as a saga.
  • Compensate each completed step if a later step fails (cancel auth, release stock).
  • Support guest and authenticated checkout paths.

Non-Functional

  • Checkout initiation to order confirmation under 2 seconds at the 95th percentile.
  • Zero oversell: stock reservation must be atomic.
  • Idempotent retry on any step without double-charging or double-deducting inventory.

Data Model

  • cart: id, user_id (nullable for guest), session_token, status (OPEN | LOCKED | CHECKED_OUT | EXPIRED), locked_at, expires_at.
  • cart_item: id, cart_id, sku_id, quantity, unit_price_snapshot, currency.
  • stock_reservation: id, cart_id, sku_id, quantity, reserved_at, expires_at, released_at.
  • checkout_session: id, cart_id, idempotency_key (UNIQUE), saga_state (JSONB), status (PENDING | PAYMENT_PENDING | CONFIRMED | FAILED | COMPENSATING), created_at, updated_at.
  • order: id, checkout_session_id, user_id, total_amount, currency, status, created_at.
  • order_item: id, order_id, sku_id, quantity, unit_price, line_total.

Core Algorithms and Flows

Cart Locking

When the buyer initiates checkout the service acquires a distributed lock (Redis SET NX PX) keyed on cart_id for a 10-minute TTL. It then reserves stock for each cart item by decrementing an atomic counter in Redis backed by a Postgres write. If any SKU is out of stock the lock is released immediately and the buyer sees an out-of-stock error before any charge is attempted.

Checkout Saga

The saga is an orchestrator-based workflow stored in checkout_session.saga_state. Steps execute in order with each step writing its outcome before proceeding:

  • Step 1 — Reserve Stock: decrement inventory; compensation releases the reservation.
  • Step 2 — Authorize Payment: call payment gateway with checkout_session_id as idempotency key; compensation voids the auth.
  • Step 3 — Create Order: insert order and order_item rows inside a single database transaction.
  • Step 4 — Trigger Fulfillment: publish an ORDER_CONFIRMED event to the fulfillment topic; compensation publishes ORDER_CANCELLED.

A background sweeper detects stalled sessions older than 15 minutes and drives them to the COMPENSATING state, executing compensations in reverse order.

Atomic Order Creation

Order creation uses a Postgres transaction that also marks the checkout_session as CONFIRMED. Because the payment authorization step already succeeded, the only failure mode here is a database crash. On restart the saga re-reads the saga_state, detects Step 3 incomplete, and retries the insert using the same order id (generated at saga start) making the insert idempotent via ON CONFLICT DO NOTHING.

API Design

  • POST /v1/checkout/sessions — initiates checkout for a cart, returns session_id and payment_client_secret.
  • POST /v1/checkout/sessions/{id}/confirm — buyer submits payment method; triggers saga execution.
  • GET /v1/checkout/sessions/{id} — polls current saga status; used by front-end to detect completion or failure.
  • DELETE /v1/checkout/sessions/{id} — buyer abandons checkout; releases stock locks and cart lock.

Scalability Considerations

  • Stock reservation counters in Redis provide sub-millisecond contention resolution; Postgres is the source of truth and is reconciled asynchronously.
  • Saga orchestrator is stateless; any instance can resume a session by reading saga_state from Postgres, enabling horizontal scaling without sticky routing.
  • Cart locks have short TTLs and are not held across payment network round trips — the lock covers only the reservation phase, reducing contention windows.
  • Downstream services (payment, fulfillment) are called with timeouts and retried with exponential backoff; circuit breakers prevent checkout from amplifying load during outages.
  • Read replicas serve cart reads and order confirmation pages, keeping writes on the primary small in volume.

{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “How does cart locking work with Redis SET NX in a checkout service?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Redis SET NX (set if not exists) is used to acquire a distributed lock on a cart before checkout begins. The service issues SET cart:{cartId}:lock {sessionId} NX PX {ttl}, which succeeds only if no lock exists. This prevents concurrent checkout attempts on the same cart. If the command returns nil, another process holds the lock and the request is rejected or retried with exponential backoff. The TTL ensures locks are released automatically if the process crashes.”
}
},
{
“@type”: “Question”,
“name”: “What is saga orchestration with compensation in checkout?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “A saga splits the checkout transaction into a sequence of local steps — reserve inventory, charge payment, confirm order — each with a corresponding compensation action that undoes it. An orchestrator service drives each step and, on any failure, invokes compensations in reverse order: cancel the payment authorization, release the inventory reservation. This maintains data consistency across microservices without a distributed 2PC lock.”
}
},
{
“@type”: “Question”,
“name”: “How do you achieve atomic order creation in a checkout service?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Atomic order creation combines an idempotency key with a database transaction. The service writes the order record and marks the cart as checked-out in a single DB transaction. An outbox table entry for downstream events is written in the same transaction. A background poller publishes those events only after the transaction commits, ensuring no order is created without a corresponding event and vice versa.”
}
},
{
“@type”: “Question”,
“name”: “How should payment failure rollback be handled in checkout?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “On payment failure the orchestrator triggers the compensation chain: it calls the payment gateway's void or cancel endpoint if an authorization was created, then releases the inventory reservation via the inventory service, and finally transitions the order to a FAILED state. The cart lock is released last. Each compensation step is retried with idempotency keys so duplicate calls are safe. Dead-letter queues capture steps that exhaust retries for manual review.”
}
}
]
}

See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering

See also: Shopify Interview Guide

See also: Stripe Interview Guide 2026: Process, Bug Bash Round, and Payment Systems

Scroll to Top