# Order creation stores correct initial state

- **Pattern:** `ab-001133` (`ecommerce-order-management.order-lifecycle.creation-initial-state`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001133
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001133)

## Why it matters

Orders created with the wrong initial state silently corrupt the state machine before a customer's first interaction. A guest checkout that creates orders as 'confirmed' and a webhook handler that creates them as 'processing' mean neither flow is correct — downstream transitions fire against the wrong base state, confirmation emails may trigger prematurely, inventory may decrement twice, and support staff see misleading history. CWE-841 (Improper Enforcement of Behavioral Workflow) applies directly: the system has an intended sequence, and this defect breaks it at step zero.

## Severity rationale

Critical because an incorrect initial status corrupts every downstream state transition and notification, making order history unreliable from the moment of creation.

## Remediation

Centralize all order creation behind a single function in `lib/orders/create.ts` that enforces the initial status via a named constant. Import it in every creation path — checkout route, webhook handler, admin creation — so the initial state can never diverge.

```ts
// lib/orders/constants.ts
export const ORDER_INITIAL_STATUS = 'pending' as const

// lib/orders/create.ts
import { ORDER_INITIAL_STATUS } from './constants'

export async function createOrder(data: CreateOrderInput) {
  return db.orders.create({
    data: { ...data, status: ORDER_INITIAL_STATUS },
  })
}
```

## Detection

- **ID:** `creation-initial-state`
- **Severity:** `critical`
- **What to look for:** Enumerate all code paths that create a new order record — count every distinct handler, server action, or function that inserts into the orders table. This is typically an API route handler (e.g., `POST /api/orders`, `POST /api/checkout/complete`) or a server action. For each creation path found, quote the exact status value assigned at creation time. Check whether multiple creation paths exist (guest checkout vs. authenticated checkout, webhook-triggered creation from a payment provider, admin-created orders) and whether they all use the same initial status value. Look for hardcoded strings like `"pending"`, `"new"`, `"draft"`, or `"created"`, or enum references like `OrderStatus.PENDING`. Report the count even on pass: "Found N creation paths, all assigning status 'pending'."
- **Pass criteria:** Every order creation path (at least 1 path must exist) assigns a consistent, semantically appropriate initial status — `pending`, `new`, `draft`, or `created`. No creation path skips setting a status (leaving it null or undefined) or jumps to a later state like `confirmed` or `processing` without explicit justification. Multiple creation paths all use the same initial status value. A single creation path that sets a valid initial status passes. A creation path that assigns `confirmed` or `processing` directly does not count as pass unless a payment verification step precedes it in the same transaction.
- **Fail criteria:** Any order creation path omits setting an initial status, sets an incorrect initial state (e.g., immediately `confirmed` without a payment check), or different creation paths assign different initial statuses inconsistently (e.g., guest checkout creates orders as `pending` but webhook handler creates them as `processing`).
- **Skip (N/A) when:** The project has no order creation functionality — it is a catalog, content, or pure lead generation site with no transactional order flow. Fewer than 0 order creation handlers exist in the codebase.
- **Detail on fail:** Describe what was found and where. Example: `"Guest checkout creates orders with status 'pending' (src/app/api/checkout/route.ts) but the Stripe webhook handler creates orders with status 'processing' (src/app/api/webhooks/stripe/route.ts). Initial state is inconsistent across 2 of 2 creation paths."`
- **Cross-reference:** If the initial state is `confirmed`, check whether the `confirmation-email` notification fires at creation time — skipping `pending` means the confirmation step may be implicit.
- **Remediation:** Centralize order creation behind a single function or service (e.g., `lib/orders/create.ts`) that enforces the initial state. Define the initial status as a constant and import it everywhere:

  ```ts
  // lib/orders/constants.ts
  export const ORDER_INITIAL_STATUS = 'pending' as const

  // lib/orders/create.ts
  import { ORDER_INITIAL_STATUS } from './constants'

  export async function createOrder(data: CreateOrderInput) {
    return db.orders.create({
      data: {
        ...data,
        status: ORDER_INITIAL_STATUS,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    })
  }
  ```

  All creation paths (checkout route, webhook handler, admin creation) should call `createOrder()` rather than inserting directly, ensuring the initial state is always consistent.

## External references

- cwe CWE-841
- iso-25010 functional-correctness

Taxons: data-integrity

HTML version: https://auditbuffet.com/patterns/ab-001133
