Cart Calculate API

The Cart Calculate API (CCA) enables you to customize cart calculation, including calculations that occur during checkout.

The Cart Calculate API provides more control and flexibility with common integrations used in cart and checkout, including tax, inventory, pricing, promotions, and shipping. The Cart Calculate API contains individual calculators for each integration that can be configured and extended based on your business needs.

With the Cart Calculate API, you have granular control over selective integrations. You can easily turn off entire services that aren't needed. For example, if you only sell digital products, you don’t need a shipping service. In addition, you can control integrations at the step level. For example, you only want to show estimated tax in cart, but callout to your tax integration to display calculated tax in checkout. With full control over your integrations, cart and checkout can be optimized to run only the integrations you need, when you need them, optimizing both your storefront and API performance.

The Cart Calculate API consists of three levels of control:

  • Orchestrators -- control which calculators are invoked and when they’re invoked.
  • Calculators -- control cart calculations, such as those needed for shipping and tax.
  • Services -- control specific calculations but aren’t necessarily limited to use cases involving cart calculation. For example, the Pricing service, which is used in cart calculations, also affects your store's PDP (Product Detail Page) pages.

By default, the Cart Calculate API is enabled for new webstores created in Spring '24 and later releases. See Enable and Disable the Cart Calculate API for a Webstore to toggle the Cart Calculate API on and off. If the Cart Calculate API is off and your store has customizations that trigger cart item changes using SObject, Apex DML, or Delivery Group APIs then you must configure the Apex Price integration. If the Cart Calculate API is off and pricing integration isn’t configured, re-pricing won’t occur during checkout.

With a custom orchestrator, you can control which calculators are invoked and when they’re invoked. If you implement a custom orchestrator, the system invokes your orchestrator whenever cart calculations are needed.

For example, if the buyer adds a product to the cart, the system invokes your custom orchestrator, assuming that your custom orchestrator is both available and registered with the system.

Custom orchestrators extend the CartExtension.CartCalculate class and are registered for the Commerce_Domain_Cart_Calculate extension point.

By default, the system invokes calculators in the following order, but your orchestrator can modify this order or even skip one or more calculators:

OrderCalculatorInvoked for CartInvoked for CheckoutEPN/Base Class
1pricingYesYes

Commerce_Domain_Pricing_CartCalculator

CartExtension.PricingCartCalculator

2promotionsYesYes

Commerce_Domain_Promotions_CartCalculator

CartExtension.PromotionsCartCalculator

3inventoryYesYes

Commerce_Domain_Inventory_CartCalculator

CartExtension.InventoryCartCalculator

4shippingNoYes

Commerce_Domain_Shipping_CartCalculator

CartExtension.ShippingCartCalculator

5postShippingNoYesN/A
6taxesNoYes

Commerce_Domain_Pricing_CartCalculator

CartExtension.PricingCartCalculator

Although calculators are invoked in this order, the shipping, postShipping, and taxes calculators are only invoked during checkout by default. See Default Calculator Invocation Logic for more information about default behavior. Also see Custom Invocation Logic Example for an example of how to customize the default behavior.

The postShipping calculator selects the least expensive delivery method and adds a shipping charge for the method. You can't modify this behavior with a custom calculator. Your custom orchestrator, however, can skip this calculator if that's appropriate for your use case.

This following code snippet is taken from the CartCalculateSample.cls sample orchestrator.

This sample shows the default system invocation logic for calculators. A boolean variable is defined for each calculator, and the corresponding calculator is invoked only if the boolean variable evaluates to true.

For example, the runPricing variable is set to true only if the buyer adds, deletes, or updates a cart item, or if the buyer starts checkout.

If the runPricing variable evaluates to true, the price calculator is invoked. Otherwise, the price calculator is skipped and the orchestrator proceeds to the next calculator.

In your custom orchestrator, you can change the invocation logic as needed for your own use cases.

When implementing your custom orchestrator, it's important to understand how buyer actions impact cart and checkout calculations.

