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.
Critical because an incorrect initial status corrupts every downstream state transition and notification, making order history unreliable from the moment of creation.
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.
// 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 },
})
}
ID: ecommerce-order-management.order-lifecycle.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:
// 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.