# System Design Interview: Design an E-Commerce Order and Checkout System

Source: https://www.techinterview.org/post/3233463028/system-design-ecommerce-order-system/
Updated: 2026-04-17 · techinterview.org

## What Is an E-Commerce Order System?

An e-commerce order system manages the full lifecycle of an online purchase: cart management, checkout, inventory reservation, payment processing, and order fulfillment. Examples: Amazon Orders, Shopify checkout, eBay purchases. Core challenges: concurrent inventory management (two buyers purchasing the last item), payment atomicity, and order state machine across distributed services.

- [Lyft Interview Guide](https://techinterview.org/lyft-software-engineer-interview/)

- [Coinbase Interview Guide](https://techinterview.org/coinbase-software-engineer-interview/)

- [DoorDash Interview Guide](https://techinterview.org/doordash-software-engineer-interview/)

- [Airbnb Interview Guide](https://techinterview.org/airbnb-software-engineer-interview/)

- [Stripe Interview Guide](https://techinterview.org/stripe-software-engineer-interview/)

- [Shopify Interview Guide](https://techinterview.org/shopify-software-engineer-interview/)

## System Requirements

### Functional

  - Shopping cart: add/remove items, persist across sessions

  - Checkout: reserve inventory, compute total, process payment

  - Order management: view, cancel, track fulfillment status

  - Inventory management: track stock, prevent overselling

### Non-Functional

  - 100M users, 10K checkouts/second at peak (Black Friday)

  - No overselling: never confirm an order when stock is 0

  - Payment atomicity: charge once per order

## Core Data Model


```
products: id, name, price_cents, sku
inventory: product_id, warehouse_id, quantity_available, quantity_reserved
carts: id, user_id, created_at, updated_at
cart_items: cart_id, product_id, quantity, price_at_add
orders: id, user_id, status, subtotal, tax, shipping, total, created_at
order_items: order_id, product_id, quantity, unit_price
payments: id, order_id, amount, status, idempotency_key, provider_charge_id
```


## Cart Implementation

Carts need fast read/write and do not require ACID guarantees. Store in Redis (TTL 30 days):


```
HSET cart:{user_id} product_id:123 quantity:2
HSET cart:{user_id} product_id:456 quantity:1
EXPIRE cart:{user_id} 2592000  # 30 days
```

On checkout: read the cart from Redis, validate prices against current product prices (prices may have changed), then persist as an order in the DB.

## Checkout Flow — Preventing Overselling


```
BEGIN TRANSACTION;
-- 1. Lock inventory rows for all items in the cart
SELECT quantity_available, quantity_reserved
FROM inventory
WHERE product_id IN (123, 456) AND warehouse_id = ?
FOR UPDATE;

-- 2. Check each product has sufficient quantity
-- (quantity_available - quantity_reserved >= ordered_quantity)

-- 3. Reserve inventory (not yet deducted -- reserved until fulfilled)
UPDATE inventory
SET quantity_reserved = quantity_reserved + ordered_qty
WHERE product_id = ?;

-- 4. Create order record
INSERT INTO orders (...) VALUES (...);
INSERT INTO order_items (...) VALUES (...);
COMMIT;
```

If any product check fails: rollback with an "out of stock" error. The transaction ensures atomicity — two concurrent checkouts for the last item both acquire the lock; one succeeds, the other gets the updated (insufficient) quantity and rolls back.

## Payment Processing

After inventory is reserved, charge the payment method:


```
charge = stripe.charge.create(
    amount=total_cents,
    customer=stripe_customer_id,
    idempotency_key=f"order-{order_id}"
)
```

On payment success: update order status to CONFIRMED, send confirmation email. On payment failure: release the reserved inventory (decrement quantity_reserved), update order status to PAYMENT_FAILED.

## Order State Machine


```
PENDING_PAYMENT → CONFIRMED → PROCESSING → SHIPPED → DELIVERED
      │                │
  PAYMENT_FAILED    CANCELLED (before shipment)
```


## Inventory at Scale

Sharding inventory by product_id distributes write load. But for flash sales (Nike shoe drop: 10K buyers competing for 1K pairs), the single-product inventory row becomes a hotspot with thousands of concurrent locks. Solutions:

  - Inventory partitioning: split 1K units across 10 virtual warehouse rows (100 units each). Random assignment distributes lock contention 10x.

  - Pre-sale reservation: allow users to queue, assign inventory in batches every 30 seconds.

  - Optimistic locking with retry: use version column instead of FOR UPDATE; retry on conflict (works for low contention, not flash sales).

## Interview Tips

  - Separate cart (Redis) from orders (SQL) — different consistency requirements.

  - quantity_reserved vs quantity_available prevents deducting before payment confirms.

  - Idempotency key = order_id ensures exactly-one charge.

  - Inventory partitioning for flash sales shows awareness of hotspot problems.