By default, shipping and tax calculators are invoked only during checkout, but you can customize this logic if you want.

The following code snippet shows an example of custom invocation logic. In this example, the shipping, post shipping, and taxes calculators can be invoked before the buyer starts checkout:

In the default invocation logic, the runShipping variable is only set to true if the buyer changes a delivery group:

This buyer action (changing a delivery group) is allowed only during checkout in B2B and B2C stores. Therefore, by default, the shipping calculator is invoked only during checkout.

In the custom invocation logic, this variable is also set to true if the buyer changes an item in the cart or changes a coupon:

Two of these buyer actions -- buyerActions.isCartItemChanged() and buyerActions.isCouponChanged() -- can occur before checkout. Therefore, in the custom logic, the shipping calculator is invoked both before and during checkout.

The custom invocation logic makes similar changes to the runPostShipping and runTaxes variables, enabling them to be invoked before and during checkout.

This example shows how to modify calculator invocation logic, but the custom logic wouldn't work in production without further customizations. For example, the Connect API resources for carts don’t accept a shipping address as an input. The sample invocation logic, therefore, would only make sense for a “flat rate” shipping scheme, not one based on address. Also, a custom tax calculator would be required, because the default tax calculator logic requires a shipping address.

Before checkout, a buyer can perform various actions that affect calculations for the cart:

  • Add an item to the cart: sets buyerAction.isCartItemChanged() to true, which executes the pricing and promotions calculators by default.
  • Remove an item from the cart: sets buyerAction.isCartItemChanged() to true, which executes the pricing and promotions calculators by default.
  • Change the quantity of an item in the cart: sets buyerAction.isCartItemChanged() to true, which executes the pricing and promotions calculators by default.
  • Add a coupon to the cart: sets buyerAction.isCouponChanged() to true, which executes the promotions calculator by default.
  • Remove a coupon from the cart: sets buyerAction.isCouponChanged() to true, which executes the promotions calculator by default.

When the buyer is ready to proceed to checkout, or after the buyer has already started checkout, the buyer can perform other actions that affect calculations for checkout:

  • Start checkout: sets buyerAction.isCheckoutStarted() to true, which executes the pricing, promotions, and inventory calculators by default.
  • Update shipping address: sets buyerAction.isDeliveryGroupChanged() to true, which executes the shipping and taxes calculators by default.
  • Update delivery method: sets buyerAction.isDeliveryMethodSelected() to true, which executes the taxes calculator by default.

To get information about buyer actions, you can use the BuyerActions and BuyerActionDetails objects.

The BuyerActions object provides several boolean methods that indicate which buyer actions have occurred since the previous cart calculation:

  • isCartItemChanged() -- Returns true if the buyer has created, updated, or deleted a cart item; false otherwise.
  • isCheckoutStarted() -- Returns true if the buyer has started checkout; false otherwise.
  • isCouponChanged() -- Returns true if the buyer has created, updated, or deleted a coupon; false otherwise.
  • isDeliveryGroupChanged() -- Returns true if the buyer has created, updated, or deleted a delivery group; false otherwise.
  • isDeliveryMethodSelected() -- Returns true if the buyer has selected a delivery method; false otherwise.

You obtain a BuyerActions object from the CartExtension.CartCalculateOrchestratorRequest object passed into your custom orchestrator's calculate() method. The CartExtension.CartCalculateOrchestratorRequest provides a getBuyerActions() method, which returns the BuyerActions object for the cart.

The BuyerActionDetails object provides additional details about buyer actions. This object isn't typically used in your custom orchestrator class, but it can be useful in one or more of your custom calculator classes.

The BuyerActionDetails object provides the following methods:

  • isCheckoutStarted() -- Returns true if the buyer has started checkout; false otherwise. This method behaves the same as the BuyerActions.isCheckoutStarted() method, but is accessible within a custom calculator class.
  • getDeliveryGroupChanges() -- Returns a list of CartDeliveryGroupChange objects with detailed info about the changes.
  • getCartItemChanges() -- Returns a list of CartItemChange objects with detailed info about the changes.
  • getCouponChanges() -- Returns a list of CouponChange objects with detailed info about the changes.

