Skip to Content
MarketplaceOrder Lifecycle

Order Lifecycle

Orders are the heart of the marketplace. Each order represents items from a single seller that need to be shipped to a buyer. When someone buys from multiple sellers in one checkout, we create multiple orders — one per seller — all linked to the same transaction.

Understanding the order lifecycle is essential for debugging issues, building new features, or just knowing what’s happening behind the scenes when a user says “my order is stuck.”

The State Machine

Orders move through a series of statuses, from creation to completion (or cancellation). Here’s the full picture — it might look complex at first, but we’ll break it down piece by piece.

Understanding Status Categories

Not all statuses are created equal. Some are “in progress” waiting for someone to take action, while others are final states where the order is done and no more changes can happen.

These statuses mean the order is still “in flight” — someone needs to do something.

StatusWhat’s happeningWho needs to act
pending_shipmentOrder just created, waiting for seller to shipSeller
cancellation_requestedBuyer wants to cancel, seller has 48h to ship or acceptSeller
shippedPackage is on its wayBuyer (wait for delivery)
deliveredPackage arrived, 7-day hold periodSystem (auto-completes)
dispute_openBuyer opened a dispute, negotiation phaseBoth parties
dispute_escalatedDispute couldn’t be resolved, needs adminAdmin

Common Flows

Most orders follow one of a few common paths. Let’s walk through each one.

The Happy Path: Successful Delivery

This is what we hope happens with every order:

Order Created

Checkout succeeds, order starts in pending_shipment

Seller Ships

Seller adds tracking number, status becomes shipped

Delivery Confirmed

Tracking shows delivered, status becomes delivered

7-Day Hold

Buyer has 7 days to inspect items and open a dispute if needed

Order Completes

After 7 days with no dispute, status becomes completed and funds release to seller

Cancellation Flow

Sometimes buyers change their mind or sellers can’t fulfill an order. Here’s how cancellations work:

The 48-Hour Grace Period: When a buyer requests cancellation, the seller gets 48 hours to either accept the cancellation or ship the order anyway. If they ship, the cancellation is voided and the order continues normally. This protects sellers who were already packing when the request came in.

Key Cancellation Rules

RuleDetails
Buyer request windowBuyers can request cancellation within 7 days of order creation
Seller 48h graceSeller can “void” cancellation by shipping within 48 hours
Seller cancelAvailable anytime before shipment — immediate refund
Auto-cancelOrders pending shipment for 5+ days are automatically cancelled

Delivery and the 7-Day Hold

After an order is delivered, we don’t immediately release funds to the seller. There’s a 7-day hold period that gives buyers time to:

  • Inspect the items they received
  • Make sure everything matches the listing
  • Open a dispute if something is wrong

Once the order reaches a terminal status (like completed), a separate 7-day review window opens for both parties to submit ratings. See the Ratings & Reviews page for details.

The Order Data Model

When you’re debugging or building features, you’ll work with the Order document. Here are the key fields:

FieldTypePurpose
orderNumberStringHuman-readable ID like OR-9F4K2D8M-1
transactionIdObjectIdLinks to the parent Transaction
buyerId / sellerIdObjectIdReferences to User documents for both parties
statusEnumCurrent state from the state machine
itemsArraySnapshot of purchased items (frozen at checkout)
subtotalNumberItem total in cents
shippingAmountNumberShipping cost in cents
sellerFeeNumberPlatform fee in cents
fundsObjectTracks held/refunded/transferred amounts
shippingObjectTracking number, carrier info
statusHistoryArrayAudit trail of all transitions
disputeObjectDispute details (if any)

Status History: The Audit Trail

Every time an order’s status changes, we record it. This creates a complete history that’s invaluable for customer support and debugging.

interface OrderStatusChange { from: OrderStatus // Previous status to: OrderStatus // New status at: Date // When it changed actor: 'buyer' | 'seller' | 'system' | 'admin' reason?: string // Why (for cancellations, etc.) metadata?: Record<string, unknown> }

The actor field tells you who triggered the change. system means it was an automated job (like auto-cancel or auto-complete), while admin means someone from the platform team manually intervened.

Domain Service: Business Rules

The OrderDomainService contains pure functions that encode all the business rules. These are the source of truth for “can this action happen?”

FunctionWhat it checks
isTransitionAllowed(from, to)Is this status transition valid?
canShip(order)Can seller mark as shipped? (status = pending_shipment)
canBuyerRequestCancellation(order)Within 7-day window? Not already shipped?
canSellerCancel(order)Is order still pending?
canOpenDispute(order)Is order delivered/shipped and within 30 days?
canRefund(order)Is this status refundable?
shouldAutoCancel(order)Has order been pending for 5+ days?
shouldAutoRelease(order)Is order delivered + 7 days?

Always use these domain functions to check permissions. Don’t write your own status checks — the rules can be subtle and change over time.

Background Automation

Several order lifecycle events happen automatically via a cron-triggered background job (process-order-lifecycle) that runs every 30 minutes. The job queries for orders matching specific criteria and processes them through the state machine.

JobTriggerWhat happens
Auto-cancel unshippedOrder in pending_shipment for 5+ calendar daysTransitions to cancelled_auto, full refund initiated, inventory restored. Both buyer and seller notified.
Buyer cancellation expiryOrder in cancellation_requested where cancellation.refundAt has passed (48h window)Transitions to cancelled_buyer, full refund initiated. Seller didn’t ship or accept within the grace period.
Auto-complete deliveredOrder in delivered where funds.payoutEligibleAt has passed (7-day hold)Transitions to completed. Fund release is then handled by the separate stuck fund operations job after the settlement delay.

The handler follows a “query batch, process each, continue on error” pattern — if one order fails, the rest still get processed. Individual errors are logged and the job reports a summary at the end.

Fund operations (refunds and transfers) are not handled by the order lifecycle job. They’re handled by a separate process-stuck-fund-operations job. See Fund Management for details.

Code Locations

ComponentLocation
Order Modelpackages/core/db/src/mongo/marketplace/order.model.ts
Order Domain Servicepackages/backend/domains/order/src/order-domain.service.ts
Order State Machinepackages/backend/domains/order/src/order.transitions.ts
Order Orchestratorpackages/backend/features/orders/src/order-orchestrator.service.ts
Order Lifecycle Handlerpackages/backend/features/orders/src/order-lifecycle.handler.ts
Order Query Servicepackages/backend/features/orders/src/order-query.service.ts
Status Constantspackages/backend/domains/order/src/order-status.ts
Last updated on