---
title: Order processing
slug: commerce-connect-14/docs/order-processing
docTags: 
createdAt: 2026-07-23T10:25:35.527Z
---

Processing of purchase orders and related items is based on classes and methods in the `EPiServer.Commerce.Order` and `EPiServer.Commerce.Marketing` namespaces.

These are the key interfaces:

- `EPiServer.Commerce.Order.IOrderGroupExtensions`
- `EPiServer.Commerce.Order.IInventoryProcessor`. See [Update inventory](docId\:Q4PxyVX4k7nEDnAMjXBT3) and [Adjust inventory](docId\:Q4PxyVX4k7nEDnAMjXBT3).
- `EPiServer.Commerce.Order.ILineItemValidator`. See [Validate line item](docId\:Q4PxyVX4k7nEDnAMjXBT3).
- `EPiServer.Commerce.Order.IPaymentProcessor`. See [Process payments](docId\:Q4PxyVX4k7nEDnAMjXBT3).
- `EPiServer.Commerce.Order.IPaymentPlanProcessor`. See [Process payment plans](docId\:Q4PxyVX4k7nEDnAMjXBT3).
- `EPiServer.Commerce.Order.IPaymentPlanExtensions`. See [Process payment plan extensions](docId\:Q4PxyVX4k7nEDnAMjXBT3).
- `EPiServer.Commerce.Order.IPlacedPriceProcessor`. See [Update placed price](docId\:Q4PxyVX4k7nEDnAMjXBT3).
- `EPiServer.Commerce.Order.IFulfillmentWarehouseProcessor`.
- `EPiServer.Commerce.Order.IPurchaseOrderProcessor `. See [Process purchase orders](docId\:Q4PxyVX4k7nEDnAMjXBT3).
- `EPiServer.Commerce.Order.IPurchaseOrderExtensions`. See [Process purchase order extensions](docId\:Q4PxyVX4k7nEDnAMjXBT3).
- `EPiServer.Commerce.Order.IShipmentProcessor`. See [Process shipments](docId\:Q4PxyVX4k7nEDnAMjXBT3).
- `EPiServer.Commerce.Order.IShipmentProcessorExtensions`. See [Process shipment extensions](docId\:Q4PxyVX4k7nEDnAMjXBT3).
- `EPiServer.Commerce.Order.IShipmentExtensions`. See [Process shipment extensions](docId\:Q4PxyVX4k7nEDnAMjXBT3).
- `EPiServer.Commerce.Marketing.IPromotionEngine`. See [Apply discount](docId\:Q4PxyVX4k7nEDnAMjXBT3).

All example code below passes dependencies to the extension methods, so that you can easily unit test your applications.

## Validation issues

When performing different actions on an IOrderGroup, issues can cause modifications to line items. These warnings and errors are represented in the class `EPiServer.Commerce.Order.ValidationIssue`. Most processing tasks take a parameter with an `Action\<ILineItem, ValidationIssue>`, which allows the caller to collect this information so a message about the issue can be displayed to the user.

## Validate line items

Validating the status of line item is useful to make sure the product is active, within the valid date range, and that the catalog entry is available in the current market.

**Example:** Validate line items in a cart and checking for validation issues.

```csharp
var orderRepository = ServiceLocator.Current.GetInstance<IOrderRepository>();
    var lineItemValidator = ServiceLocator.Current.GetInstance<ILineItemValidator>();
    var contactId = PrincipalInfo.CurrentPrincipal.GetContactId();
    var cart = orderRepository.LoadCart(contactId, "Default");
    var validationIssues = new Dictionary<ILineItem, ValidationIssue>();
    
    //Check all line items on cart
    cart.ValidateOrRemoveLineItems((item, issue) => validationIssues.Add(item, issue), lineItemValidator);
    
    //Check one lineitem
    var lineItem = cart.GetAllLineItems().First();
    if (!lineItemValidator.Validate(lineItem, cart.Market, (item, issue) => validationIssues.Add(item, issue)))
      {
        //Check validationIssues for problems
      }
```

## Update placed price

Updating the placed price is useful when a shopper has an item in the cart for an extended period of time. It is recommended to add validation to check if the product price has changed, and if needed update the price.

**Example:** Check and update placed price for line items.

