Carts and Orders overview

Overview of the concepts related to carts and orders involved in the checkout process with your Composable Commerce Project.

Carts hold Product Variants that are referred to as Line Items once added to the Cart. A Cart can be ordered and it either belongs to a Customer or an anonymous session. Carts that belong to an anonymous session are referred to as anonymous Carts.

Line Items and Custom Line Items

A Line Item is a snapshot of a Product Variant at the time it is added to a Cart. A Custom Line Item is a generic item not associated to any Product Variant. As such, they can be used for selling services or virtual goods that do not require any tracking of inventory, like additional fees or vouchers.

Line Items

The snapshot of a Product Variant is created from the ProductProjection as follows:

  • The current projection is used.
  • If the Cart is bound to a Store, the following additional checks and projections are performed:
    • For Stores with active Product Selections, the selected Product Variant must be included in at least one of the active Product Selections.
    • The selected Product Variant must be available in a Store.
    • All LocalizedStrings are filtered according to the locales defined in the Store.
    • All Prices are filtered according to the Product distribution Channels defined in the Store. Prices without a Channel are also included.
    • All ProductVariantAvailabilities are filtered according to the Inventory supply Channel defined in the Store. Inventory entries without a Channel are also included.

If the Product Variant changes, the relation to the current ProductProjection is kept, but the Line Item is not updated automatically. The Line Item can be updated through the Carts API on demand. With this update, the ProductVariant data is copied to the variant field of the LineItem.

Carts containing Line Items that reference deleted Product Variants cannot be used to create Orders.

Line Item Price selection

The price of a LineItem is selected from the Product Variant based on several conditions:

  • selection based on the price mode:

    As a first step, the API uses the Product's priceMode value to decide where to look up the prices of a Product Variant. See ProductPriceMode for more details.

  • selection based on the distribution channel, the currency, the country, and the customer group:

    The selection of the price is based on the distributionChannel provided on the LineItemDraft or on the Add LineItem update action, and on the currency, country, and the customerGroup of the Cart. If a price with an exact match cannot be found, wildcards (all countries, all channels, all CustomerGroups) are used.

    The list below describes the order in which the price is selected. If a price could not be found in one step, the rule in the next step will be applied until a price could be found. The priority order for the selection of the price is customer group > channel > country (customer group has the highest priority and country the lowest).
    The price for a specific currency is determined in the following order:

    1. Find a price for the customer group, channel, and country.
    2. Find a price for the customer group and channel for all countries.
    3. Find a price for the customer group and country for all channels.
    4. Find a price for the customer group for all channels and all countries.
    5. Find a price for the country and channel for all customer groups.
    6. Find a price for the channel for all customer groups and all countries.
    7. Find a price for the country for all customer groups and all channels.
    8. Find a price for all customer groups, all channels, and all countries.
  • selection based on the price's validity period:

    For all of the price selection steps, the prices with a validity period are checked before the prices without any validity period.
    If a currently valid price is found, it is used first.

  • tier price selection based on the Line Item quantity:

    If the Product Price has some PriceTier, the tier price valid for the Line Item quantity is selected.
    If no tier price can be used, the base price is used.

Custom Line Items

A generic Custom Line Item can be added to the Cart just like Line Items, but without Line Item Price selection, meaning you control the price and taxes of the Custom Line Item. Since the money value of a Custom Line Item can be negative, it can be used for discounts applied by vouchers. Furthermore, Custom Line Items can be used to implement a more complex cart logic via Cart Predicates.

A Cart's InventoryMode has no effect on Custom Line Items.

Cart price precision

  • Prices with TypedMoney can have cent-precision and high-precision.
  • When calculating net and gross prices, prices with decimals are rounded. In case the fraction of centAmount is exactly 0.5, the RoundingMode defined by taxRoundingMode is applied to the Cart's price.
  • The tax portions are derived from the net prices in case the tax is not included in the price, meaning the includedInPrice field in the TaxRate for the product is set to false. When the tax is included in the price (includedInPrice: true), then the tax portions are derived from the gross prices. This only applies when the Platform TaxMode is used.
  • When prices are discounted with relative discounts, they are always rounded in favor of the customer with the half-down rounding.

Cart tax rate selection

A Cart cannot calculate taxes for the items in it until the shippingAddress property is set. The shippingAddress.country and shippingAddress.state properties affect the selection criteria. These are compared to the TaxRate's country and state fields. A Tax Rate is only eligible if the country and state properties are an exact match. Because the state field is optional on both TaxRate and shippingAddress, this means the following:

  • If shippingAddress.country and TaxRate.country are an exact match, and no state properties are present, the TaxRate applies to the Cart.
  • If shippingAddress.country and TaxRate.country are an exact match, but only one of shippingAddress.state or TaxRate.state is present, the Tax Rate does not apply to the Cart.
  • If shippingAddress.country and TaxRate.country are an exact match, and shippingAddress.state and TaxRate.state are an exact match, the Tax Rate applies to the Cart.
  • If shippingAddress.country and TaxRate.country are present, and shippingAddress.state and TaxRate.state are present, but there is a mismatch of either country or state, the Tax Rate does not apply to the Cart.

