Implement order creation

Advanced license required

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

The Order Creation Service provides a streamlined way to create orders from shopping cart data. It automates the entire order creation workflow, including customer management, price calculation, and order persistence.

Developer preview

This feature is released as an experimental developer preview feature and is subject to change in future updates. Use it for evaluation purposes and provide feedback to help shape its final design.

What should you do with this feature?

  • DO try out development of price calculation service and calculation steps.
  • DO feel free to share your feedback with the Kentico Product team.
  • DO NOT use the feature in production projects.

The Order Creation Service (IOrderCreationService) is a high-level API that handles the complete order creation process. When you call the service with order data, it automatically:

  1. Manages customers and addresses – Creates or updates customer records and their addresses based on the order data.
  2. Calculates prices – Integrates with the Price Calculation Service to calculate order totals, taxes, and shipping costs.
  3. Creates order records – Generates and persists the order, order items, and order addresses in the database.
  4. Sends notifications – Triggers external and internal order notifications for corresponding order states.

Key concepts

Order data model

Order data is represented by the IOrderData interface and its implementation OrderData. It contains:

  • Customer information – Member ID (for authenticated users)
  • Order items – Products and quantities using IOrderItem<ProductIdentifier>
  • Addresses – Billing and shipping addresses as AddressDto objects
  • Payment and shipping – Selected payment and shipping method IDs
  • Language – Language name for product data localization
  • Order number – Unique identifier for the order
C#
Basic order data structure

var orderData = new OrderData
{
    MemberId = currentMemberId,
    OrderNumber = "ORD-2024-0001",
    LanguageName = "en",
    BillingAddress = new AddressDto { /* address fields */ },
    ShippingAddress = new AddressDto { /* address fields */ },
    PaymentMethodId = 1,
    ShippingMethodId = 2,
    OrderItems = new[]
    {
        new OrderItem
        {
            ProductIdentifier = new ProductIdentifier { Identifier = 123 },
            Quantity = 2
        }
    }
};

Address handling

Addresses are represented by the AddressDto record, which includes:

  • Contact information – First name, last name, company, email, phone
  • Location details – Address lines, city, postal code, country, and state

The service automatically:

  • Creates customer addresses if they don’t already exist
  • Compares addresses to avoid duplicates (case-insensitive, trimmed comparison)
  • Uses shipping address data as primary source for customer information when available

Customer management

The service handles both authenticated and anonymous users:

  • Authenticated users (members) – Retrieves existing customer records by member ID or creates new ones.
  • Anonymous users – Creates a new customer record for each order using email or billing address as the identifier.

Customer addresses are stored separately and reused across orders when they match existing addresses.

Price calculation integration

The Order Creation Service integrates with the Price Calculation Service to compute order totals. You can customize the price calculation by:

  • Implementing IPriceCalculationRequestMapper<TCalculationRequest, TOrderData> to add custom data to calculation requests.
  • Using custom calculation steps to modify pricing logic.

For more information on price calculation, see Implement price calculation.

Using the Order Creation Service

Basic usage

To use the Order Creation Service in your checkout process:

  1. Inject IOrderCreationService<TOrderData, TCalculationRequest, TCalculationResult, TAddressDto> into your controller or service.
  2. Prepare the OrderData object with cart items, addresses, and selected methods.
  3. Call CreateOrder to process the order.
C#
Example - Basic order creation

public class CheckoutController : Controller
{
    private readonly IOrderCreationService<OrderData, PriceCalculationRequest,
        PriceCalculationResult, AddressDto> orderCreationService;

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

    public async Task<IActionResult> ConfirmOrder(
        CustomerViewModel customer,
        CustomerAddressViewModel billingAddress,
        ShippingAddressViewModel shippingAddress,
        int paymentMethodId,
        int shippingMethodId,
        CancellationToken cancellationToken)
    {
        // Retrieve shopping cart items (implementation depends on your cart model)
        var cartItems = await GetShoppingCartItems(cancellationToken);

        // Prepare order data
        var orderData = new OrderData
        {
            MemberId = User.GetMemberId(), // null for anonymous users
            OrderNumber = await GenerateOrderNumber(cancellationToken),
            LanguageName = currentLanguage,
            BillingAddress = MapToAddressDto(billingAddress, customer),
            ShippingAddress = shippingAddress.IsSameAsBilling
                ? null
                : MapToAddressDto(shippingAddress, customer),
            PaymentMethodId = paymentMethodId,
            ShippingMethodId = shippingMethodId,
            OrderItems = cartItems.Select(item => new OrderItem
            {
                ProductIdentifier = item.ProductIdentifier,
                Quantity = item.Quantity
            })
        };

        // Create the order
        int orderId = await orderCreationService.CreateOrder(orderData, cancellationToken);

        // Clear the shopping cart and redirect to confirmation page
        await ClearShoppingCart(cancellationToken);

        return RedirectToAction("OrderConfirmation", new { orderId });
    }

    private AddressDto MapToAddressDto(
        CustomerAddressViewModel address,
        CustomerViewModel customer)
    {
        return new AddressDto
        {
            FirstName = customer.FirstName,
            LastName = customer.LastName,
            Company = customer.Company,
            Email = customer.Email,
            Phone = customer.PhoneNumber,
            Line1 = address.Line1,
            Line2 = address.Line2,
            City = address.City,
            Zip = address.PostalCode,
            CountryID = address.CountryId,
            StateID = address.StateId
        };
    }
}

