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();
// Via the OrderInfo
OrderInfo order = OrderInfoProvider.GetOrderInfo(23);
order.OrderCustomData.SetValue("CustomDataNubmer", 42);
order.OrderCustomData.SetValue("CustomDataString", "Hello");
order.Update();
Where is it stored in the db?
COM_Order.OrderCustomData
What will it look like?
<CustomData>
<CustomDataNumber>42</CustomDataNumber>
<CustomDataString>Hello</CustomDataString>
</CustomData>
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();
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>
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");
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>
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");
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>
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;
}
}
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>
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
andOrderDiscounts
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");
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>
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();
// 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();
Where is it stored in the db?
COM_OrderItem.OrderItemCustomData
What will it look like?
<CustomData>
<CustomDataNumber>10</CustomDataNumber>
<CustomDataString>Hello</CustomDataString>
</CustomData>
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);
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>
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);
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>
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:
Or my Kentico blog series:
Top comments (0)