Get Started with Salesforce Commerce Extensions

Extensions are a mechanism for using Apex to customize the functionality that powers B2B and B2C storefronts. The custom Apex class that implements an extension is called an extension provider.

There are two types of extensions: domain extensions and endpoint extensions. Domain extensions are related to a functional domain, such as pricing. Endpoint extensions are related to specific Connect API endpoints (resources).

When you implement an extension provider for a domain extension, you affect a functional area, and the customizations that you provide can affect multiple Connect API endpoints. In contrast, when you implement an extension provider for an endpoint extension, you affect only a single Connect API endpoint (resource).

For domain extensions, a provider can extend or replace the default functionality of a commerce domain. Extensions are available for a wide range of commerce domains, including cart, pricing, inventory, and more.

For endpoint extensions, a provider can modify the request and response of the Connect API endpoint, but can't replace the default functionality.

For the complete list of extensions that you can customize, see Available Extensions.

Extensions donโ€™t apply to the B2C Commerce architecture, which uses a different extensibility system called hooks.

Extensions represent the next step for Salesforce Commerce extensibility that began with integrations. Extensions allow you to customize functionality in a more targeted way than integrations. Also, extensions are available for more commerce domains than integrations.

Hereโ€™s a summary of the key differences between integrations and extensions:

IntegrationsExtensions
Coding approachImplement Apex interfaceExtend Apex base class
CustomizationReplaceExtend (for both domain or endpoint extensions) or replace (for domain extensions)
Commerce domainsCheckout calculators (4)Cart/checkout calculators (5), pricing service, other services

To check whether an extension is generally available, see Available Extensions.

Although extensions are now the preferred way to customize Salesforce Commerce, integrations remain fully supported. Currently, payments canโ€™t be customized with an extension.

Extensions can be used by a wide range of developers. An in-house team or a systems integrator can create an extension provider to make small, targeted changes to a storefront to meet a merchantโ€™s unique business needs. For more comprehensive solutions, a vendor can bundle several extension providers together and distribute them as a package on AppExchange.

Extensions unlock new opportunities to integrate Commerce Cloud with other software (such as ERP systems), cut maintenance costs, and deliver great customer experiences.

Several domain extensions are designed to support cart calculation use cases, including cart calculations that occur during checkout. These extensions, taken together, comprise the Cart Calculate API. For more information, see Cart Calculate API.

In the Salesforce Commerce architecture, extensions can interact with many other parts of the system, including other extensions.

This diagram traces how a domain extension fits into the architecture using Pricing Service as an example.

Extensions Flow

Letโ€™s look at whatโ€™s happening at each stage of the extension flow, as indicated by the corresponding numbers in the diagram:

  1. The Pricing Service extension is invoked. An extension can be triggered by a user action, a Connect API requestโ€”or even another extension. The extensible code checks to see if an extension provider class is mapped for the specific extension and store. The code that checks the mapping is called an extension point. The identifier used to uniquely identify an extension is called an extension point name (EPN). The EPN is used to map an extension point to one or more extension providers.
  2. If an extension provider class is registered and mapped to the extension point name, the custom Apex in the extension provider class is executed. The extension provider can optionally make API requests.
  3. For domain extensions, the extension provider can invoke the default implementation using the super() method. If you plan to modify -- but not completely replace -- default functionality, we strongly recommend calling the super() method as a best practice to ensure platform compatibility. If we add features, the features are automatically supported in the super() method implementation.
  4. The extension provider can optionally invoke other commerce domain functionality, which can also be extended.

Each domain extension has a corresponding base class. Endpoint extensions, in contrast, all extend the same base class, and the extension provider canโ€™t invoke a super() method.

To show how a domain extension provider class is constructed, letโ€™s walk through a series of code snippets. The snippets extend the base Apex class for the Pricing Service extension.

The actual implementation details are left out of the snippets so that you can follow the structure of the extension class more easily. A working version of this extension class (along with other examples) is available on the Extension Provider Examples page. This example class is provided for illustration purposes only and isnโ€™t designed to represent a realistic use case.

Start by extending the base class, which is commercestorepricing.PricingService:

You can override the processPrice method of commercestorepricing.PricingService, which is used on PLPs and PDPs. In this example, weโ€™ve replaced the entire method with custom logic to extract product data, get the price from an external service, and process the pricing response.

The base class has another method that you can override: processTransactionalPrice. Overriding this method affects cart and checkout. For illustration purposes only, we treat this method differently than processPrice. Instead of replacing the entire method with custom logic, we call the base method with the super() method. Although weโ€™re calling the base method, we can still insert custom logic before and after the super() call.

We define a custom private method to get the prices from an external service:

As you can see from this example, for domain extensions, you have the flexibility to create extension classes that can:

  • Replace the entire default implementation of each base class method with custom logic.
  • Add custom logic before calling the default implementation of a base class method.
  • Add customer logic after calling the default implementation of a base class method.
  • Add custom methods and classes.

To learn how to add your own Apex classes to a Salesforce org, see Adding an Apex Class.