```csharp
var orderRepository = ServiceLocator.Current.GetInstance<IOrderRepository>();
    var placedPriceProcessor = ServiceLocator.Current.GetInstance<IPlacedPriceProcessor>();
    var contactId = PrincipalInfo.CurrentPrincipal.GetContactId();
    var cart = orderRepository.LoadCart(contactId, "Default");
    var validationIssues = new Dictionary<ILineItem, ValidationIssue>();
    
    //Update all placed prices on the cart
    cart.UpdatePlacedPriceOrRemoveLineItems(PrincipalInfo.CurrentPrincipal.GetCustomerContact(),
      (item, issue) => validationIssues.Add(item, issue), placedPriceProcessor);
    
    //Update line item placed price
    var lineItem = cart.GetAllLineItems().First();
    lineItem.UpdatePlacedPrice(PrincipalInfo.CurrentPrincipal.GetCustomerContact(), cart.Market,
      cart.Currency,(item, issue) => validationIssues.Add(item, issue), placedPriceProcessor);
    
    //Update line item placed price
    var lineItem = cart.GetAllLineItems().First();
    placedPriceProcessor.UpdatePlacedPrice(lineItem, PrincipalInfo.CurrentPrincipal.GetCustomerContact(),
      cart.Market, cart.Currency,(item, issue) => validationIssues.Add(item, issue));
```

## Apply discounts

Applying discounts will run the promotion engine to evaluate and apply discounts to the `IOrderGroup`. If certain discounts require coupons, it is important to add coupons to the `IOrderForm` before running `ApplyDiscounts`.

**Example:** Check for discounts on items in a cart.

```csharp
var orderRepository = ServiceLocator.Current.GetInstance<IOrderRepository>();
    var promotionEngine = ServiceLocator.Current.GetInstance<IPromotionEngine>();
    var contactId = PrincipalInfo.CurrentPrincipal.GetContactId();
    var cart = orderRepository.LoadCart(contactId, "Default");
    
    //run apply discounts on the cart
    var rewardDescriptions = cart.ApplyDiscounts(promotionEngine, new PromotionEngineSettings());
    
    //run apply discounts on the cart
    var rewardDescriptions = promotionEngine.Run(cart, new PromotionEngineSettings());
```

## Process payments

Process payments allow the configured payments to be processed through their respective payment providers.

**Example:** Run the payment processor for a cart.

```csharp
var orderRepository = ServiceLocator.Current.GetInstance<IOrderRepository>();
    var paymentProcessor = ServiceLocator.Current.GetInstance<IPaymentProcessor>();
    var orderGroupCalculator = ServiceLocator.Current.GetInstance<IOrderGroupCalculator>();
    var contactId = PrincipalInfo.CurrentPrincipal.GetContactId();
    var cart = orderRepository.LoadCart(contactId, "Default");
    
    
    //Process payments for the cart
    cart.ProcessPayments(paymentProcessor, orderGroupCalculator);
```

## Update inventory

`UpdateInventory` checks that a sufficient quantity of inventory exists for the selected shipment warehouse. This method also updates the line item with the available quantity or, if none are available, removes it.

**Example:** Update inventory information for a cart.

```csharp
var orderRepository = ServiceLocator.Current.GetInstance<IOrderRepository>();
    var inventoryProcessor = ServiceLocator.Current.GetInstance<IInventoryProcessor>();
    var contactId = PrincipalInfo.CurrentPrincipal.GetContactId();
    var cart = orderRepository.LoadCart(contactId, "Default");
    var validationIssues = new Dictionary<ILineItem, ValidationIssue>();
    
    //Update Inventory on cart
    cart.UpdateInventoryOrRemoveLineItems((item, issue) => validationIssues.Add(item, issue), inventoryProcessor);
    
    //Update inventory on shipment line items
    var shipment = cart.GetFirstShipment();
    inventoryProcessor.UpdateInventoryOrRemoveLineItem(shipment, (item, issue) => validationIssues.Add(item, issue));
```

## Adjust inventory

`AdjustInventory` adjusts the inventory for each line item and removes it if no inventory exists. This operation is done in sequence for each shipment in an `IOrderGroup`, or for a specific shipment if the `IInventoryProcessor` method is called directly.

The behavior changes based on the value of the `IOrderGroup OrderStatus` property. If calling the `IInventoryProcessor` directly, you should pass the status of the shipment's parent `IOrderGroup`:

- `Completed` – Makes permanent the reservation of inventory, decrementing available quantity.
- `Canceled` – Cancels the reservation of inventory, incrementing available quantity.
- `InProgress || AwaitingExchange` – Reserves inventory to fulfill the shipment.

In addition, the value of `IShipment.OrderShipmentStatus` may override the behavior for a specific shipment.

- `Shipped` – Same behavior as `OrderStatus.Completed` but is applied regardless of the value of `IOrderGroup.OrderStatus`.
- `Cancelled` – Same behavior as `OrderStatus.Cancelled` but is applied regardless of the value of `IOrderGroup.OrderStatus`.

**Example:** Adjust inventory information for a line items in a cart.

