All 20 checks with why-it-matters prose, severity, and cross-references to related audits.
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.
Why this severity: Critical because an incorrect initial status corrupts every downstream state transition and notification, making order history unreliable from the moment of creation.
ecommerce-order-management.order-lifecycle.creation-initial-stateSee full patternWithout application-layer transition guards, any code path — including a customer-facing API route — can write an arbitrary status value to any order. An attacker who discovers the order update endpoint can skip 'confirmed' and 'shipped' to mark their own order 'delivered', triggering fulfillment or refund logic without a real purchase completing the intervening steps. Even without malice, a race condition or a bug in an admin tool can jump an order from 'pending' to 'delivered' in one call, corrupting history and triggering incorrect emails. A database enum constraint alone (CWE-841) does not prevent logically invalid sequences — application-layer guards must enforce the allowed graph.
Why this severity: Critical because unguarded transitions allow both malicious status manipulation by authenticated users and accidental logical corruption that invalidates order history and triggers incorrect downstream actions.
ecommerce-order-management.order-lifecycle.state-transitionsSee full patternWithout a persistent order history log, you cannot answer the questions that matter most during a dispute or refund: When did the order ship? Who changed the status? Was the cancellation before or after fulfillment? The only surviving evidence is the current `status` field and a single `updatedAt` timestamp that gets overwritten on every subsequent change. CWE-778 (Insufficient Logging) and GDPR Art. 5(1)(f) both require that processing activities are traceable and reconstructable. An `updatedAt` column proves nothing — it's a cursor, not a log.
Why this severity: High because missing history makes it impossible to reconstruct the order timeline during disputes, refund requests, or compliance audits, and the gap can never be backfilled after the fact.
ecommerce-order-management.order-lifecycle.history-logSee full patternIf the only record of order confirmation is the `status` field flipping to 'confirmed', there is no queryable timestamp for 'orders confirmed in the last 7 days', no display value for 'Confirmed on March 12' in the customer order detail view, and no audit anchor distinct from subsequent status changes. CWE-841 covers workflows where the state machine advances correctly but the associated data capture is incomplete. A `confirmedAt` timestamp is a lightweight addition with disproportionate operational value — it powers fulfillment metrics, SLA tracking, and customer-facing order timelines.
Why this severity: Low because the order flow itself is not broken — the missing metadata only becomes a problem when querying, reporting, or displaying confirmation timing.
ecommerce-order-management.order-lifecycle.confirmation-recordSee full patternWithout a `deliveredAt` timestamp, any query for fulfillment metrics — average time from ship to delivery, on-time delivery rate, SLA compliance — becomes impossible or relies on `updatedAt`, which gets overwritten by post-delivery events like refund requests. If the customer contacts support asking when their package arrived, the platform has no answer beyond 'the carrier said so'. Capturing delivery time at the moment the status transitions is a one-line write that prevents a permanent data gap — a gap that can never be filled after the fact.
Why this severity: Low because delivery timestamp absence only affects reporting and display quality; the order flow itself continues correctly without it.
ecommerce-order-management.order-lifecycle.delivery-timestampSee full patternMarking an order 'shipped' with no tracking information means the customer cannot follow their package, support staff cannot look up the shipment when a delivery dispute arises, and the shipment email (if it exists) contains nothing actionable. A 'your order has shipped' notification with no tracking number is effectively a lie by omission — it closes the customer's window of action while giving them nothing to work with. This gap also cascades into the OWASP A01 risk surface if the tracking update endpoint accepts shipping confirmation without validating that the actor has captured required fulfillment data.
Why this severity: High because an order marked shipped without tracking data leaves customers unable to monitor delivery and support staff unable to investigate loss or delay claims.
ecommerce-order-management.order-lifecycle.tracking-infoSee full patternMissing pre-shipment cancellation is a direct OWASP A01 (Broken Access Control) and CWE-285 gap: a customer has a legitimate right to cancel an order they just placed, and if no endpoint exists to exercise that right, they lose money or are forced to contact support for a routine action. Beyond customer experience, an absent cancellation flow means that if a payment is captured for an order that should not have been fulfilled — duplicate order, payment error, customer mistake — there is no programmatic path to unwind it. The handler must also guard against cancelling already-shipped orders through the same path, which could incorrectly trigger a refund for a package in transit.
Why this severity: High because the absence of a guarded cancellation endpoint forces customers to contact support for a routine action and creates no programmatic path to unwind incorrectly captured payments.
ecommerce-order-management.cancellation-refunds.preshipment-cancellationSee full patternAn order that was shipped yesterday cannot be cancelled the same way as an order placed five minutes ago. If the same code path applies to both, a customer can trigger a 'cancelled' transition on an already-shipped order — potentially generating an automated refund while the package is still in transit and arrives at the customer's door. This is a CWE-285 failure: the system grants a cancellation action to a user for an order state where that action should be restricted to admin review. Post-shipment cases require a separate branch: queue the request, notify the admin, and communicate a different resolution timeline to the customer.
Why this severity: Medium because a single undifferentiated cancellation path enables customers to cancel shipped orders with no admin oversight, creating refund and logistics conflicts.
ecommerce-order-management.cancellation-refunds.postshipment-cancellationSee full patternA cancellation that only flips the `status` field leaves no durable record of who cancelled, when, or why. This is a CWE-778 gap and a GDPR Art. 5(1)(f) issue: processing activities must be accountable, and 'the order is cancelled' with no supporting event log is not accountable. Without a history entry, support staff cannot determine whether the cancellation was customer-initiated or admin-initiated, and refund disputes have no audit anchor. Both writes — status update and history entry — must be atomic in a transaction; a non-transactional write creates a race condition where one operation can succeed and the other fail.
Why this severity: High because a cancellation with no history entry eliminates the audit trail needed for refund disputes, compliance checks, and post-incident support investigations.
ecommerce-order-management.cancellation-refunds.cancellation-historySee full patternWhen refund state is encoded only as `order.status = 'refunded'`, the system cannot represent intermediate states: a refund that was requested but not yet processed, a refund that the payment provider rejected, or an order that was cancelled without a refund being issued at all (e.g., store credit was given instead). This is an iso-25010:2011 functional correctness failure and a GDPR Art. 17 (right to erasure / financial correction) accountability gap. Separate refund tracking also enables storing the provider's refund ID — essential for reconciling Stripe refunds against your own records and for debugging partial or failed refunds.
Why this severity: High because a single 'refunded' status value cannot distinguish between pending, processing, completed, or failed refund states, making financial reconciliation and dispute resolution unreliable.
ecommerce-order-management.cancellation-refunds.refund-status-separateSee full patternAll-or-nothing refunds create a real business problem: a customer receives a damaged item among a multi-item order and is entitled to a partial refund. If the refund handler always calls `stripe.refunds.create()` with no `amount` parameter, the only option is a full refund — over-refunding destroys margin. The iso-25010:2011 functional correctness gap here is the absence of an `amount` field on the refund interface, and the GDPR Art. 17 dimension is the inability to correctly reflect the agreed financial correction in the stored record. The validation preventing total refunds from exceeding order total is equally important — without it, a bug or a race condition can issue more money than was collected.
Why this severity: Medium because all-or-nothing refunds force over-refunding in partial-damage scenarios and prevent accurate financial reconciliation across multi-item orders.
ecommerce-order-management.cancellation-refunds.partial-refundsSee full patternWithout a defined refund window, refunds can theoretically be issued for orders cancelled two years ago — a refund policy violation that a simple date comparison prevents. Beyond fraud prevention, the absence of a configured timeframe means your refund policy (stated in terms and conditions) and your code are inconsistent: the policy says '30 days', the system enforces no limit. CWE-285 applies when a time-based access control exists in policy but not in code. The fix is a single numeric constant — `REFUND_WINDOW_DAYS` in `lib/orders/config.ts` — that makes the policy machine-enforceable and auditable.
Why this severity: Low because the practical risk requires a customer to request a refund long after the window and the system to process it, which is unlikely but not impossible without the guard.
ecommerce-order-management.cancellation-refunds.refund-timeframeSee full patternA customer who pays for an order and receives no confirmation email has no evidence that the transaction occurred. From their perspective, they may have been charged without a successful order — leading to chargebacks, duplicated purchases, and unnecessary support contacts. This is an iso-25010:2011 reliability failure: the system accepted a payment but failed to complete its contractual obligation to the customer. The confirmation email is also the canonical moment to surface the order number, line items, and delivery estimate — omitting it makes every subsequent customer question ('where is my order?') harder to handle.
Why this severity: Critical because failing to send a confirmation email after a successful payment leaves customers with no proof of purchase and no reference number, directly causing chargebacks and support escalations.
ecommerce-order-management.notifications.confirmation-emailSee full patternA shipment notification that omits tracking information is worse than no notification at all: it tells the customer the package has left the warehouse but gives them nothing to act on. Without tracking data in the email, the customer must contact support or log in to the account page to find the tracking number — and if they can't find it, the next call is to their credit card company. This is an iso-25010:2011 reliability gap: the email was sent but failed to deliver the information that makes it useful. The tracking number and carrier captured at the 'shipped' transition (see `tracking-info`) must flow directly into the email template.
Why this severity: High because a shipment email without tracking data gives customers no ability to monitor delivery, turning every 'where is my order?' contact into a preventable support ticket.
ecommerce-order-management.notifications.shipment-emailSee full patternDelivery confirmation from the carrier is a natural trigger for re-engagement — a 'your order arrived' email is the highest-open-rate message in ecommerce. Without it, the customer only knows their order arrived because they were home when it showed up. The platform misses the opportunity to prompt a review, confirm customer satisfaction, or detect a non-delivery claim early. This is an iso-25010:2011 reliability issue: the system receives or computes a delivery event and fails to act on it. The delivery email also serves as the customer's receipt of physical delivery, which matters if they later dispute receiving the item.
Why this severity: Medium because missing delivery notification skips the highest-engagement touchpoint in the order lifecycle and leaves the platform blind to delivery disputes until the customer contacts support.
ecommerce-order-management.notifications.delivery-emailSee full patternA customer who cancels an order — or whose order is admin-cancelled — and receives no email confirmation is left wondering whether the cancellation actually went through. Without notification, they may place a duplicate order, not monitor their bank account for the refund, or contact support unnecessarily. If the admin cancels an order on behalf of a customer (for a fraud flag, inventory issue, or address problem), the customer deserves immediate notification with context. The iso-25010:2011 reliability dimension: the cancellation event occurred in the system but the customer has no evidence of it.
Why this severity: Low because cancellation notification failures inconvenience customers and increase support volume without creating financial or security risk.
ecommerce-order-management.notifications.cancellation-emailSee full patternA customer who requests a refund and receives no acknowledgment has no idea whether the request was received, is being processed, or was silently rejected. Under GDPR Art. 12, data subjects must receive transparent communication about actions taken on their financial data. Without a refund initiation email, customers default to disputing the charge with their bank — a chargeback that costs you the dispute fee on top of the refund itself. The notification is also the correct moment to set expectations: '5–10 business days' in an email beats a support ticket asking the same question.
Why this severity: Low because missing refund notifications increase chargeback rates and support contact volume without directly corrupting order data or enabling unauthorized access.
ecommerce-order-management.notifications.refund-emailSee full patternAn admin who cannot search orders by identifier cannot do their job. When a customer contacts support with 'my order ORD-48291 hasn't arrived', the admin must be able to pull up that record in under 10 seconds. Without server-side search across at least order ID and customer email, admins are left scrolling a paginated list — and for stores with hundreds of orders, that means support contacts go unresolved or require direct database access. CWE-285 applies here in an operational context: the admin role lacks the access controls and tooling its responsibilities require. iso-25010:2011 operability demands that the system is usable by the people maintaining it.
Why this severity: High because the inability to search orders by identifier makes customer support operationally impossible at any meaningful order volume, forcing admins to use direct database access.
ecommerce-order-management.admin-management.order-searchSee full patternAn admin detail view that shows only the current order status gives a misleading picture: the order is 'shipped', but was it ever in 'confirmed'? Was it cancelled and re-opened? Did the admin manually override a status? Without the full history timeline, admins cannot diagnose processing anomalies, verify that the correct notifications fired at each step, or respond to customer complaints about 'the order status changed without warning.' CWE-778 and GDPR Art. 5(1)(f) both require that the audit trail is accessible, not just stored — data in a table that no UI surfaces is operationally equivalent to data that was never captured.
Why this severity: Medium because order history data may exist in the database but is worthless if not surfaced in the admin UI, turning every dispute into a manual SQL query.
ecommerce-order-management.admin-management.order-history-viewSee full patternSearch by identifier (handled by `order-search`) and structured filtering are complementary and both required. An admin who can find a specific order by ID but cannot filter to 'all pending orders from the last 7 days' cannot manage their queue proactively — they can only react to inbound support contacts. iso-25010:2011 operability requires that the system supports the work patterns of its operators. For order operations, that means at minimum: filter by status (to work a specific queue) and filter by date range (to find orders placed in a given period). Without server-side filtering, a client-side filter on a paginated list gives false confidence while silently excluding records off the first page.
Why this severity: Low because the absence of structured filters degrades admin efficiency but does not prevent order processing — admins can still use search and pagination to manage orders manually.
ecommerce-order-management.admin-management.order-filteringSee full patternRun this audit in your AI coding tool (Claude Code, Cursor, Bolt, etc.) and submit results here for scoring and benchmarks.
Open Order Management Audit