---
title: Customize order creation
related:
  - https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/checkout-process/order-creation.md
  - https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/price-calculation.md
  - https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/price-calculation/customization.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

> **License:** Advanced license required.
>
> Features described on this page require the Xperience by Kentico **Advanced** license tier.

The [order creation service](https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/checkout-process/order-creation.md) provides several mapper interfaces that allow you to customize the order creation process without modifying core functionality. Use these interfaces to add [custom fields](https://docs.kentico.com/documentation/developers-and-admins/customization/object-types/extend-system-object-types.md) to orders, customers, addresses, and order items, or to modify data before it's persisted to the database.

## Customization scenarios and overview

Consider implementing custom mappers when you need to:

- Store additional business-specific data on orders, customers, or addresses.
- Calculate or derive values during order creation (loyalty points, customer segments, etc.).
- Integrate with external systems and store integration metadata.
- Apply custom business rules before persisting order data.
- Maintain audit trails or custom tracking information.

## Mapper usage in order creation lifecycle

The following diagram illustrates where each available mapper is called in the order creation process:

![Order creation process with mapper integration points](https://docs.kentico.com/docsassets/documentation/customize-order-creation/order-creation-mappers.drawio.svg "Order creation process with mapper integration points")

> **Info:** Usage notes
>
> - All mappers are optional. If no mapper is registered, the default behavior is used.
> - `ICustomerInfoMapper<TOrderData>` is only invoked when creating **new** customer records. If an existing customer is found (by member ID or email), the mapper is not called. To update existing customer data, implement separate logic after order creation.

## Customer information mapping

Implement `ICustomerInfoMapper<TOrderData>` to add [custom fields](https://docs.kentico.com/documentation/developers-and-admins/customization/object-types/extend-system-object-types.md) to customer records or modify customer data during order creation.

```csharp title="Example - Custom customer mapper"
using CMS.Commerce;

/// <summary>
/// Maps custom fields to customer records during order creation.
/// Only called when creating new customers, not for existing ones.
/// </summary>
public class CustomCustomerInfoMapper : ICustomerInfoMapper<CustomOrderData>
{
    public CustomerInfo PopulateInfo(CustomerInfo customerInfo, CustomOrderData orderData)
    {
        // Custom extension methods to populate additional customer fields
        customerInfo.SetLoyaltyPoints(orderData.LoyaltyPoints);

        return customerInfo;
    }
}

public static class CustomerInfoExtensions
{
    public static decimal? GetLoyaltyPoints(this CustomerInfo customer)
    {
        return customer.GetValue<decimal?>("CustomerLoyaltyPoints", null);
    }

    public static void SetLoyaltyPoints(this CustomerInfo customer, decimal points)
    {
        customer.SetValue("CustomerLoyaltyPoints", points);
    }
}
```

> **Note:** **Important:** Custom mappers are called only when creating new customer records. If an existing customer is found (by member ID or email), the mapper is not invoked. To update existing customer data, you need to implement separate customer update logic outside the order creation process. After order creation, you have access to the ID of the created order, which you can use to retrieve the corresponding customer.
>
> ```csharp title="Access customer objects via orderId"
> using CMS.Commerce;
> using CMS.DataEngine;
>
> // Class that uses constructor dependency injection to obtain providers
> public class CustomerComponent(IInfoProvider<OrderInfo> orderInfoProvider, IInfoProvider<CustomerInfo> customerInfoProvider)
> {
>     // Gets the order
>     var order = await orderInfoProvider.GetAsync(orderId, cancellationToken);
>
>     // Gets the associated customer
>     var customer = await customerInfoProvider.GetAsync(order.OrderCustomerID, cancellationToken);
> }
> ```

Register the mapper in the service container.

```csharp title="Program.cs"
using Microsoft.AspNetCore.Builder;

using CMS.Commerce;

// ...

// Registers the mapper in the application service container
builder.Services.AddTransient<ICustomerInfoMapper<CustomOrderData>, CustomCustomerInfoMapper>();
```

## Order information mapping

Implement `IOrderInfoMapper<TOrderData, TPriceCalculationResult>` to add [custom fields](https://docs.kentico.com/documentation/developers-and-admins/customization/object-types/extend-system-object-types.md) to order records. This mapper has access to both the order data and the price calculation result, allowing you to store calculated values or metadata with the order.

```csharp title="Example - Custom order mapper"
using CMS.Commerce;

/// <summary>
/// Maps custom fields to order records.
/// Has access to both order data and price calculation results.
/// </summary>
public class CustomOrderInfoMapper : IOrderInfoMapper<CustomOrderData, PriceCalculationResult>
{
    public OrderInfo PopulateInfo(
        OrderInfo orderInfo,
        CustomOrderData orderData,
        PriceCalculationResult calculationResult)
    {
        // Add custom logic to populate additional order fields

        // Custom extension methods to populate additional customer fields
        orderInfo.SetAdditionalNotes(orderData.AdditionaNotes);

        return orderInfo;
    }
}

public static class OrderInfoExtensions
{
    public static void SetAdditionalNotes(this OrderInfo order, string additionaNotes)
    {
        order.SetValue("OrderAdditionalNotes", additionaNotes);
    }

    public static string? GetAdditionalNotes(this OrderInfo order)
    {
        return order.GetValue<string?>("OrderAdditionalNotes", null);
    }
}
```

Register the mapper in the service container.

```csharp title="Program.cs"
using Microsoft.AspNetCore.Builder;

using CMS.Commerce;

// ...

// Registers the mapper in the application service container
builder.Services.AddTransient<IOrderInfoMapper<CustomOrderData, PriceCalculationResult>, CustomOrderInfoMapper>();
```

## Address mapping

Implement `ICustomerAddressInfoMapper<TAddressDto>` or `IOrderAddressInfoMapper<TAddressDto>` to add [custom fields](https://docs.kentico.com/documentation/developers-and-admins/customization/object-types/extend-system-object-types.md) to address records. Both customer addresses and order addresses can be extended independently.

```csharp title="Example - Custom address mapper"
using CMS.Commerce;

/// <summary>
/// Maps custom fields to order address records.
/// </summary>
public class CustomAddressInfoMapper : IOrderAddressInfoMapper<CustomAddressDto>
{
    public OrderAddressInfo PopulateInfo(
        OrderAddressInfo orderAddressInfo,
        CustomAddressDto addressDto)
    {
        // Custom extension methods to populate additional customer fields
        orderAddressInfo.SetAddressDeliveryNote(addressDto.DeliveryNotes);

        return orderAddressInfo;
    }

    public CustomAddressDto PopulateDto(CustomAddressDto addressDto, OrderAddressInfo orderAddressInfo)
    {
        // Place custom mapping from the database entity to the DTO object here
        return addressDto;
    }
}

public static class OrderAddressInfoExtensions
{
    public static void SetAddressDeliveryNote(this OrderAddressInfo address, string notes)
    {
        address.SetValue("OrderAddressDeliveryNotes", notes);
    }

    public static string? GetAddressDeliveryNote(this OrderAddressInfo address)
    {
        return address.GetValue<string?>("OrderAddressDeliveryNotes", null);
    }
}
```

> **Note:** **Address comparison**: When checking for duplicate customer addresses, the service uses all fields including custom fields in the comparison. Ensure your custom fields are appropriate for duplicate detection, or implement custom comparison logic if needed.

Register the mapper in the service container.

```csharp title="Program.cs"
using Microsoft.AspNetCore.Builder;

using CMS.Commerce;

// ...

// Registers the mapper in the application service container
builder.Services.AddTransient<IOrderAddressInfoMapper<CustomAddressDto>, CustomAddressInfoMapper>();
```

## Order item mapping

Implement `IOrderItemInfoMapper<TOrderData, TPriceCalculationResult>` to add [custom fields](https://docs.kentico.com/documentation/developers-and-admins/customization/object-types/extend-system-object-types.md) to order items. This is useful for storing product-specific metadata or configuration data with each line item.

```csharp title="Example - Custom order item mapper"
using CMS.Commerce;

/// <summary>
/// Maps custom fields to order item records.
/// Useful for product configuration, warranty selections, etc.
/// </summary>
public class CustomOrderItemInfoMapper : IOrderItemInfoMapper<CustomOrderData, PriceCalculationResult>
{
    public OrderItemInfo PopulateInfo(
        OrderItemInfo orderItemInfo,
        CustomOrderData orderData,
        PriceCalculationResult calculationResult,
        ProductIdentifier productIdentifier)
    {
        // Add custom logic to populate additional order item fields

        orderItemInfo.SetOrderItemWarranty(24);

        return orderItemInfo;
    }
}

public static class OrderItemInfoExtensions
{
    public static void SetOrderItemWarranty(this OrderItemInfo address, int notes)
    {
        address.SetValue("ItemWarrantyMonths", notes);
    }

    public static int? GetOrderItemWarranty(this OrderItemInfo address)
    {
        return address.GetValue<int?>("ItemWarrantyMonths", null);
    }
}
```

Register the mapper in the service container.

```csharp title="Program.cs"
using Microsoft.AspNetCore.Builder;

using CMS.Commerce;

// ...

// Registers the mapper in the application service container
builder.Services.AddTransient<IOrderItemInfoMapper<CustomOrderData, PriceCalculationResult>, CustomOrderItemInfoMapper>(); 
```

## Price calculation customization

Implement `IPriceCalculationRequestMapper<TPriceCalculationRequest, TOrderData>` to add custom data to [price calculation requests](https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/price-calculation/customization.md#extend-data-transfer-objects) before prices are calculated. This allows you to influence the pricing calculation with order-specific context.

```csharp title="Example - Custom calculation request mapper"
using CMS.Commerce;

/// <summary>
/// Maps custom data to price calculation requests.
/// Allows passing order-specific context to pricing calculations.
/// </summary>
public class CustomCalculationRequestMapper : IPriceCalculationRequestMapper<PriceCalculationRequest, CustomOrderData>
{
    public PriceCalculationRequest PopulateCalculationRequest(
        PriceCalculationRequest calculationRequest,
        CustomOrderData orderData)
    {
        // Add custom logic to modify the calculation request

        // Example: Add customer tier for tiered pricing
        // Example: Add promotional context or B2B contract information

        return calculationRequest;
    }
}
```

For more information on customizing the price calculation pipeline itself, see [Customize price calculation](https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/price-calculation/customization.md).

Register the mapper in the service container.

```csharp title="Program.cs"
using Microsoft.AspNetCore.Builder;

using CMS.Commerce;

// ...

// Registers the mapper in the application service container
builder.Services.AddTransient<IPriceCalculationRequestMapper<PriceCalculationRequest, CustomOrderData>, CustomCalculationRequestMapper>();
```

## Extend order data objects

To use [custom fields](https://docs.kentico.com/documentation/developers-and-admins/customization/object-types/extend-system-object-types.md) in your mappers, extend the `OrderData` class and use your custom class throughout your order creation implementation:

```csharp title="Example - Custom order data"
using CMS.Commerce;

/// <summary>
/// Extended order data with custom fields.
/// </summary>
public class CustomOrderData : OrderData
{
    public int LoyaltyPoints { get; set; }
    public string AdditionaNotes { get; set; } = string.Empty;
}
```

You can also extend address DTOs to add custom address fields:

```csharp title="Example - Custom address DTO"
using CMS.Commerce;

/// <summary>
/// Extended address DTO with delivery notes field.
/// </summary>
public record CustomAddressDto : AddressDto
{
    public string DeliveryNotes { get; set; } = string.Empty;
}
```

Update your controller to use the custom order data type:

```csharp title="Example - Using custom order data"
using System.Threading;
using System.Threading.Tasks;

using CMS.Commerce;

using Microsoft.AspNetCore.Mvc;

/// <summary>
/// Example controller using custom order data type.
/// Shows how to create orders with custom fields.
/// </summary>
public class CustomCheckoutController : Controller
{
    private readonly IOrderCreationService<CustomOrderData, PriceCalculationRequest,
        PriceCalculationResult, AddressDto> orderCreationService;

    public CustomCheckoutController(
        IOrderCreationService<CustomOrderData, PriceCalculationRequest,
            PriceCalculationResult, AddressDto> orderCreationService)
    {
        this.orderCreationService = orderCreationService;
    }

    public async Task<IActionResult> CreateOrder(CancellationToken cancellationToken)
    {
        var orderData = new CustomOrderData
        {
            // Populates standard fields
            BuyerIdentifier = BuyerIdentifier.FromMemberId(1),
            OrderNumber = "ORD-2024-0001",
            LanguageName = "en",
            BillingAddress = new AddressDto
            {
                FirstName = "John",
                LastName = "Doe",
                Email = "john@example.com",
                Line1 = "123 Main St",
                City = "New York",
                Zip = "10001",
                CountryID = 1,
                StateID = 1
            },
            OrderItems =
            [
                new OrderItem
                {
                    ProductIdentifier = new ProductIdentifier { Identifier = 123 },
                    Quantity = 2
                }
            ],
            
            // Populates custom fields
            LoyaltyPoints = 150
        };

        int orderId = await orderCreationService.CreateOrder(orderData, cancellationToken);
        return RedirectToAction("Confirmation", new { orderId });
    }
}
```