```csharp
var orderRepository = ServiceLocator.Current.GetInstance<IOrderRepository>();
    var inventoryProcessor = ServiceLocator.Current.GetInstance<IInventoryProcessor>();
    var contactId = PrincipalInfo.CurrentPrincipal.GetContactId();
    var cart = orderRepository.LoadCart(contactId, "Default");
    var validationIssues = new Dictionary<ILineItem, ValidationIssue>();
    
    //Adjust Inventory on cart
    cart.AdjustInventoryOrRemoveLineItems((item, issue) => validationIssues.Add(item, issue), inventoryProcessor);
    
    //Adjust inventory on shipment line items
    var shipment = cart.GetFirstShipment();
    inventoryProcessor.AdjustInventoryOrRemoveLineItems(shipment, cart.OrderStatus, (item, issue) => validationIssues.Add(item, issue));
```

## Process purchase orders

`IPurchaseOrderProcessor` contains functionality for putting and releasing orders on hold, canceling orders, and starting order processing.

**Example:** Use `HoldOrder()` to put a purchase order on hold.

```csharp
IOrderRepository orderRepository;
    IPurchaseOrderProcessor purchaseOrderProcessor;
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    purchaseOrderProcessor.HoldOrder(purchaseOrder);



**Example:** Using ReleaseOrder() to release a purchase order from hold.



    IOrderRepository orderRepository;
    IPurchaseOrderProcessor purchaseOrderProcessor;
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    purchaseOrderProcessor.ReleaseOrder(purchaseOrder);
```

**Example:** Use `CancelOrder()` to cancel a purchase order.

```csharp
IOrderRepository orderRepository;
    IPurchaseOrderProcessor purchaseOrderProcessor;
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    purchaseOrderProcessor.CancelOrder(purchaseOrder);
```

**Example:** Use `ProcessOrder()` to start processing a purchase order.

```csharp
IOrderRepository orderRepository;
    IPurchaseOrderProcessor purchaseOrderProcessor;
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    purchaseOrderProcessor.ProcessOrder(purchaseOrder);
```

## Process purchase order extensions

`IPurchaseOrderExtensions` extends the functionality provided with `IPurchaseOrderProcessor`.

**Example:** Use `IsPaid()` to determine if a purchase order is paid.

```csharp
IOrderRepository orderRepository;
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    var isPaid = purchaseOrder.IsPaid();
```

**Example:** Use `CanBeCancelled()` to determine if purchase order can be cancelled.

```csharp
IOrderRepository orderRepository;
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    var canBeCancelled = purchaseOrder.CanBeCancelled();
```

**Example:** Use `CanBePutOnHold()` to determine if the purchase order can be put on hold.

```csharp
IOrderRepository orderRepository;
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    var canBeHold = purchaseOrder.CanBePutOnHold();
```

**Example:** Use `HasAwaitingStockReturns()` to determine if the purchase order has any awaiting stock returns.

```csharp
IOrderRepository orderRepository;
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    var awaitingStockReturns = purchaseOrder.HasAwaitingStockReturns();
```

**Example:** Use `HasAwaitingReturnCompletable()` to determine if the purchase order has any awaiting return completable.

```csharp
IOrderRepository orderRepository;
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    var awaitingReturnCompletable = purchaseOrder.HasAwaitingReturnCompletable();
```

**Example:** Use `GetActiveReturnForms()` to get a purchase order's active return forms (moved from `DefaultReturnPurchaseOrderCalculator`).

```csharp
IOrderRepository orderRepository;
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    var activeReturnForms = purchaseOrder.GetActiveReturnForms();
```

**Example:** Use `CanReleaseShipment()` to determine if a shipment can be released.

```csharp
IOrderRepository orderRepository;
    IShipment shipment;
    
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    var canReleaseShipment = purchaseOrder.CanReleaseShipment(shipment);
```

**Example:** Use `CanCancelShipment()` to determine if a shipment can be cancelled.

```csharp
IOrderRepository orderRepository;
    IShipment shipment;
    
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    var canCancelShipment = purchaseOrder.CanCancelShipment(shipment);
```

**Example:** Use `CanReturnShipment()` to determine if a shipment can be returned.

```csharp
IOrderRepository orderRepository;
    IShipment shipment;
    
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    var canReturnShipment = purchaseOrder.CanReturnShipment(shipment);
```

**Example:** Use `CanCompleteShipment()` to determine if a shipment can be completed.

```csharp
IOrderRepository orderRepository;
    IShipment shipment;
    
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    var canCompleteShipment = purchaseOrder.CanCompleteShipment(shipment);
```

## Process shipments

`IShipmentProcessor` contains functionality for canceling, completing, and realizing shipments, and adding them to and from warehouse picklists.

**Example:** Use `CancelShipment()` to cancel a shipment.

