AI agents: the content index is at /llms.txt. Every page is also available as Markdown — send the request header Accept: text/markdown, or append .md to the page URL.
Xperience Docs

Cookie consent

We use necessary cookies (opens in a new tab) to run our website and improve your experience while browsing. Additional cookies are only used with your consent. You may revoke your consent on the Cookies Policy (opens in a new tab) page or in your browser at any time.

Configure (opens in a new tab)
  • Documentation
    Administration interface basicsDevelopers and adminsBusiness usersChangelogGlossary
  • Guides
    Welcome to Xperience by KenticoAdministration interface basicsCustomer journeysDigital marketingConfigurationDevelopmentArchitectureUpgrade to Xperience by Kentico
  • Training
    For AdminsFor Business usersFor DevelopersFor Solution architects
  • API Examples
    ConfigurationContent managementDevelopmentDigital marketing
  • Changelog
    Documentation updatesSecurity advisories
  1. Home
  2. Developers and admins
  3. Digital commerce
  4. Checkout process
  5. Customize order creation

Customize order creation


Was this page helpful?

Send us feedback Go back to top
  • Community Questions & Answers (opens in a new tab)
  • Contact support (opens in a new tab)

Advanced license required

Features described on this page require the Xperience by Kentico Advanced license tier.

The order creation service provides several mapper interfaces that allow you to customize the order creation process without modifying core functionality. Use these interfaces to add custom fields 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:

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 to customer records or modify customer data during order creation.

C#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);
    }
}

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.

C#Access customer objects via orderId
using System.Threading;
using System.Threading.Tasks;

using CMS.Commerce;
using CMS.DataEngine;

// Class that uses constructor dependency injection to obtain providers
public class CustomerComponent(
    IInfoProvider<OrderInfo> orderInfoProvider,
    IInfoProvider<CustomerInfo> customerInfoProvider)
{
    public async Task<CustomerInfo> GetOrderCustomer(int orderId, CancellationToken cancellationToken)
    {
        // Gets the order
        var order = await orderInfoProvider.GetAsync(orderId, cancellationToken);

        // Gets the associated customer
        return await customerInfoProvider.GetAsync(order.OrderCustomerID, cancellationToken);
    }
}

Register the mapper in the service container.

C#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 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.

C#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.

C#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 to address records. Both customer addresses and order addresses can be extended independently.

C#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);
    }
}

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.

C#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 to order items. This is useful for storing product-specific metadata or configuration data with each line item.

C#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.

C#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 before prices are calculated. This allows you to influence the pricing calculation with order-specific context.

C#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.

Register the mapper in the service container.

C#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 in your mappers, extend the OrderData class and use your custom class throughout your order creation implementation:

C#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:

C#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:

C#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 });
    }
}

On this page

  • Customization scenarios and overview
  • Mapper usage in order creation lifecycle
  • Customer information mapping
  • Order information mapping
  • Address mapping
  • Order item mapping
  • Price calculation customization
  • Extend order data objects

Related pages

  • Create orders
  • Price calculation
  • Customize price calculation