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 a Line Item that references a deleted or unpublished Product Variant, cannot be converted to 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 country, customer group, and channel.
    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

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
setKey-
setCustomerEmail
setPurchaseOrderNumber-
setShippingAddress
setBillingAddress
setCountry-
recalculate--
setShippingMethod-
setCustomShippingMethod-
addDiscountCode-
removeDiscountCode-
setCustomerId
setAnonymousId--
setCustomerGroup-
setCustomType
setCustomField
addPayment
removePayment
setShippingMethodTaxRate-
setShippingMethodTaxAmount-
setCartTotalTax--
setOrderTotalTax--
changeTaxMode-
setLocale
changeTaxRoundingMode-
setShippingRateInput-
changeTaxCalculationMode-
addShoppingList-
setDeleteDaysAfterLastModification--
addItemShippingAddress
removeItemShippingAddress
updateItemShippingAddress
setShippingAddressAndShippingMethod--
setShippingAddressAndCustomShippingMethod--
LineItem update actions
applyDeltaToLineItemShippingDetailsTargets--
setLineItemShippingDetails
addLineItem-
removeLineItem-
changeLineItemQuantity-
setLineItemCustomType
setLineItemCustomField
setShippingAddressCustomType
setShippingAddressCustomField
setBillingAddressCustomType
setBillingAddressCustomField
setItemShippingAddressCustomType
setItemShippingAddressCustomField
setDeliveryAddressCustomType
setDeliveryAddressCustomField
setDeliveryCustomType-
setDeliveryCustomField-
setParcelCustomType-
setParcelCustomField-
setReturnItemCustomType-
setReturnItemCustomField-
setLineItemTaxRate-
setLineItemTaxAmount-
setLineItemTotalPrice-
setLineItemPrice-
setLineItemDistributionChannel-
CustomLineItem update actions
applyDeltaToCustomLineItemShippingDetailsTargets--
setCustomLineItemShippingDetails-
addCustomLineItem-
removeCustomLineItem-
setCustomLineItemCustomType
setCustomLineItemCustomField
setCustomLineItemTaxAmount-
setCustomLineItemTaxRate-
changeCustomLineItemQuantity-
changeCustomLineItemMoney-
changeCustomLineItemPriceMode--
Composable Commerce
Getting StartedMerchant CenterTutorialsHTTP APIGraphQL APIImport & ExportSDKs & Client LibrariesCustom Applications
Frontend
Getting StartedStudioDevelopingHTTP API
Sign upLog inSupportStatusOfferingTech BlogIntegrationsUser Research Program
Copyright © 2023 commercetools
Privacy PolicyImprint