```csharp
IOrderRepository orderRepository;
    IShipmentProcessor shipmentProcessor;
    IShipment shipment;
    
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    shipmentProcessor.CancelShipment(purchaseOrder, shipment);
```

**Example:** Use `CompleteShipment()` to complete a shipment.

```csharp
IOrderRepository orderRepository;
    IShipmentProcessor shipmentProcessor;
    IList<Ishipment> shipments;
    
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    shipmentProcessor.CompleteShipment(purchaseOrder, shipments);
```

**Example:** Use `ReleaseShipment()` to release a shipment.

```csharp
IOrderRepository orderRepository;
    IShipmentProcessor shipmentProcessor;
    IList<Ishipment> shipments;
    
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    shipmentProcessor.ReleaseShipment(purchaseOrder, shipments);
```

**Example:** Use `AddShipmentToPicklist()` to add a shipment to a picklist.

```csharp
IOrderRepository orderRepository;
    IShipmentProcessor shipmentProcessor;
    IShipment shipment;
    
    (…)
    var orderGroupId = 123;
    var pickListId = 1;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    shipmentProcessor.AddShipmentToPicklist(purchaseOrder, shipment, pickListId);
```

**Example:** Use `RemoveShipmentFromPicklist()` to remove a shipment from a picklist.

```csharp
IOrderRepository orderRepository;
    IShipmentProcessor shipmentProcessor;
    IShipment shipment;
    
    (…)
    var orderGroupId = 123;
    var purchaseOrder = orderRepository.Load<PurchaseOrder>(orderGroupId);
    shipmentProcessor.RemoveShipmentFromPicklist(purchaseOrder, shipment);
```

## Process shipment extensions

`IShipmentProcessorExtensions` extends the functionality provided with `IShipmentProcessor`.

**Example:** Use `CompleteShipment()` to complete a shipment.

```csharp
IShipmentProcessor shipmentProcessor; 
    IPurchaseOrder purchaseOrder;
    IShipment shipment;
    
    (…)
    shipmentProcessor.CompleteShipment(purchaseOrder, shipment);
```

**Example:** Use `ReleaseShipment()` to release a shipment.

```csharp
IShipmentProcessor shipmentProcessor; 
    IPurchaseOrder purchaseOrder;
    IShipment shipment;
    
    (…)
    shipmentProcessor.ReleaseShipment(purchaseOrder, shipment);
```

**Example:** Use `CanBePacked()` in `IShipmentExtensions` to determine if a shipment can be packed.

```csharp
IShipment shipment;
    
    (…)
    var canBePacked = shipment.CanBePacked();
```

## Process payment plans

`IPaymentPlanProcessor` contains functionality for canceling payment plans.

**Example:** Use `CancelPaymentPlan()` to cancel a payment plan.

```csharp
IOrderRepository orderRepository;
    IPaymentPlanProcessor paymentPlanProcessor;
    (…)
    var orderGroupId = 123;
    var paymentPlan = orderRepository.Load<PaymentPlan>(orderGroupId);
    paymentPlanProcessor.CancelPaymentPlan(paymentPlan);
```

## Process  payment plan extensions

`IPaymentPlanExtensions` contains functionality for checking payment and cancellation of payment plans.

**Example:** Use `IsPaid()` to determine if a payment plan is paid.

```csharp
IOrderRepository orderRepository;
    (…)
    var orderGroupId = 123;
    var paymentPlan = orderRepository.Load<PaymentPlan>(orderGroupId);
    var isPaid = paymentPlan.IsPaid();
```

**Example:** Use `CanBeCancelled()` to determine if a payment plan can be cancelled.

```csharp
IOrderRepository orderRepository;
    (…)
    var orderGroupId = 123;
    var paymentPlan = orderRepository.Load<PaymentPlan>(orderGroupId);
    var canBeCancelled = paymentPlan.CanBeCancelled();
```

## Use APIs instead of CartHelper for order checkout

The `IOrderRepository` API replaces the legacy functionality provided with the `CartHelper` activity workflow. The following provides some guidelines on how to use the API instead.

The legacy cart checkout workflow contained activities that can be called separately by the APIs.

- `ProcessPaymentActivity` – Previously used for processing payments added to a cart. Use `IOrderGroupExtensions.ProcessPayments` instead.
- `CalculateTotalsActivity` – Previously used for calculating order totals. Use `IOrderGroupExtensions.GetTotals()` instead.
- `AdjustInventoryActivity` – Previously used for adjusting line item inventories. Use `IOrderGroupExtensions.AdjustInventoryOrRemoveLineItems()` instead.
- `RecordPromotionUsageActivity` – Previously used to calculate and save promotion usage. Use `IOrderGroupExtensions.ApplyDiscounts()` instead.
- Use `IOrderRepository.SaveAsPurchaseOrder()` to save a cart as a purchase order.
