Core Entities
Cart: cart_id, user_id (NULL for guest), session_id (for guest carts), status (ACTIVE, ABANDONED, CHECKED_OUT), currency, created_at, updated_at, expires_at. CartItem: item_id, cart_id, product_id, variant_id (size/color), quantity, unit_price (captured at add-to-cart time), custom_options (JSONB: gift wrap, personalization text). Product: product_id, name, description, base_price, currency, stock_quantity, is_active. ProductVariant: variant_id, product_id, sku, attributes (JSONB: size, color), price_override (NULL = use base_price), stock_quantity. Discount: discount_id, code, type (PERCENTAGE, FIXED_AMOUNT, FREE_SHIPPING, BUY_X_GET_Y), value, min_order_amount, max_uses, uses_count, valid_from, valid_until, applicable_products (NULL = all). CartDiscount: cart_id, discount_id, applied_amount. SavedForLater: user_id, product_id, variant_id, saved_at.
Cart Persistence Strategy
class CartService:
# Guest carts: stored in Redis with TTL (30 days)
# Authenticated carts: stored in both Redis (cache) and DB (persistent)
def get_cart(self, cart_id: str, user_id: Optional[int]) -> Cart:
# Try Redis first (fast path)
cached = self.redis.get(f"cart:{cart_id}")
if cached:
return Cart.from_json(cached)
# Cache miss: load from DB (authenticated users only)
if user_id:
cart = self.db.get_cart_by_user(user_id)
if cart:
self.redis.setex(f"cart:{cart_id}", 86400, cart.to_json())
return cart
return None # guest cart expired
def add_item(self, cart_id: str, product_id: int,
variant_id: int, quantity: int) -> Cart:
with db.transaction():
# Validate product availability
variant = self.db.get_variant(variant_id)
if not variant.is_available or variant.stock_quantity < quantity:
raise InsufficientStock(variant_id)
# Capture current price (price can change after add-to-cart)
unit_price = variant.price_override or variant.product.base_price
# Upsert cart item
existing = self.db.get_cart_item(cart_id, variant_id)
if existing:
new_qty = existing.quantity + quantity
if variant.stock_quantity < new_qty:
raise InsufficientStock(variant_id)
self.db.update_cart_item(existing.item_id, {"quantity": new_qty})
else:
self.db.insert_cart_item({
"cart_id": cart_id, "product_id": product_id,
"variant_id": variant_id, "quantity": quantity,
"unit_price": unit_price
})
cart = self.db.get_cart(cart_id)
self.redis.setex(f"cart:{cart_id}", 86400, cart.to_json())
return cart
Price Calculation and Discount Engine
class PricingEngine:
def calculate(self, cart: Cart,
discount_code: Optional[str] = None) -> CartSummary:
subtotal = sum(item.unit_price * item.quantity for item in cart.items)
# Validate and apply discount
discount_amount = 0
if discount_code:
discount = self.db.get_discount(discount_code)
error = self._validate_discount(discount, cart, subtotal)
if error:
raise DiscountError(error)
discount_amount = self._compute_discount(discount, cart, subtotal)
# Shipping calculation
shipping = self._calculate_shipping(cart, subtotal - discount_amount)
# Tax calculation (by destination address)
taxable = subtotal - discount_amount + shipping
tax = self._calculate_tax(taxable, cart.shipping_address)
return CartSummary(
subtotal=subtotal,
discount=discount_amount,
shipping=shipping,
tax=tax,
total=subtotal - discount_amount + shipping + tax
)
def _validate_discount(self, d, cart, subtotal) -> Optional[str]:
if not d or not d.is_active: return "Invalid discount code"
if d.valid_until = d.max_uses: return "Discount limit reached"
if subtotal < d.min_order_amount: return f"Minimum order ${d.min_order_amount}"
return None
Checkout and Inventory Reservation
Checkout flow must atomically reserve inventory and create the order to prevent overselling. Step 1: Price recalculation. Recalculate prices at checkout time (not at add-to-cart time) to catch price changes. Notify user if any price changed. Step 2: Inventory reservation. Use SELECT FOR UPDATE on product_variants rows for all items. Verify stock >= quantity for each item. Decrement stock atomically in the same transaction. If any item is out of stock: rollback and return a specific error per item. Step 3: Order creation. Create Order and OrderItem records with the final prices. Set cart.status = CHECKED_OUT. Step 4: Payment. Create a Stripe PaymentIntent with the final total. On payment failure: reverse inventory reservation (increment stock back). On payment success: confirm the order and trigger fulfillment. Idempotency: the checkout endpoint accepts an idempotency_key (UUID) from the client. If the same key is submitted twice (network retry), return the existing result without double-processing.
See also: Shopify Interview Prep
See also: Stripe Interview Prep
See also: Airbnb Interview Prep