Working with custom product identifiers

If your product catalog uses product variants or custom identifiers, you can extend ProductIdentifier and OrderItem:

C#
Example - Custom product identifier with variants

// Custom identifier that includes variant information
public record ProductVariantIdentifier : ProductIdentifier
{
    public int? VariantIdentifier { get; init; }
}

// Custom order item using the variant identifier
public record ProductVariantOrderItem : OrderItemBase<ProductVariantIdentifier>
{
}

// Custom order data using the variant items
public class VariantOrderData : OrderDataBase<ProductVariantOrderItem, AddressDto>
{
}

// Usage in checkout
var orderData = new VariantOrderData
{
    OrderItems = new[]
    {
        new ProductVariantOrderItem
        {
            ProductIdentifier = new ProductVariantIdentifier
            {
                Identifier = 123,
                VariantIdentifier = 5 // Size Large, Color Blue
            },
            Quantity = 1
        }
    },
    // ... other properties
};

await orderCreationService.CreateOrder(orderData, cancellationToken);

Customization points

The Order Creation Service provides several interfaces that allow you to customize the order creation process without modifying core functionality.

Customer information mapping

Implement ICustomerInfoMapper<TOrderData> to add custom fields to customer records:

C#
Example - Custom customer mapper

public class CustomCustomerInfoMapper : ICustomerInfoMapper<OrderData>
{
    public CustomerInfo PopulateInfo(CustomerInfo customerInfo, OrderData orderData)
    {
        // Add custom logic to populate additional customer fields
        // Example: customerInfo.SetValue("CustomerLoyaltyPoints", orderData.LoyaltyPoints);

        return customerInfo;
    }
}

// Register in Program.cs
builder.Services.AddSingleton<ICustomerInfoMapper<OrderData>, CustomCustomerInfoMapper>();

Order information mapping

Implement IOrderInfoMapper<TOrderData, TCalculationResult> to add custom fields to order records:

C#
Example - Custom order mapper

public class CustomOrderInfoMapper : IOrderInfoMapper<OrderData, PriceCalculationResult>
{
    public OrderInfo PopulateInfo(
        OrderInfo orderInfo,
        OrderData orderData,
        PriceCalculationResult calculationResult)
    {
        // Add custom logic to populate additional order fields
        // Example: orderInfo.SetValue("OrderDiscountCode", orderData.DiscountCode);

        return orderInfo;
    }
}

// Register in Program.cs
builder.Services.AddSingleton<IOrderInfoMapper<OrderData, PriceCalculationResult>,
    CustomOrderInfoMapper>();

Address mapping

Implement ICustomerAddressInfoMapper<TAddressDto> or IOrderAddressInfoMapper<TAddressDto> to add custom fields to address records:

C#
Example - Custom address mapper

public class CustomAddressInfoMapper : IOrderAddressInfoMapper<AddressDto>
{
    public OrderAddressInfo PopulateInfo(
        OrderAddressInfo orderAddressInfo,
        AddressDto addressDto)
    {
        // Add custom logic to populate additional address fields
        // Example: orderAddressInfo.SetValue("AddressNotes", addressDto.DeliveryNotes);

        return orderAddressInfo;
    }

    public AddressDto PopulateDto(AddressDto addressDto, OrderAddressInfo orderAddressInfo)
    {
        // Reverse mapping if needed for reading order addresses
        return addressDto;
    }
}

// Register in Program.cs
builder.Services.AddSingleton<IOrderAddressInfoMapper<AddressDto>, CustomAddressInfoMapper>();

Order item mapping

Implement IOrderItemInfoMapper<TOrderData, TCalculationResult> to add custom fields to order items:

C#
Example - Custom order item mapper

public class CustomOrderItemInfoMapper :
    IOrderItemInfoMapper<OrderData, PriceCalculationResult>
{
    public OrderItemInfo PopulateInfo(
        OrderItemInfo orderItemInfo,
        OrderData orderData,
        PriceCalculationResult calculationResult,
        ProductIdentifier productIdentifier)
    {
        // Add custom logic to populate additional order item fields
        // Example: orderItemInfo.SetValue("ItemWarrantyMonths", 24);

        return orderItemInfo;
    }
}

// Register in Program.cs
builder.Services.AddSingleton<IOrderItemInfoMapper<OrderData, PriceCalculationResult>,
    CustomOrderItemInfoMapper>();

Price calculation customization

Implement IPriceCalculationRequestMapper<TCalculationRequest, TOrderData> to add custom data to price calculation requests:

C#
Example - Custom calculation request mapper

public class CustomCalculationRequestMapper :
    IPriceCalculationRequestMapper<PriceCalculationRequest, OrderData>
{
    public PriceCalculationRequest PopulateCalculationRequest(
        PriceCalculationRequest calculationRequest,
        OrderData orderData)
    {
        // Add custom logic to modify the calculation request
        // Example: Add discount codes, customer tier information, etc.

        return calculationRequest;
    }
}

// Register in the dependency injection container
builder.Services.AddSingleton<IPriceCalculationRequestMapper<PriceCalculationRequest, OrderData>,
    CustomCalculationRequestMapper>();