Create order
Entry. POST /v1/commerce/orders → OrderController → OrderService / OrderCommandService.
Marketplace creates use a different entry. See Marketplace order ingestion.
Sequence
Synchronous behavior
Inside the command transaction, when a status change is involved, state is checked with isValidOrderTransition.
OutboxWriterService.enqueue writes:
order.createdorder.confirmedon confirm pathsorder.location.changedon location changesstock.sync.requestedon some pathsorder.address.resolution_triggeredon some paths
OrderCommandService.create always inserts orderStatus: PENDING. The order.confirmed enqueue in that method runs only when the saved status is CONFIRMED, so this create method does not emit it.
If channelOrderNo or orderNo is present, create rejects a second live order with the same channelOrderNo, channelId, and tenantId (order.error.orderNoAlreadyExistsInChannel, deletedAt: null). A retry with a new channel order number is not covered by that check. A missing channel order number is stored as the allocated orderNo.
The client does not wait for fulfillment or the wallet.
Transaction boundary
The order row and the outbox rows commit or roll back together, because enqueue receives the transaction client. An in-process eventEmitter.emit would not.
Asynchronous behavior
| Event | Consumer | Effect |
|---|---|---|
order.created | fulfillment listener | Fulfillment work |
order.confirmed | wallet listener | deductToken |
order.address.resolution_triggered | WhatsApp address listener | Address resolution |
Failure
| Where | Result |
|---|---|
| Validation pipe | 400, order not written |
| Throw before commit | Order and outbox rolled back |
| Listener throw | Order remains. Outbox retries up to 10, then DEAD |
Idempotency
A second live order with the same channelOrderNo, channelId, and tenantId is rejected. There is no Idempotency-Key header on this route. A retry that omits the channel order number, or sends a different one, can insert another order. IdempotencyRecord is not used by this method.
Side effects that do not happen here
Inventory quantity on a channel is not updated inside this HTTP call. Stock sync is an outbox event that becomes an inventory publish job. See Publish path.