DEV Community

Cover image for Kentico CMS Quick Tip: Understanding E-Commerce XML Structures
Sean G. Wright for WiredViews

Posted on • Edited on

Kentico CMS Quick Tip: Understanding E-Commerce XML Structures

Photo by Ramiro Mendes on Unsplash

E-Commerce Types and XML Data

Kentico's documentation provides examples of ways that developers can interact with the OrderInfo and OrderItemInfo types πŸ‘.

These are the primary objects developers retrieve order information from after a customer completes checkout with their shopping cart and an OrderInfo instance is created from the ShoppingCartInfo instance belonging to the customer.

However, it's not so clear from the documentation how all the fields in these objects get populated and what values we should expected to be stored in them πŸ€·πŸ½β€β™€οΈ.

This is especially true with the XML structures that Kentico uses for Summary and Custom Data fields, which we might find ourselves needing to parse for business logic customizations 🀨.

Below, we'll look at where these XML values come from and what schema we should expect them to use πŸ€“.

OrderInfo fields

OrderCustomData

How do we populate it?

This is one of a couple fields that is transparently copied from the ShoppingCartInfo to the OrderInfo when the order is created, so we can use either the ShoppingCartInfo.ShoppingCartCustomData or OrderInfo.OrderCustomData.

// Via the ShoppingCartInfo
var shoppingService = Service.Resolve<IShoppingService>();

ShoppingCartInfo cart = shoppingService.GetCurrentShoppingCart();

cart.ShoppingCartCustomData.SetValue("CustomDataNumber", 42);
cart.ShoppingCartCustomData.SetValue("CustomDataString", "Hello");

cart.Update();
Enter fullscreen mode Exit fullscreen mode
// Via the OrderInfo
OrderInfo order = OrderInfoProvider.GetOrderInfo(23);

order.OrderCustomData.SetValue("CustomDataNubmer", 42);
order.OrderCustomData.SetValue("CustomDataString", "Hello");

order.Update();
Enter fullscreen mode Exit fullscreen mode

Where is it stored in the db?

COM_Order.OrderCustomData

What will it look like?

<CustomData>
    <CustomDataNumber>42</CustomDataNumber>
    <CustomDataString>Hello</CustomDataString>
</CustomData>
Enter fullscreen mode Exit fullscreen mode

OrderPaymentResult

How do we populate it?

To set a payment result on an order we need to use the OrderInfo.UpdateOrderStatus() method, passing a valid PaymentResultInfo.

var paymentResult = new PaymentResultInfo
{
    PaymentDate = DateTime.Now,
    PaymentDescription = "Test Payment completed",
    PaymentIsCompleted = true,
    PaymentIsAuthorized = true,
    PaymentTransactionID = Guid.NewGuid().ToString(),
    PaymentStatusValue = Guid.NewGuid().ToString(),
    PaymentMethodName = "TestPaymentName"
};

OrderInfo order = OrderInfoProvider.GetOrderInfo(23);

order.UpdateOrderStatus(paymentResult);

order.Update();
Enter fullscreen mode Exit fullscreen mode

We can see below that each value populated in the PaymentResultInfo creates an element in the XML stored in the database.

The values like {$PaymentGateway.Result.TransactionID$} are unresolved localization macros that would be populated with translated values of the specific payment gateway / payment method being used for this order 🧐.

However, in my examples I have not populated those localization strings.

Where is it stored in the db?

COM_Order.OrderPaymentResult

What will it look like?

<result>
    <item name="date" 
        header="{$PaymentGateway.Result.Date$}" 
        value="11/17/2019 10:16:30 PM" />
    <item name="method" 
        header="{$PaymentGateway.Result.PaymentMethod$}" 
        text="TestPaymentName" />
    <item name="status" 
        header="{$PaymentGateway.Result.Status$}" 
        value="6a418f48-2aee-4bde-86ef-c6e3eef6667e" />
    <item name="transactionid" 
        header="{$PaymentGateway.Result.TransactionID$}" 
        value="237b5af1-bc28-4187-b9d4-0ecf1084b6ec" />
    <item name="description" 
        header="{$PaymentGateway.Result.Description$}" 
        value="Test Payment completed" />
    <item name="completed" 
        header="{$PaymentGateway.Result.IsCompleted$}" 
        value="1" text="{$PaymentGateway.Result.PaymentCompleted$}" />
    <item name="authorized" 
        header="{$PaymentGateway.Result.IsAuthorized$}" 
        value="1" text="{$paymentgateway.result.paymentauthorized$}" />
    <item name="failed" header="{$PaymentGateway.Result.IsFailed$}" />
</result>
Enter fullscreen mode Exit fullscreen mode

OrderDiscounts

How do we populate it?

Order discounts come from coupon codes defined in the CMS under the "Order Discounts" module.

In the example below I've created an Order Discount named Test Discount 10% that applies a 10% discount to the entire order.

Then I created a Coupon Code, TEST_10_OFF, for this discount.