You obtain a BuyerActionDetails object from the CartExtension.CartCalculateCalculatorRequest object passed into your custom calculator's calculate() method. The CartExtension.CartCalculateCalculatorRequest object provides a getOptionalBuyerActionDetails() method, which returns a CartExtension.OptionalBuyerActionDetails object.

The CartExtension.OptionalBuyerActionDetails object provides a get() method, which returns a BuyerActionDetails object.

The BuyerActionDetails.getDeliveryGroupChanges() method returns a List of CartDeliveryGroupChange objects.

See CartDeliveryGroupChange.

The CartDeliverGroupChange object provides one method -- getChangedDeliveryGroup() -- which returns an OptionalCartDeliveryGroup object. This object is "optional" because it's possible the delivery group was deleted.

The BuyerActionDetails.getCartItemChanges() method returns a List of CartItemChange objects.

See CartItemChange.

The CartItemChange object provides the following methods:

  • getChangedItem() -- returns an OptionalCartItem object. This object is "optional" because it's possible the cart item was deleted.
  • isAdded() -- returns true if the cart item was added to the cart; otherwise, false.
  • isRemoved() -- returns true if the cart item was removed from the cart; otherwise, false.
  • isQuantityIncreased() -- returns true if the quantity was increased for the cart item; otherwise, false.
  • isQuantityDecreased() -- returns true if the quantity was decreased for the cart item; otherwise, false.

The BuyerActionDetails.getCouponChanges() method returns a List of CouponChange objects.

See CouponChange.

The CouponChange object provides the following methods:

  • getChangedAdjustmentBasis() -- returns an OptionalCartAdjustmentBasis object. This object is "optional" because it's possible the coupon was deleted.
  • isAdded() -- returns true if the coupon was added to the cart; otherwise, false.
  • isRemoved() -- returns true if the coupon was removed from the cart; otherwise, false.

Some calculator default implementations call underlying services:

  1. pricing -- invokes the pricing service, which is extensible via the Commerce_Domain_Pricing_Service base class.
  2. promotions -- invokes the promotions service, which isn’t extensible.
  3. inventory -- invokes the inventory service, which is extensible via the Commerce_Domain_Inventory_Service base class.
  4. shipping -- doesn't invoke a service.
  5. postShipping -- doesn't invoke a service.
  6. taxes -- invokes the tax service, which is extensible via the Commerce_Domain_Tax_Service base class.

Starting in Spring '24, the Cart Calculate API is enabled for all new webstores.

If you create a webstore in the Spring '24 release (or later) and the store is based on an LWR template, you can immediately use the Cart Calculate API.

If you created a webstore prior to the Spring '24 release and the store is based on an LWR template, you can manually enable the Cart Calculate API for your store.

If your webstore is based on an Aura template, the Cart Calculate API isn't supported. You can manually disable the Cart Calculate API for your store.

The Cart Calculate API isn't compatible with integrations. Your webstore can either use extensions or integrations, but not both. Although extensions are now the preferred way to customize Salesforce Commerce, integrations remain fully supported. If you want your Spring '24 (or later) webstore to use integrations, you must disable the Cart Calculate API.

To disable the Cart Calculate API for a webstore:

  1. In the Developer Console, click Query Editor.
  2. Enter the following query and click Execute: Select Id, Name, OptionsCartCalculateEnabled FROM Webstore
  3. In the OptionsCartCalculateEnabled column, set the value to false for the Webstore.

To enable the Cart Calculate API for a webstore:

  1. In the Developer Console, click Query Editor.
  2. Enter the following query and click Execute: Select Id, Name, OptionsCartCalculateEnabled FROM Webstore
  3. In the OptionsCartCalculateEnabled column, set the value to true for the Webstore.