Order creation stores correct initial state
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.
// 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 likeOrderStatus.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, orcreated. No creation path skips setting a status (leaving it null or undefined) or jumps to a later state likeconfirmedorprocessingwithout 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 assignsconfirmedorprocessingdirectly 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
confirmedwithout a payment check), or different creation paths assign different initial statuses inconsistently (e.g., guest checkout creates orders aspendingbut webhook handler creates them asprocessing). -
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 theconfirmation-emailnotification fires at creation time — skippingpendingmeans 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.
External references
- cwe · CWE-841 — Improper Enforcement of Behavioral Workflow
- iso-25010:2011 · functional-correctness — Functional Correctness
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ecommerce-order-management·automated