Checkout State Machine
The checkout system implements a robust state machine that ensures transactional consistency and prevents race conditions during the purchase process. The state machine coordinates cart locking, payment processing, and order creation with atomic transitions.
State Definitions
Core States
enum CheckoutState {
CREATED = "CREATED", // Initial state after cart conversion
LOCKED = "LOCKED", // Cart locked, inventory reserved
PAYMENT_PENDING = "PAYMENT_PENDING", // Payment intent created
PAYMENT_CONFIRMED = "PAYMENT_CONFIRMED", // Payment verified
ORDER_CREATED = "ORDER_CREATED", // Order record created
COMPLETED = "COMPLETED", // Checkout fully processed
FAILED = "FAILED", // Checkout failed (terminal)
}
Terminal States
- COMPLETED: Successful checkout completion
- FAILED: Checkout failure (payment declined, inventory issues, etc.)
State Transition Rules
Valid Transitions
const TRANSITION_RULES = {
[CREATED]: [LOCKED],
[LOCKED]: [PAYMENT_PENDING, FAILED],
[PAYMENT_PENDING]: [PAYMENT_CONFIRMED, FAILED],
[PAYMENT_CONFIRMED]: [ORDER_CREATED],
[ORDER_CREATED]: [COMPLETED],
[COMPLETED]: [], // Terminal
[FAILED]: [], // Terminal
}
Transition Flow
CREATED → LOCKED → PAYMENT_PENDING → PAYMENT_CONFIRMED → ORDER_CREATED → COMPLETED
↓ ↓
FAILED FAILED
Checkout Session Data Structure
Session State
interface CheckoutSession {
state: CheckoutState;
cartId: string;
paymentIntentId: string | null;
orderId: string | null;
updatedAt: string;
}
Metadata Storage
interface CheckoutMetadata {
customerId: string;
userId: string | null;
shippingAddressId: string;
billingAddressId: string;
shippingCost: number;
discountSnapshot: DiscountSnapshot | null;
pricingSnapshot: PricingSnapshot | null;
createdAt: string;
}
State Machine Implementation
Atomic Transitions
The state machine uses Lua scripts in Redis to ensure atomic state transitions:
-- transition-state.lua
local key = KEYS[1]
local expectedState = ARGV[1]
local newState = ARGV[2]
local metadata = ARGV[3]
-- Get current state
local current = redis.call('GET', key)
if not current then
return redis.call('SET', key, newState)
end
-- Validate transition
if current ~= expectedState then
return redis.call('ERROR', 'Invalid transition')
end
-- Atomic transition
redis.call('SET', key, newState)
if metadata then
redis.call('SET', key .. ':metadata', metadata)
end
return 'OK'
Transition Validation
function validateTransition(from: CheckoutState, to: CheckoutState): void {
if (!TRANSITION_RULES[from].includes(to) && from !== to) {
throw new Error(`Invalid transition: ${from} → ${to}`);
}
}
Checkout Process Flow
1. Cart to Checkout Conversion
State: CREATED
Action: Convert cart to checkout session
async createCheckout(cartId: string): Promise<CheckoutSession> {
// Validate cart exists and has items
// Create checkout session with CREATED state
// Return session ID for frontend tracking
}
2. Inventory Locking
State: CREATED → LOCKED
Action: Reserve inventory for all cart items
async lockCheckout(checkoutId: string): Promise<void> {
// Reserve inventory for each variant
// Calculate final pricing with discounts
// Store pricing and discount snapshots
// Transition to LOCKED state
}
Atomic Operation:
- Reserve all inventory or fail completely
- Calculate and freeze pricing
- Store immutable snapshots
3. Payment Intent Creation
State: LOCKED → PAYMENT_PENDING
Action: Create payment gateway intent
async createPaymentIntent(checkoutId: string): Promise<PaymentIntent> {
// Get checkout metadata
// Create Razorpay order
// Store payment intent ID
// Transition to PAYMENT_PENDING
}
Payment Intent Data:
interface PaymentIntent {
id: string;
amount: number;
currency: string;
status: PaymentIntentStatus;
orderId: string | null;
}
4. Payment Confirmation
State: PAYMENT_PENDING → PAYMENT_CONFIRMED
Action: Verify payment completion
async confirmPayment(checkoutId: string, paymentId: string): Promise<void> {
// Verify payment with Razorpay
// Update payment intent status
// Transition to PAYMENT_CONFIRMED
}
5. Order Creation
State: PAYMENT_CONFIRMED → ORDER_CREATED
Action: Create order record
async createOrder(checkoutId: string): Promise<Order> {
// Create order from checkout metadata
// Consume reserved inventory
// Store order ID in session
// Transition to ORDER_CREATED
}
6. Checkout Completion
State: ORDER_CREATED → COMPLETED
Action: Finalize checkout
async completeCheckout(checkoutId: string): Promise<void> {
// Send order confirmation
// Clean up checkout session
// Transition to COMPLETED
}
Error Handling & Recovery
Failure Scenarios
Inventory Unavailable
LOCKED → FAILED
- Release all reserved inventory
- Mark checkout as failed
- Notify customer of stock issues
Payment Declined
PAYMENT_PENDING → FAILED
- Release inventory reservations
- Cancel payment intent
- Mark checkout as failed
Order Creation Failure
PAYMENT_CONFIRMED → FAILED
- Refund payment if possible
- Release inventory reservations
- Alert administrators
Recovery Mechanisms
Session Expiration
- Automatic cleanup after TTL (30 minutes)
- Inventory reservations released
- Payment intents cancelled
Manual Recovery
async recoverFailedCheckout(checkoutId: string): Promise<void> {
// Assess current state
// Attempt recovery based on failure point
// Manual intervention for complex cases
}
Redis Key Patterns
Checkout Session Keys
checkout:session:{checkoutId} → CheckoutSession
checkout:session:{checkoutId}:metadata → CheckoutMetadata
checkout:session:{checkoutId}:payment → PaymentIntent
Expiration Strategy
- Active Sessions: 30-minute TTL
- Completed Sessions: Immediate cleanup
- Failed Sessions: 24-hour retention for debugging
API Endpoints
Checkout Lifecycle
# Create checkout from cart
POST /storefront/checkout
{
"cartId": "cart-123",
"customerId": "customer-456"
}
# Lock checkout (reserve inventory)
POST /storefront/checkout/{checkoutId}/lock
# Create payment intent
POST /storefront/checkout/{checkoutId}/payment-intent
# Confirm payment
POST /storefront/checkout/{checkoutId}/confirm-payment
{
"paymentId": "pay_123",
"orderId": "order_123"
}
# Complete checkout
POST /storefront/checkout/{checkoutId}/complete
Admin Operations
# Get checkout session details
GET /admin/checkout/{checkoutId}
# Force state transition (emergency)
POST /admin/checkout/{checkoutId}/transition
{
"toState": "FAILED",
"reason": "Payment timeout"
}
# List active checkouts
GET /admin/checkout/active
Monitoring & Observability
State Transition Metrics
// Track state transition times
checkout_state_transition_duration: histogram
// Count state transitions
checkout_state_transitions_total: counter
// Track failed checkouts
checkout_failures_total: counter
Business Metrics
// Checkout conversion rates
checkout_conversion_rate: gauge
// Average checkout completion time
checkout_completion_duration: histogram
// Checkout abandonment rate
checkout_abandonment_rate: gauge
Performance Optimization
Caching Strategy
- Session Data: Cached in Redis with TTL
- Pricing Snapshots: Immutable after locking
- Inventory Checks: Cached during active sessions
Concurrency Control
- Atomic Operations: Lua scripts prevent race conditions
- Optimistic Locking: Version-based conflict resolution
- Sequential Processing: Single-threaded state transitions
Security Considerations
Data Protection
- Sensitive Data: Encrypted in transit and at rest
- Access Control: Customer-scoped checkout access
- Audit Trail: Complete state change history
Fraud Prevention
- Session Validation: Verify session ownership
- Payment Verification: Gateway callback validation
- Rate Limiting: Prevent checkout spam
Best Practices
State Machine Design
- Explicit States: No inferred state, explicit fields only
- Atomic Transitions: All-or-nothing state changes
- Validation: Strict transition rules enforced
- Recovery: Clear failure and recovery paths
Business Logic
- Inventory First: Reserve before payment to prevent overselling
- Snapshot Pricing: Freeze prices at checkout time
- Idempotent Operations: Safe retry of failed operations
- Cleanup: Automatic resource cleanup on failures
Monitoring
- State Distribution: Track checkout states over time
- Failure Analysis: Identify common failure points
- Performance Tracking: Monitor checkout completion times
- Conversion Optimization: Analyze abandonment points