All endpoint extension provider classes extend the ConnectApi.BaseEndpointExtension class.

The ConnectApi.BaseEndpointExtension class defines the following virtual methods:

  • global virtual ConnectApi.EndpointExtensionRequest beforeGet(ConnectApi.EndpointExtensionRequest request)
  • global virtual ConnectApi.EndpointExtensionResponse afterGet(ConnectApi.EndpointExtensionResponse response, ConnectApi.EndpointExtensionRequest request)
  • global virtual ConnectApi.EndpointExtensionRequest beforePost(ConnectApi.EndpointExtensionRequest request)
  • global virtual ConnectApi.EndpointExtensionResponse afterPost(ConnectApi.EndpointExtensionResponse response, ConnectApi.EndpointExtensionRequest request)
  • global virtual ConnectApi.EndpointExtensionRequest beforePatch(ConnectApi.EndpointExtensionRequest request)
  • global virtual ConnectApi.EndpointExtensionResponse afterPatch(ConnectApi.EndpointExtensionResponse response, ConnectApi.EndpointExtensionRequest request)
  • global virtual ConnectApi.EndpointExtensionRequest beforePut(ConnectApi.EndpointExtensionRequest request)
  • global virtual ConnectApi.EndpointExtensionResponse afterPut(ConnectApi.EndpointExtensionResponse response, ConnectApi.EndpointExtensionRequest request)
  • global virtual ConnectApi.EndpointExtensionRequest beforeDelete(ConnectApi.EndpointExtensionRequest request)
  • global virtual ConnectApi.EndpointExtensionResponse afterDelete(ConnectApi.EndpointExtensionResponse response, ConnectApi.EndpointExtensionRequest request)

For each HTTP method, the base class provides a before and after method. You can override before methods to modify the request before the Connect API endpoint is called. And you can override after methods to modify the response of the Connect API endpoint after it's called.

The ConnectApi.EndpointExtensionRequest object wraps the request payload for the Connect API endpoint request. This object provides the following methods:

  • global Object getParam(String name) -- get the value for a parameter name.
  • global void setParam(String name, Object value) -- set the value for a parameter name.
  • global Set<String> getKeys() -- return all parameter names in the request.

The ConnectApi.EndpointExtensionResponse object wraps the response payload returned by the Connect API endpoint, and provides the following method:

  • global Object getResponseObject() -- returns the Connect API response wrapped as an Object.

To manipulate a request or response, you cast the request or response to a type that's expected for the Connect API endpoint.

For example, let's consider the Commerce Webstore Account Addresses Connect API endpoint.

This endpoint has an HTTP GET method and an HTTP POST method. Therefore, in your extension provider class for this endpoint, you can override the beforeGet, afterGet, beforePost, and afterPost methods.

To handle the requests and responses, you must know the Apex types for the various inputs and outputs.

In this example, the GET method doesn't take an input, but the request URL can specify multiple URL parameters.

The GET method returns a Commerce Address Collection output. Within your Apex code, you reference this type as ConnectApi.CommerceAddressCollectionRepresentation.

The POST method takes an input, but the request URL doesn't specify URL parameters. The POST method takes a Commerce Address Input input. Within your Apex code, you reference this type as ConnectApi.CommerceAddressInputRepresentation.

The POST method returns a Commerce Address output. Within your Apex code, you reference this type as ConnectApi.CommerceAddressOutputRepresentation.

In the beforeGet method, you can access the URL parameters in the request like this:

In the afterGet method, you access the body of the response like this:

In the beforePost method, you can access the request like this:

Lastly, in the afterPost method, you can access the response like this:

After you create a variable for the request or response, you can access the properties of the request and response with getters and setters. Each property has a corresponding getter and setter.

For example, you can access the count property of the ConnectApi.CommerceAddressCollectionRepresentation response like this:

After adding an extension class to a Salesforce org, there are two more tasks required before you can fill an extension slot with a custom extension provider:

  1. Register the extension class.
  2. Map the extension class to a B2B or B2C store.

The easiest way to register and map extension classes is with Salesforce CLI and the Salesforce Commerce plug-in for sf.

To manage extensions, you must use the latest version of Salesforce CLI. For detailed instructions, see Install Salesforce CLI and Update Salesforce CLI.

To install the plug-in, run sf plugins install @salesforce/commerce.

Example commands:

For detailed installation and usage information, see the plug-inโ€™s README.

You can also register and map an extension provider by inserting custom Apex into sObjects. The setup process for extensions is similar to the setup process for integrations. To register an extension provider, modify RegisteredExternalService and to map an extension provider, modify StoreIntegratedService.

Merchants can map and unmap domain extension providers directly in the administration interface. See Replace a Default Commerce Process with an Apex Class on Salesforce Help.

In this guide weโ€™ve explored how Salesforce Commerce extensions give you fine-grained control over storefront functionality. Weโ€™ve examined how extensions fit into the Salesforce Commerce architecture. We've walked through the construction of an example extension provider. We've also described the methods for registering and mapping extension providers.

Now that you have a solid understanding of extensions, the next steps are to: