Skip to main content

Orders Module

The orders module manages the order lifecycle, cart operations, and checkout process.

Configuration Options​

export interface OrderSettingsOrderPositionValidation {
order: Order;
product: Product;
quantityDiff?: number;
configuration?: { key: string; value: string }[] | null;
}

export interface OrdersSettingsOptions {
ensureUserHasCart?: boolean;
orderNumberHashFn?: (order: Order, index: number) => string;
validateOrderPosition?: (
validationParams: OrderSettingsOrderPositionValidation,
unchainedAPI: UnchainedAPI,
) => Promise<void>;
lockOrderDuringCheckout?: boolean;
}
  • ensureUserHasCart: If enabled, Unchained will try to pre-generate a new cart when a user does not have one on various occasions, it's still not guaranteed that a user always has a cart. (default: false)
  • lockOrderDuringCheckout: If enabled, Unchained tries to use a so-called "Distributed Locking" approach with MongoDB while the checkout process is running, highly encouraged for most cases (default: false)

Order Number Creation​

The orderNumberHashFn is used to generate human-readable codes that can be easily spelled out to support staff. The default generates a random alphanumeric uppercase string with length 6 using an unambiguous charset (no O, 0, 1). If the number has already been taken, the function gets iteratively called with an increasing index.

Default Random Hash Generator

Order Position Validation​

When users mutate their cart, you sometimes have cases where you want custom checks. For example you want to restrict users to only buy 4 tickets in one go despite having a stock of 100 tickets.

With validateOrderPosition you can validate cart manipulations and throw Errors that bubble through to the client.

The default validator checks if a product is active.

await startPlatform({
options: {
orders: {
orderNumberHashFn: (order, index) => `${100000 + index}`,
validateOrderPosition: async ({ order, product, quantityDiff, configuration }, context) => {
const justOneAtATime = product.tags?.includes('one-at-a-time');
const positions = await context.modules.orders.positions.findOrderPositions({
orderId: order._id,
});
const userAlreadyHasProductInPositions = positions.some((p) => p.productId === product._id);
if (justOneAtATime && userAlreadyHasProductInPositions && quantityDiff > 0) {
throw new Error('ONE_AT_A_TIME');
}
},
},
},
});

Module API​

Access via modules.orders in the Unchained API context.

Queries​

MethodArgumentsDescription
cart{ userId, countryCode?, orderNumber? }Get user's cart
findOrder{ orderId? | orderNumber? }, options?Find a specific order
findOrders{ limit?, offset?, sort?, ...query }List orders with pagination
countqueryCount orders
orderExists{ orderId }Check if order exists
isCartorderCheck if order is a cart

Mutations​

MethodArgumentsDescription
create{ userId, countryCode, currencyCode, billingAddress?, contact?, context? }Create order
deleteorderIdDelete order
setCartOwner{ orderId, userId }Change order owner
updateBillingAddressorderId, billingAddressUpdate billing address
updateContactorderId, contactUpdate contact info
updateContextorderId, contextUpdate order context/meta
updateStatusorderId, { status, info? }Update order status
setPaymentProviderorderId, paymentProviderIdSet payment method
setDeliveryProviderorderId, deliveryProviderIdSet delivery method
acquireLockorderId, identifier, timeout?Acquire distributed lock

Sub-modules​

modules.orders.positions — Order line items:

MethodArgumentsDescription
findOrderPosition{ itemId }Get single position
findOrderPositions{ orderId }Get all positions in order
addProductItem{ orderId, productId, originalProductId, quantity, configuration?, quotationId?, context? }Add product to cart
updateProductItem{ orderPositionId, quantity, configuration }Update line item
deleteorderPositionIdRemove line item
removePositions{ orderId }Clear all positions

modules.orders.payments — Order payments:

MethodArgumentsDescription
findOrderPayment{ orderPaymentId }Get payment record
updateStatusorderPaymentId, { status, transactionId?, info? }Update payment status
markAsPaidorderPayment, meta?Mark an open payment as paid

modules.orders.deliveries — Order deliveries:

MethodArgumentsDescription
findDelivery{ orderDeliveryId }Get delivery record
updateStatusorderDeliveryId, { status, info? }Update delivery status
markAsDeliveredorderDeliveryMark an open delivery as delivered

modules.orders.discounts — Order discounts:

MethodArgumentsDescription
findOrderDiscount{ discountId }Get discount record
createdocCreate discount
deleteorderDiscountIdRemove discount

Statistics​

MethodArgumentsDescription
statistics.countByDateFielddateField, dateRange?, options?Count orders by date
statistics.aggregateByDateFielddateField, dateRange?, options?Aggregate stats by date
statistics.getTopCustomersorderIds, options?Get top spending customers

Events​

EventPayloadDescription
ORDER_CREATE{ order }Emitted when an order is created
ORDER_UPDATE{ order, field }Emitted when an order is updated
ORDER_REMOVE{ orderId }Emitted when an order is removed
ORDER_CHECKOUT{ order, oldStatus }Emitted when checkout is initiated
ORDER_CONFIRMED{ order, oldStatus }Emitted when an order is confirmed
ORDER_FULFILLED{ order, oldStatus }Emitted when an order is fulfilled
ORDER_REJECTED{ order, oldStatus }Emitted when an order is rejected
ORDER_ADD_PRODUCT{ orderPosition }Emitted when a product is added to cart
ORDER_UPDATE_CART_ITEM{ orderPosition }Emitted when a cart item is updated
ORDER_REMOVE_CART_ITEM{ orderPosition }Emitted when a cart item is removed
ORDER_EMPTY_CART{ orderId, count }Emitted when cart is emptied
ORDER_SET_DELIVERY_PROVIDER{ order }Emitted when delivery provider is set
ORDER_SET_PAYMENT_PROVIDER{ order }Emitted when payment provider is set
ORDER_UPDATE_DELIVERY{ orderDelivery }Emitted when delivery is updated
ORDER_DELIVER{ orderDelivery }Emitted when order is delivered
ORDER_PAY{ orderPayment }Emitted when an order payment is marked as paid
ORDER_UPDATE_PAYMENT{ orderPayment }Emitted when an order payment is updated
ORDER_CREATE_DISCOUNT{ discount }Emitted when a discount is created
ORDER_UPDATE_DISCOUNT{ discount }Emitted when a discount is updated
ORDER_REMOVE_DISCOUNT{ discount }Emitted when a discount is removed

More Information​

For API usage and detailed documentation, see the core-orders package on GitHub.