var shoppingService = Service.Resolve<IShoppingService>();

shoppingService.AddCouponCode("TEST_10_OFF");
Enter fullscreen mode Exit fullscreen mode

Where is it stored in the db?

COM_Order.OrderDiscounts

What will it look like?

<Summary>
    <Item>
        <Name>Test Discount 10%</Name>
        <Value>0.30</Value>
    </Item>
</Summary>
Enter fullscreen mode Exit fullscreen mode

OrderOtherPayments

How do we populate it?

This field stores gift cards applied to the order, and in Kentico gift cards are basically coupons that reduce the final cost of the entire order and also the total balance of the gift card.

So, it shouldn't be much of a surprise that gift cards are added to an order the same way coupon codes are πŸ˜€.

For the example below, in the CMS "Gift Cards" module, I created a gift card named "Two Dollar Gift Card" with a coupon code TWO_DOLLARS_PLEASE.

var shoppingService = Service.Resolve<IShoppingService>();

shoppingService.AddCouponCode("TWO_DOLLARS_PLEASE");
Enter fullscreen mode Exit fullscreen mode

Where is it stored in the db?

COM_Order.OrderOtherPayments

What will it look like?

<Summary>
    <Item>
        <Name>Two Dollar Gift Card</Name>
        <Value>2.00</Value>
    </Item>
</Summary>
Enter fullscreen mode Exit fullscreen mode

OrderTaxSummary

How do we populate it?

Taxes for an order are calculated by an ITaxCalculationService which has a pretty simple signature, TaxCalculationResult CalculateTaxes(TaxCalculationRequest taxRequest).

Kentico's documentation has a good example of how an implementation of this interface might look πŸ’ͺ🏿.

Here's a trivial example of an ITaxCalculationService that sets some values in the TaxCalculationResult.Summary.

public class CustomTaxCalculationService : ITaxCalculationService
{
    public TaxCalculationResult CalculateTaxes(
        TaxCalculationRequest taxRequest)
    {
        var result = new TaxCalculationResult();

        decimal itemsTotal = ...

        result.ItemsTax = itemsTotal * .01m;

        result.Summary.Sum("Custom tax", itemsTotal * .027m);

        decimal shippingTaxRate = 0.15m;
        result.ShippingTax = taxRequest.ShippingPrice * shippingTaxRate;

        result.Summary.Sum("Custom shipping tax", result.ShippingTax);

        result.Summary.Sum("County Tax", itemsTotal * .06m);

        return result;
    }
}
Enter fullscreen mode Exit fullscreen mode

We can see below how each of the string keys used in our call to Summary.Sum() (like "County Tax") results in a element in the XML summary.

Where is it stored in the db?

COM_Order.OrderTaxSummary

What will it look like?

<Summary>
    <Item>
        <Name>Custom tax</Name>
        <Value>0.270</Value>
    </Item>
    <Item>
        <Name>Custom shipping tax</Name>
        <Value>0.000</Value>
    </Item>
    <Item>
        <Name>County Tax</Name>
        <Value>0.1620</Value>
    </Item>
</Summary>
Enter fullscreen mode Exit fullscreen mode

OrderCouponCodes

How do we populate it?

By applying any coupons to an order we can populate the OrderCouponCodes field - this include both order discount coupons and gift card coupons.

These are the coupons that result in the OrderOtherPayments and OrderDiscounts fields being populated 🧐.

This field will also contain any "Product Coupons" (see OrderItemDiscountSummary below) applied to the order, even though these are order item specific.

var shoppingService = Service.Resolve<IShoppingService>();

// Apply an order discount
shoppingService.AddCouponCode("TEST_10_OFF");

// Apply a gift card
shoppingService.AddCouponCode("TWO_DOLLARS_PLEASE");
Enter fullscreen mode Exit fullscreen mode

Where is it stored in the db?

COM_Order.OrderCouponCodes

What will it look like?

<CouponCodes>
    <CouponCode>
        <Code>TEST_10_OFF</Code>
    </CouponCode>
    <CouponCode>
        <Code>TWO_DOLLARS_PLEASE</Code>
        <ValueInMainCurrency>2.00</ValueInMainCurrency>
    </CouponCode>
</CouponCodes>
Enter fullscreen mode Exit fullscreen mode

OrderItem fields

OrderItemCustomData

How do we populate it?

This field works just like OrderInfo.OrderCustomData, except it allows us to store custom data per line-item in the order. This means we can use either ShoppingCartItemInfo.CartItemCustomData or OrderItemInfo.OrderItemCustomData.

// Via the ShoppingCartItemInfo
var shoppingService = Service.Resolve<IShoppingService>();

ShoppingCartInfo cart = shoppingService.GetCurrentShoppingCart();

ShoppingCartItemInfo cartItem = cart.CartItems.First();

cartItem.CartItemCustomData.SetValue("CustomDataNumber", 10);
cartItem.CartItemCustomData.SetValue("CustomDataString", "Hello");