If you need to include a state or country sub-unit for shipping purposes, use shippingAddress.region to avoid Tax Rate conflicts.

Cart tax calculation

Tax mode

By default, the taxed price of a Cart is calculated by Composable Commerce based on the tax rate that applies for the country (and optionally state) of the Cart's shippingAddress. In this default Platform TaxMode, the TaxRate is taken from the TaxCategory assigned to Products and Shipping Methods. If you retrieve the tax rates for a location from a third-party service, you can set those yourself in the External TaxMode. With this mode, you need to tell Composable Commerce whether tax is included in the price you provide on a Line Item, Custom Line Item, or on a Shipping Method.

When includedInPrice: true, the tax is calculated in a top-down manner, and when includedInPrice: false, the tax is calculated in a bottom-up manner.

The following example shows the impact of includedInPrice in the Cart calculation with taxRoundingMode: HalfEven and taxCalculationMode: LineItemLevel.

Line ItemProduct Variant AProduct Variant BShipping
Line Item: Price$15$25$5
Quantity105---
External Tax Rate: includedInPricefalsetruefalse
External Tax Rate: amount0.190.150.15
Taxed Item Price: totalNet$150$108.70$5
Taxed Item Price: totalGross$178.5$125$5.75
Calculation formula15 x 10 (1 + 0.19)25 x 5 / (1 + 0.15)5 x (1 + 0.15)

Tax calculation mode

Due to the principle of rounding on every calculation step, this slight difference in when to apply tax can end up having a considerable effect on the Cart total. Composable Commerce offers two different tax calculation modes to support the option your use case requires. The LineItemLevel TaxCalculationMode enforces tax calculation after multiplying the individual item price by the quantity. The UnitPriceLevel mode leads to calculating the taxed price before multiplying by the quantity.

The following example demonstrates the differences in the Cart total depending on the different tax calculation modes. Let's assume:

Line Item #QuantityUnit PriceLine Item Total, Net
(LineItemLevel)
Line Item Total, Net
(UnitPriceLevel)
Line Item Total, Gross
11$1.00$0.84$0.84$1.00
210$1.08$9.08$9.10$10.80
310$108.08$908.24$908.20$1080.80
41$2.00$1.68$1.68$2.00
550$0.01$0.42$0.50$0.50
61$4.90$4.12$4.12$4.90
Cart Total, Net
(LineItemLevel)
Cart Total, Net
(UnitPriceLevel)
Cart Total, Gross
$924.38$924.44$1100.00

