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.
The status of a Cart is defined by its CartState. Carts that can be updated and ordered have the CartState Active
. A Customer can have more than one active Cart. To retrieve the most recently modified active Cart, use Get Cart or Get Cart in Store. To retrieve all active Carts for a Customer, use Query Carts or Query Carts in Store with a query predicate that targets both the Customer and CartState.
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 by the locales defined in the Store. If the API cannot find a matching locale in the Store languages, the first locale found in the Project settings is used as a fallback.
- 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.
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.
Stores
If a Customer is assigned to one or more Stores, they are considered a Store-specific Customer. Store-specific Customers can only be associated with Carts and Orders in the assigned Stores. In contrast, global Customers are not assigned to any Stores and can be associated with Carts and Orders either in specific Stores or without a Store.
For more information, see Global versus Store-specific Customers.
Cart price precision
- Prices with TypedMoney can be cent-precision or high-precision.
- When calculating net and gross prices, prices with decimals are rounded. If the fraction of
centAmount
is exactly 0.5, the RoundingMode defined bytaxRoundingMode
is applied to the Cart's price. - If the tax is not included in the price (
includedInPrice=false
on the TaxRate), the tax portions are derived from the net prices. If the tax is included in the price (includedInPrice=true
on the TaxRate), then the tax portions are derived from the gross prices. This only applies when thePlatform
TaxMode is used. - If the prices are discounted and if tax is included in the price (
includedInPrice=true
on the TaxRate), the discount is applied to the gross price of a Line Item or Cart. IfincludedInPrice=false
, the discount is applied to the net price of a Line Item or Cart. - 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
andTaxRate.country
are an exact match, and nostate
properties are present, theTaxRate
applies to the Cart. - If
shippingAddress.country
andTaxRate.country
are an exact match, but only one ofshippingAddress.state
orTaxRate.state
is present, the Tax Rate does not apply to the Cart. - If
shippingAddress.country
andTaxRate.country
are an exact match, andshippingAddress.state
andTaxRate.state
are an exact match, the Tax Rate applies to the Cart. - If
shippingAddress.country
andTaxRate.country
are present, andshippingAddress.state
andTaxRate.state
are present, but there is a mismatch of eithercountry
orstate
, 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, newly created Carts use the Platform
TaxMode. With this tax mode, the taxedPrice
of a Cart is calculated by Composable Commerce automatically based on the Tax Rate that applies for the country
(and optionally, state
) of the Cart's shippingAddress
. The Tax Rate is taken from the TaxCategory assigned to Products and Shipping Methods.
If you prefer to have more control over how taxes are calculated in your Project, including the use of an external tax calculation service, you can use one of the following TaxModes:
External
: you are responsible for explicitly setting the Tax Rates for Line Items and Custom Line Items. With this tax mode, you must specify whether tax is included in the price for Line Items or Custom Line Items. This mode is suitable when you want Composable Commerce to handle the tax calculations based on the provided Tax Rates.ExternalAmount
: you are responsible for providing the exact tax amounts for Line Item and Custom Line Items, and explicitly setting thetaxedPrice
of the Cart using Set Cart Total Tax. This mode is suitable when you want full control over the tax calculation. For more information about how to set tax amounts, see ExternalAmount TaxMode.
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 Item | Product Variant A | Product Variant B | Shipping |
---|---|---|---|
Line Item: Price | $15 | $25 | $5 |
Quantity | 10 | 5 | --- |
External Tax Rate: includedInPrice | false | true | false |
External Tax Rate: amount | 0.19 | 0.15 | 0.15 |
Taxed Item Price: totalNet | $150 | $108.70 | $5 |
Taxed Item Price: totalGross | $178.5 | $125 | $5.75 |
Calculation formula | 15 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 have a considerable effect on the Cart total.
Composable Commerce offers two tax calculation modes to support different use cases:
- The
LineItemLevel
TaxCalculationMode enforces tax calculation after multiplying the individual item price by the quantity. - The
UnitPriceLevel
mode calculates 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:
- A tax of 19% is included in the price for all Line Items.
- The Cart's TaxRoundingMode is
HalfEven
. - The Cart's price precision is two decimals.
Line Item # | Quantity | Unit Price | Line Item Total, Net ( LineItemLevel ) | Line Item Total, Net ( UnitPriceLevel ) | Line Item Total, Gross |
---|---|---|---|---|---|
1 | 1 | $1.00 | $0.84 | $0.84 | $1.00 |
2 | 10 | $1.08 | $9.08 | $9.10 | $10.80 |
3 | 10 | $108.08 | $908.24 | $908.20 | $1080.80 |
4 | 1 | $2.00 | $1.68 | $1.68 | $2.00 |
5 | 50 | $0.01 | $0.42 | $0.50 | $0.50 |
6 | 1 | $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.
removes any Line Items that are invalid due to an inactive StandalonePrice.
When a Product is unpublished, any associated Line Items already present in a Cart remain unaffected and can still be ordered. To prevent this, do the following:
- If the Product uses Embedded Prices, remove them from the unpublished Product.
- If the Product uses Standalone Prices, inactivate or delete the Standalone Prices.
updates any LineItem
price
field if the result of the Line Item price selection has changed because of a Product update,updates the ShippingInfo, in particular, the
price
andshippingMethodState
fields,updates the discounts, which may affect the LineItem or CustomLineItem
totalPrice
fields and ShippingInfodiscountedPrice
field,- if a Cart Discount becomes invalid due to deactivation, expiration, or the predicate no longer matches the Cart's content, the discount is removed.
- if a Cart Discount becomes active and applicable (since the last update), the discount is added.
- if a Discount Code becomes invalid due to usage limits, deactivation, expiration, or the predicate no longer matches the Cart's content, the discount is removed.
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, Cart Discounts and Discount Codes that become inactive, 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.
During Order creation, if a Frozen Cart contains a Discount that must be removed (according to the previously mentioned restrictions), the discount is removed, and the Order creation process fails.
Update an Order
An Order is a legally binding purchase agreement between the Merchant and the customer; therefore, changes to it should be carefully considered.
To update the Order, you have two options: direct updates or Order Edits. The option that you choose depends on whether a financial change to the purchase agreement is required.
If, for instance, a customer provides an incorrect shipping address by mistake, a change is required to ensure the goods can be delivered to the customer. Composable Commerce offers a direct update action to change the shipping address for such cases. However, if the proposed change to the shipping address results in changes to the applicable taxes of the Order items, a direct update to the Order is not possible. In this case, because a financial change will occur, you must use an Order Edit instead, which ensures the amendment is traceable for audit purposes.
To learn about which update actions are available for each method, see Overview of update actions for Cart, Order, and Order Edit.
Make non-financial changes
Use the Order update actions to directly modify an Order when no financial change occurs.
For example, changes to:
- Order number, State, or locale
- Customer's email address or billing address
- Shipping information, such as addresses, that do not require recalculation of the Shipping Method and shipping rates
- Payment information
- Deliveries and returns
- Any other change that does not affect the Order total
Make financial changes
Use Order Edits when you need to update an Order that results in a financial change.
For example, changes to:
- Line Items and Custom Line Items (such as quantity)
- Shipping information, such as addresses, that require recalculation of the Shipping Method and shipping rates
- Discounts
- Any other change that affects the Order total
In addition to any updates that financially impact the Order, you can also update non-financial properties within the same Order Edit. For example, you can update a Customer's email address alongside any changes that affect the total price of the Order.
The Order Edits API does not support tracking or reserving inventory. You can only create an Order Edit if the InventoryMode of the Order and its LineItems is None
.
Shipping addresses, including country and state, are used to identify applicable Shipping Methods and shipping rates for an Order. You can update shipping addresses using direct updates or Order Edits, however, Composable Commerce only triggers reevaluation of Shipping Methods and shipping rates when changes are initiated through an Order Edit.
Intended flow
The basic flow for an Order Edit:
Create an OrderEdit: use the Create OrderEdit endpoint to create an OrderEdit entity. Upon creation, the OrderEdit contains a reference to the target Order within the
resource
field, however, no data is copied between the entities. At this point, you can consider the OrderEdit to be a staging area to collect the proposed changes.Add the proposed changes: use the Update OrderEdit by ID endpoint to add the individual update actions to the OrderEdit. Immediately upon adding the update actions to the OrderEdit, the
stagedActions
are ran against the Order; a so-called dry run. As a response, the Order is returned as if the Order actions had been applied. This allows you to preview the changes and understand the impact on the total price of the Order. The response will also contain Messages which capture the changes applied to the Order. Dry runs are always performed against the latest version of the Order.If the Order endpoint contains API Extensions, they are also called during the dry run.
Review the preview: ensure a careful review of the preview takes place due to the Order always respecting the current product catalog, price configuration, and promotions. For example, if an OrderEdit is made soon after Order placement, changes will likely be minimal. However, if more time has passed, price adjustments and expired promotions may significantly impact the Order.
We recommend that you review the Messages within the preview to understand the proposed changes. For example, the OrderEditApplied Message details the before and after values of the net total, gross total, and tax portion.
Apply the changes: use the Apply OrderEdit endpoint to apply your proposed changes to the original Order.
When you apply an Order Edit, the Order is recalculated using the same criteria that is listed under Cart updates. During recalculation, Discounts that have become invalid or are otherwise no longer applicable are removed. Only Discounts that are valid at the time of the Order Edit application are applied to the updated Order.
The Order (not the OrderEdit) triggers the OrderEditApplied Message which contains both the original and the newly applied values. At this point, you can delete the Order Edit (if desired). Deleting an Order Edit does not affect the related Order.
State
To check the state of an Order Edit, use the result
field.
State | Description |
---|---|
NotProcessed | The changes have not been applied to the Order. This status shows on results returned from the Query OrderEdits endpoint. |
PreviewSuccess | A preview of the modified Order including any available Messages is available for evaluation. This status can show after OrderEdit creation and when an OrderEdit is fetched or updated via key or ID. |
PreviewFailure | A preview could not be generated due to one or more invalid changes specified in the errors field. This status can show after OrderEdit creation and when an OrderEdit is fetched or updated via key or ID. |
Applied | The changes have been applied to the Order. This state contains information detailing the state before (excerptBeforeEdit ) and after (excerptAfterEdit ) the OrderEdit was applied. Once applied, any additional changes must be done in a new OrderEdit. |
Overview of update actions for Cart, Order, and Order Edit
Update action | Cart | Order | Order 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 action | Cart | Order | Order Edit |
---|---|---|---|
Change LineItem Quantity | ✓ | - | ✓ |
Change LineItems Order | ✓ | - | - |
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 action | Cart | Order | Order 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 action | Cart | Order | Order Edit |
---|---|---|---|
Set Billing Address Custom Type | ✓ | ✓ | ✓ |
Set Billing Address CustomField | ✓ | ✓ | ✓ |
on Shipping Address
Update action | Cart | Order | Order Edit |
---|---|---|---|
Set Shipping Address Custom Type | ✓ | ✓ | ✓ |
Set Shipping Address CustomField | ✓ | ✓ | ✓ |
on ItemShipping Address
Update action | Cart | Order | Order Edit |
---|---|---|---|
Set ItemShipping Address Custom Type | ✓ | ✓ | ✓ |
Set ItemShipping Address CustomField | ✓ | ✓ | ✓ |
on ShippingMethod
Update action | Cart | Order | Order Edit |
---|---|---|---|
Set ShippingMethod TaxAmount | ✓ | - | ✓ |
Set ShippingMethod TaxRate | ✓ | - | ✓ |
on Shipping
Update action | Cart | Order | Order Edit |
---|---|---|---|
Set Shipping Custom Type | ✓ | ✓ | ✓ |
Set Shipping CustomField | ✓ | ✓ | ✓ |
on Delivery
Update action | Cart | Order | Order 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 action | Cart | Order | Order Edit |
---|---|---|---|
Set Delivery Address Custom Type | - | ✓ | ✓ |
Set Delivery Address CustomField | - | ✓ | ✓ |
on Parcel
Update action | Cart | Order | Order Edit |
---|---|---|---|
Set Parcel Measurements | - | ✓ | ✓ |
Set Parcel Tracking Data | - | ✓ | ✓ |
Set Parcel Items | - | ✓ | ✓ |
Set Parcel Custom Type | - | ✓ | ✓ |
Set Parcel CustomField | - | ✓ | ✓ |
on ReturnItem
Update action | Cart | Order | Order Edit |
---|---|---|---|
Set ReturnItem Custom Type | - | ✓ | ✓ |
Set ReturnItem CustomField | - | ✓ | ✓ |