cartItem.Update();
Enter fullscreen mode Exit fullscreen mode
// Via the OrderItemInfo
ICollection<OrderItemInfo> orderItems = OrderItemInfoProvider
    .GetOrderItems(orderId);

OrderItemInfo orderItem = orderItems.First();

orderItem.OrderItemCustomData.SetValue("CustomDataNumber", 10);
orderItem.OrderItemCustomData.SetValue("CustomDataString", "Hello");

orderItem.Update();
Enter fullscreen mode Exit fullscreen mode

Where is it stored in the db?

COM_OrderItem.OrderItemCustomData

What will it look like?

<CustomData>
    <CustomDataNumber>10</CustomDataNumber>
    <CustomDataString>Hello</CustomDataString>
</CustomData>
Enter fullscreen mode Exit fullscreen mode

OrderItemProductDiscounts

How do we populate it?

This field is populated by any catalog discounts we have defined for our store in the CMS. These can be managed in the "Catalog Discounts" module.

For the example below, I've created a catalog discount named "Test Product 5% off" which applies a percentage discount of 5% to a single product named "Test Product".

This means the "Test Product" needs to be in my order for this discount to be applied and the field to be populated βœ”.

SKUInfo sku = SKUInfoProvider
    .GetSKUs()
    .WhereEquals(nameof(SKUInfo.SKUName), "TestProduct")
    .TopN(1)
    .FirstOrDefault();

var shoppingService = Service.Resolve<IShoppingService>();

ShoppingCartInfo cart = shoppingService.GetCurrentShoppingCart();

shoppingService.AddItemToCart(sku.SKUID, 1);
Enter fullscreen mode Exit fullscreen mode

Note that this is the one discount/coupon related field where the discount value and not the discounted amount is stored.

So, for example, a Value of "0.05" for a catalog discount of 5% would be stored and not "1.00" for a 10% discount on a $10.00 product.

All other discount/coupon fields store the amount to be subtracted from the order or item price.

Where is it stored in the db?

COM_OrderItem.OrderItemProductDiscounts

What will it look like?

<Summary>
    <Item>
        <Name>Test Product 5% off</Name>
        <Value>0.05</Value>
    </Item>
</Summary>
Enter fullscreen mode Exit fullscreen mode

OrderItemDiscountSummary

How do we populate it?

This field is populated when the order has items that are part of a Buy->Get type discount, which are defined in the CMS "Buy X Get Y" module.

These kinds of discounts state that if a customer purchases some set of products they will get a discount of some amount either on specific items (in order to encourage buying products together) or on the cheapest items in the cart (as a promotion) πŸ€”.

In the example below I have a discount defined that applies a 20% discount named "Test Buy X Get Y" on the product named "Test Accessory" if the order also has a product named "Test Product".

SKUInfo skuProduct = SKUInfoProvider
    .GetSKUs()
    .WhereEquals(nameof(SKUInfo.SKUName), "TestProduct")
    .TopN(1)
    .FirstOrDefault();

SKUInfo skuAccessory = SKUInfoProvider
    .GetSKUs()
    .WhereEquals(nameof(SKUInfo.SKUName), "TestAccessory")
    .TopN(1)
    .FirstOrDefault();

var shoppingService = Service.Resolve<IShoppingService>();

ShoppingCartInfo cart = shoppingService.GetCurrentShoppingCart();

shoppingService.AddItemToCart(skuProduct.SKUID, 1);
shoppingService.AddItemToCart(skuAccessory.SKUID, 1);
Enter fullscreen mode Exit fullscreen mode

We can also populate this field with coupons created in the "Product Coupons" CMS module.

If we add a coupon to the shopping cart that applies to a product in the cart, the Product Coupon display name and the discount amount will be stored in the XML.

Where is it stored in the db?

COM_OrderItem.OrderItemDiscountSummary

What will it look like?

<Summary>
    <Item>
        <Name>Test Buy X Get Y</Name>
        <Value>0.20</Value>
    </Item>
</Summary>
Enter fullscreen mode Exit fullscreen mode

Summary

Kentico has all sorts of nooks and crannies, hiding implementation details. Some times the defaults fulfill the needs of our business logic, but other times we need to explore these thins to understand how we can best apply customizations πŸ€“.

We've looked at how the XML fields of ShoppingCartInfo, ShoppingCartItemInfo, OrderInfo, and OrderItemInfo are populated, used, and stored in the database.

These fields can usually be ignored until we explicitly need them, and in those cases it's nice to have some concrete examples, like the ones above, to ensure the results we are seeing in our applications are the correct ones.

Hopefully this blog post can be a little cheat-sheet for you (and me! 😎) when working with Kentico's E-Commerce functionality.

Thanks for reading πŸ™!


If you are looking for additional Kentico content, checkout the Kentico tag here on DEV:

#kentico

Or my Kentico blog series:

Top comments (0)