As seen from the example, if a Line Item has a quantity of 1, the two modes have no impact on the total net value of a Line Item (see Line Item #1, 4, and 6). However, if the quantity is more than 1, the two modes calculate the total net value of a Line Item with a difference of several cents (see Line Item #2, 3, and 5). Depending on the specific Line Item values, the chosen mode can give a greater or lesser net value (see line item #2 compared to #3). A difference of 6 cents is accumulated in the Cart total. The connection to rounding is especially clear with Line Item #5. Due to rounding after applying tax to 0.01 and multiplying by quantity 50 upon that, the net value when using UnitPriceLevel ends up being equal to the gross value. With LineItemLevel, where quantity multiplication is performed as a first calculation step, the rounding occurs to a greater value, and the net value ends up being $0.42.

Cart updates

The lifetime of a Cart (controlled by the deleteDaysAfterLastModification property) can be adjusted according to your requirements. During this lifetime, a Cart is kept as it was the last time when actions were performed on it. It contains Line Items and Custom Line Items with prices that were valid at the time of the last Cart update and Cart Discounts that were active at that time. Any read method on a Cart alone will not lead to a representation with up-to-date prices and so on, only Cart updates will trigger this.

After all update actions are performed, the Cart:

  • removes any Line Items that are invalid (due to deleted Products, Product Variants, or Prices),
  • updates any LineItem price field if the result of the price selection has changed because of a Product update,
  • updates the ShippingInfo, in particular, the price and shippingMethodState fields,
  • updates the discounts, which may affect the LineItem or CustomLineItem totalPrice fields and ShippingInfo discountedPrice field,
  • updates the Cart totalPrice field,
  • updates the Tax Rates, in particular, the taxedPrice field,
  • updates the LineItem productKey.

To perform the above-mentioned updates without additional update actions, send a request with only the Recalculate update action.

To update the Product data saved in the Line Items, use the Recalculate update action with updateProductData set to true.

Frozen Carts

If you want to prevent carts from being modified by changes to prices and discounts that become active or inactive over time, you have the option to freeze the cart. When a cart is frozen, all prices stay as they were at the time that the cart was frozen. Update actions as well as background services modifying prices on the cart are deactivated for frozen carts. Inventory is not reserved for line items in frozen carts. Frozen carts can either be ordered or unfrozen. When unfrozen, a cart continues to accept new updates.

Restrictions of frozen carts:

  • Cart Discounts that become active or are created after cart freeze are not applied.
  • Cart Discounts and Product Discounts that expire after cart freeze remain applied.
  • New Discount Codes can still be added to the cart, but their DiscountCodeState is of DoesNotMatchCart.
  • Regardless of a cart freeze, Cart Discounts that are deleted or that become invalid due to Cart Predicates not matching, are removed from the cart.
  • Regardless of a cart freeze, Discount Codes that become invalid due to Cart Predicates not matching, remain present on the cart with the DiscountCodeState of DoesNotMatchCart.
  • Update actions that could change the price of items in the cart are not accepted. Find the list of restricted update actions on the Frozen value for CartState.
  • All other update actions are still supported and applied. The API still performs the validations in the cart ensuring the cart is convertible to an Order, and reports errors if such a validation fails.

Overview of update actions for Cart, Order, and Order Edit

Update actionCartOrderOrder Edit
Set Key-
Set Order Number-
Set Purchase Order Number-
Set Customer ID
Set Customer Email
Set Customer Group-
Set Anonymous ID--
Set Business Unit--
Add LineItem-
Remove LineItem-
Add CustomLineItem-
Remove CustomLineItem-
Add Shopping List-
Set Cart Total Tax--
Set Order Total Tax--
Change TaxMode-
Change Tax RoundingMode-
Change Tax CalculationMode-
Add DiscountCode-
Remove DiscountCode-
Set DirectDiscounts-
Add Payment
Remove Payment
Change PaymentState-
Set Billing Address
Set Shipping Address
Add ItemShippingAddress
Remove ItemShippingAddress
Update ItemShippingAddress
Add ShippingMethod--
Add Custom ShippingMethod--
Remove Shipping Method--
Set ShippingMethod-
Set ShippingAddress and ShippingMethod--
Set ShippingAddress and Custom ShippingMethod--
Set Custom ShippingMethod-
Set ShippingRateInput-
Add Delivery-
Remove Delivery-
Change ShipmentState-
Add ReturnInfo-
Set ReturnInfo-
Set ReturnShipmentState-
Set ReturnPaymentState-
Change OrderState-
Transition State-
Update SyncInfo-
Set Locale
Set Country-
Set Store-
Set Custom Type
Set CustomField
Set DeleteDaysAfterLastModification--
Freeze Cart--
Unfreeze Cart--
Recalculate--

on LineItem

Update actionCartOrderOrder Edit
Change LineItem Quantity-
Set LineItem TaxRate-
Set LineItem TaxAmount-
Set LineItem Price-
Set LineItem TotalPrice-
Set LineItem DistributionChannel-
Set LineItem ShippingDetails
Apply DeltaToLineItemShippingDetailTargets--
Set LineItem Custom Type
Set LineItem CustomField
Set LineItem InventoryMode--
Set LineItem SupplyChannel--
Transition LineItem State-
Import LineItem State-

on CustomLineItem

Update actionCartOrderOrder Edit
Change CustomLineItem Quantity-
Set CustomLineItem TaxRate-
Set CustomLineItem TaxAmount-
Change CustomLineItem Money-
Change CustomLineItem Price Mode--
Set CustomLineItem ShippingDetails
Apply DeltaToCustomLineItemShippingDetailsTargets--
Set CustomLineItem Custom Type
Set CustomLineItem CustomField
Transition CustomLineItem State-
Import CustomLineItem State-

on Billing Address

Update actionCartOrderOrder Edit
Set Billing Address Custom Type
Set Billing Address CustomField

on Shipping Address

Update actionCartOrderOrder Edit
Set Shipping Address Custom Type
Set Shipping Address CustomField

on ItemShipping Address

Update actionCartOrderOrder Edit
Set ItemShipping Address Custom Type
Set ItemShipping Address CustomField

on ShippingMethod

Update actionCartOrderOrder Edit
Set ShippingMethod TaxAmount-
Set ShippingMethod TaxRate-
Set Shipping Custom Type--
Set Shipping CustomField--

on Delivery

Update actionCartOrderOrder Edit
Set Deliver Address-
Add Parcel to Delivery-
Remove Parcel from Delivery-
Set Delivery Items-
Set Delivery Custom Type-
Set Delivery CustomField-

on Delivery Address

Update actionCartOrderOrder Edit
Set Delivery Address Custom Type-
Set Delivery Address CustomField-

on Parcel

Update actionCartOrderOrder Edit
Set Parcel Measurements-
Set Parcel Tracking Data-
Set Parcel Items-
Set Parcel Custom Type-
Set Parcel CustomField-

on ReturnItem

Update actionCartOrderOrder Edit
Set ReturnItem Custom Type-
Set ReturnItem CustomField-