---
id: STATE-COM-ORD-001
title: Checkout lifecycle
object: DOM-COM-ORD-001
product_area: commerce
module: orders
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
states:
  - name: open
    meaning: 'Shopping is in progress, with edits, preview, holds and placement available until expiry.'
  - name: preparing
    meaning: Placement uses this intermediate status within the transaction that finishes as placed.
  - name: placed
    meaning: 'A completed sale is linked, and cart editing has ended.'
  - name: abandoned
    meaning: The cart ended without placement and its held resources were released.
  - name: cancelled
    meaning: An unused terminal status for internal cart cancellation.
transitions:
  - from: open
    to: preparing
    trigger: Placement begins its commit after remote assessment succeeds.
    guard: The locked cart retains open status and the assessed version.
    side_effects:
      - capture the seller-attested item details
      - store the bound placement calculation
      - append the preparation event
    who: Staff or portal buyer with order-placing permission
    provenance: observed
    source: 'services-commerce:modules/orders/services/placement/place-order.ts:placeOrder'
  - from: preparing
    to: placed
    trigger: The placement transaction completes its sale and dependent writes.
    guard: A resulting order and completed claims and redemptions are available.
    side_effects:
      - link the completed sale and its request key
      - append the status-change event
    who: System within the placement request
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/placement.e2e.spec.js#leaves the checkout placed, naming
      the order, its items frozen with what prepare attested, and its trail telling the story
  - from: preparing
    to: open
    trigger: The declared rollback destination permits return to shopping.
    guard: No current caller commits this transition; transaction rollback restores open instead.
    side_effects: []
    who: System
    provenance: observed
    source: 'services-commerce:modules/checkout/services/persistence/checkouts.ts:CHECKOUT_TRANSITIONS'
  - from: open
    to: abandoned
    trigger: A caller abandons shopping or the composer replaces the purchasing parties.
    guard: Status must remain open.
    side_effects:
      - return each held resource through its seller function and update the reservation record
      - record reservation release and cart closure events
    who: Staff or portal buyer with cart-update permission
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/holds-and-claims.e2e.spec.js#releases every hold when the
      checkout is abandoned
  - from: open
    to: abandoned
    trigger: The expiry pass discovers an overdue cart.
    guard: A cart that ended meanwhile is skipped.
    side_effects:
      - use the same resource-release path as requested abandonment
      - attribute the expiry transition to the system
    who: System (checkout sweeper)
    provenance: observed
    source: 'services-commerce:modules/checkout/services/checkout/checkout-sweeper.ts:sweepExpiredCheckouts'
  - from: open
    to: cancelled
    trigger: The map permits internal cart cancellation without a current caller.
    guard: Status must remain open.
    side_effects: []
    who: Nobody today
    provenance: observed
    source: 'services-commerce:modules/checkout/services/persistence/checkouts.ts:CHECKOUT_TRANSITIONS'
invalid:
  - from: placed
    to: open
    why: Completed placement is retained and repeated placement returns the existing sale.
  - from: abandoned
    to: open
    why: Abandoned carts stay ended; another purchase needs a fresh cart.
  - from: open
    to: placed
    why: The service map requires the preparing stage before placed.
  - from: cancelled
    to: open
    why: Cancelled is a terminal service status.
concurrency: >-
  The cart lock orders placement and edits. Expiry discovery runs every minute in bounded pages, so backlog
  can delay processing. The intended cleanup of finished carts is not implemented.
invariants:
  - INV-DOM-COM-ORD-001-01
  - INV-DOM-COM-ORD-001-04
  - INV-DOM-COM-ORD-001-05
evidence:
  - >-
    services-commerce at c8b66a98: modules/checkout/services/persistence/checkouts.ts (CHECKOUT_TRANSITIONS,
    transitionCheckout); services/checkout/checkout-workflow.ts (abandonReleasingHolds); checkout-sweeper.ts;
    modules/orders/services/placement/place-order.ts.
---

# Checkout lifecycle

## The Lifecycle

```mermaid
stateDiagram-v2
  [*] --> open: cart opened
  open --> preparing: placement (inside its transaction)
  preparing --> placed: order written
  preparing --> open: declared, never committed
  open --> abandoned: abandoned by a person or expired
  open --> cancelled: declared, unused
  placed --> [*]
  abandoned --> [*]
  cancelled --> [*]
```

## Transitions Explained

The cart starts open. Placement performs remote preparation first, then moves through preparing to placed within a single transaction. Failure rolls that transaction back to the still-open cart; abandonment instead releases holds and ends shopping.

## What Cannot Happen

Terminal carts have no outgoing service transitions. Direct placement from open is absent from the map. Cancelled and the explicit preparing-to-open path are declared without current committed callers.

## Concurrency and Timing

The cart lock orders placement and edits. Expiry discovery runs every minute in bounded pages, so backlog can delay processing. The intended cleanup of finished carts is not implemented.
