POST /orders reference
Implementation notes and runnable test cases for the order-placement endpoint. Each case fires a real request and explains the backend branch it triggers.
/ordersreserve → charge → confirm sagaPlaces an order: validates the cart, geocodes the destination, picks the nearest warehouse that can fill every line item, reserves inventory under a row lock, charges the card outside the lock, and confirms the order. The cases below run real requests against the API and explain exactly which branch each one exercises.
Implementation: the saga, step by step
Idempotency replay
If an Idempotency-Key header is present and already maps to an order, return that order immediately. Duplicate product lines in the body are also rejected here (they would defeat the per-line stock check).
Validate products & compute total
Load every productId in one findMany. A length mismatch → 404. The order total is computed up front in integer cents (Math.round(price*100)) to avoid binary-float drift, so it is available even for a later-failing attempt.
Geocode the shipping address
GeocodingService turns the address into { lat, lng }. Runs only after cheap validation passes, so malformed orders never pay the geocoding cost.
Rank eligible warehouses
findEligibleByDistance() returns warehouses that can fill EVERY line item from one location, sorted by haversine distance. Empty list → 422 (no single-warehouse fulfilment).
Reserve (short transaction)
For each warehouse nearest-first: lock inventory rows FOR UPDATE, re-validate quantities, decrement, and create the Order as PENDING. A lost stock race falls through to the next warehouse. Locks are held only for this brief transaction, never across payment.
Charge the card (no locks held)
PaymentService.charge() runs outside any transaction. A card starting with 0000 is declined. On decline, releaseReservation() restores inventory and marks the order PAYMENT_FAILED.
Confirm PENDING → PAID
An atomic updateMany guarded by status: PENDING flips the order to PAID. If the row is no longer PENDING (the reaper expired it past the TTL while payment was in flight), the charge is refunded and a 409 is returned.
Executable test cases for POST /orders
Open DevTools > Network tab first to inspect outgoing requests.
/orders201 Created/orderstwo requests, same order id/orders5 parallel requests/orders422 Unprocessable Entity/orders422 Unprocessable Entity/orders404 Not Found/orders400 Bad Request