Skip to Content
FeaturesOrders

Orders

Orders track customer purchases through a state machine. Unlike the cart (a cookie), an order row is only created when checkout starts.

Order States

created → payment_pending → paid → shipped → delivered ↓ ↓ ↓ cancelled cancelled cancelled
StateDescription
createdDraft checkout (active=true)
payment_pendingCheckout submitted, becomes a real order
paidPayment received
shippedShipped to customer
deliveredReceived by customer
cancelledOrder cancelled

Allowed transitions are enforced by STATE_TRANSITIONS in src/lib/server/services/order-utils.ts. created, payment_pending, and paid can each move to cancelled; shipped can only move to delivered.

Starting Checkout

The cart cookie first touches the database in orderService.startCheckout. It creates (or resumes) a draft order, snapshots the cookie lines into order_lines with prices/names/tax from getCartView, and reserves stock for 15 minutes.

src/lib/server/services/orders.ts
const { order, checkoutToken, isNew, stockErrors } = await orderService.startCheckout(cartLines, { customerId, checkoutToken });
  • The draft is active=true, state=created, identified by the checkout_token cookie (the orders.checkoutToken column).
  • If the cookie no longer matches the draft’s lines, the draft is wiped and rebuilt.
  • Quantities are clamped to available stock; anything unavailable is reported in stockErrors rather than failing the whole checkout.

Stock reservations are created only at checkout and expire after 15 minutes. There is no background job — expired reservations are cleaned up opportunistically at checkout entry (reservationService.deleteExpired).

State Transitions

src/lib/server/services/orders.ts
await orderService.transitionState(orderId, "payment_pending"); await orderService.transitionState(orderId, "paid"); await orderService.transitionState(orderId, "shipped"); await orderService.transitionState(orderId, "delivered");

Key side effects:

  • payment_pending — the draft becomes a real order (active=false) and orderPlacedAt is set.
  • paid — stock is validated one final time, reserved stock is permanently deducted from tracked variants, reservations are released, and promotion usage counts increment.
  • cancelled — stock is restored if the order was already paid/shipped, and any reservations are released.

Order Lines

Lines snapshot the product/variant name, SKU, and pricing (including a full tax breakdown) at checkout time:

order.lines = [ { variantId: 456, productName: "Blue Shirt", variantName: "Medium", sku: "SHIRT-M", quantity: 2, unitPrice: 2999, lineTotal: 5998, taxCode: "standard", taxRate: 2400, // basis points, 4 decimals (0.24) taxAmount: 1160, unitPriceNet: 2419, lineTotalNet: 4838 } ];

Order Code

Orders have a human-readable code like ORD-XXXXXXXX:

const order = await orderService.getByCode("ORD-AB12CD34");

Guest Orders

Orders can be placed without an account. Guest checkouts are tracked by the checkout_token cookie and store customerEmail directly on the order.

currencyCode and exchangeRate columns exist on orders, but they are vestigial — EUR is hardcoded and no currency conversion is performed.

Last updated on