Create a Custom Recommendations Component for B2B Stores Using Commerce Einstein APIs

Creating a custom B2B Commerce Einstein recommendations component includes implementing the Activity Tracking API and connecting the component to the Commerce Einstein Webstore Recommendations Connect API resource. This process is for B2B stores on the Aura platform.

Using Connect APIs ensures appropriate product entitlement filtering before recommendations are delivered.

Make sure to reindex after you change associations between buyer groups and products, such as when changing entitlement policies or product-to-buyer group assignments.

Your component must incorporate calls to the Activity Tracking API. When configuring the API:

  • Implement both the viewReco and clickReco activities.
  • If you replace the Product Detail Purchase Options component with a custom component, implement the viewProduct activity.
  • If you add custom add-to-cart functionality, implement the addToCart activity.

You can use custom JavaScript, HTML, and CSS to make front-end modifications. For more information, see the Lightning Aura Components Developer Guide.

  1. Create a custom Apex controller.

    1. Go to Setup | Developer Console.

    2. Select File | New | Apex Class.

    3. Enter a name for the Apex class.

      Make sure to match the component class name for easier identification. For example, recsController.

      The controller file (in this case, recsController.apxc) opens to show an empty class.

  2. Update the controller file, making sure to modify the orgDomain and webstoreId values to match your storefront domain and ID.

    1public with sharing class recsController {
    2    @AuraEnabled
    3    public static String getRecs(String recommender, String anchorValues, String cookie) {
    4        String orgDomain = 'alpinecommerce236.my.salesforce.com';
    5        String webstoreId = '0ZEB0000000HMNGOA4';
    6        String endpoint = 'https://'+orgDomain+'/services/data/v55.0/commerce/webstores/
    7        '+webstoreId+'/ai/recommendations?language=en-US&asGuest=true&recommender='+recommender;
    8        if (anchorValues.length() > 0) {
    9            endpoint += '&anchorValues='+anchorValues;
    10        }
    11
    12        HttpRequest req = new HttpRequest();
    13        req.setEndpoint(endpoint);
    14        req.setHeader('Cookie', cookie);
    15        req.setMethod('GET');
    16        req.setHeader('Authorization', 'OAuth ' + UserInfo.getSessionId());
    17
    18        Http http = new Http();
    19        HTTPResponse res = http.send(req);
    20        return res.getBody();
    21    }
    22}
  3. Update the controller file with the appropriate profile for buyers.

    1. Go to Setup | Custom Code | Apex Classes.

    2. Next to the controller file that you created, click Security.

    3. Add the Shopper profile to the enabled profiles column.

    4. Click Save.

  4. Create an SFDX project in Visual Studio Code and run the SFDX: Create Aura Component command.

  5. Specify a name for the component.

  6. Update the component file with the attribute configuration (for example, force-app/main/default/aura/<project_name>/<component_name>.cmp).

    Make sure to use the controller filename that you used when creating the Apex controller, and provide the tag needed for importing activity tracking.

    1<aura:component implements="forceCommunity:availableForAllPageTypes" controller="RecsController" access="global">
    2
    3    <aura:attribute name="title" type="String" default="" required="true" />
    4    <aura:attribute name="recommender" type="String" default="RecentlyViewed" required="true" />
    5    <aura:attribute name="anchorValues" type="String" default="" required="false" />
    6    <aura:attribute name="uuid" type="String" default=""/>
    7    <aura:attribute name="loading" type="boolean" default="false"/>
    8    <aura:attribute name="showProducts" type="boolean" default="false"/>
    9    <aura:attribute name="products" type="List" default="[]" />
    10
    11    <!-- Tag needed to import Commerce Activity Tracking-->
    12    <commerce:activitiesApi aura:id="activitiesApi" />
    13
    14    <aura:handler name="init" value="{!this}" action="{!c.onLoadComponent}"/>
    15
    16    <div>
    17        <aura:if isTrue="{!v.loading}">LOADING...</aura:if>
    18
    19        <aura:if isTrue="{!v.showProducts}">
    20            <div class="title">{!v.title}</div>
    21            <div class="products">
    22                <aura:iteration items="{!v.products}" var="product">
    23                    <div class="product">
    24                        <img data-pid="{!product.id}" onclick="{!c.handleClickProduct}" src="{!product.defaultImage.url}"/>
    25                        <a class="name" data-pid="{!product.id}" onclick="{!c.handleClickProduct}">{!product.name}</a>
    26                    </div>
    27                </aura:iteration>
    28            </div>
    29        </aura:if>
    30    </div>
    31
    32</aura:component>
  7. Update the metadata in the component’s XML file (for example, force-app/main/default/aura/<project_name>/<component_name>.cmp-meta.xml).

    Make sure that the <apiVersion> tag references the latest API version.

    1<?xml version="1.0" encoding="UTF-8" ?>
    2<AuraDefinitionBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    3    <apiVersion>55.0</apiVersion>
    4    <description>Aura Recommendations Component</description>
    5    <isExposed>true</isExposed>
    6    <targets>
    7        <target>lightningCommunity__Page</target>
    8        <target>lightningCommunity__Default</target>
    9    </targets>
    10</AuraDefinitionBundle>
  8. Edit your component’s CSS file to add custom styling (for example, force-app/main/default/aura/<project_name>/<component_name>.css).

    This sample code includes modifications to the component’s image size and headings.

    1.THIS div.title {
    2  text-align: center;
    3  font-weight: 800;
    4  font-size: 2em;
    5}
    6
    7.THIS .products {
    8  height: 400px;
    9  text-align: center;
    10}
    11.THIS .products .product {
    12  width: 25%;
    13  height: 450px;
    14  padding: 10px;
    15  display: inline-block;
    16}
    17.THIS .products .product img {
    18  cursor: pointer;
    19  max-height: 300px;
    20  width: auto;
    21}
    22.THIS .products .product .name {
    23  display: block;
    24}
  9. Edit your component’s Controller.js file, making sure to modify the storeName value for the current store (for example, force-app/main/default/aura/<project_name>/<component_name>Controller.js).

    1({
    2  onLoadComponent: function (cmp, event, helper) {
    3    let pageProductId = helper.getProductDetailProductId();
    4    if (pageProductId) {
    5      // Component is on a product detail page; show Similar Products recommender
    6      cmp.set("v.title", "Similar Products");
    7      cmp.set("v.recommender", "SimilarProducts");
    8      cmp.set("v.anchorValues", pageProductId);
    9    } else {
    10      // Show Recently Viewed recommender
    11      cmp.set("v.title", "Recently Viewed");
    12      cmp.set("v.recommender", "RecentlyViewed");
    13      cmp.set("v.anchorValues", "");
    14    }
    15    helper.loadProductRecommendations(cmp, event, helper);
    16  },
    17
    18  // send a clickReco activity and navigate to the product detail page
    19  handleClickProduct: function (cmp, event, helper) {
    20    let productId = event.currentTarget.getAttribute("data-pid");
    21    let trackClickReco = cmp.find("activitiesApi").trackClickReco;
    22    let recName = helper.recommenderNames[cmp.get("v.recommender")];
    23    let uuid = cmp.get("v.uuid");
    24    let products = cmp.get("v.products");
    25    let product = products.filter((p) => p.id === productId)[0];
    26    let productToSend = {
    27      id: product.id,
    28      price: product.prices ? product.prices.listPrice : undefined,
    29    };
    30    trackClickReco(recName, uuid, productToSend);
    31
    32    // navigate to the product page
    33    let storeName = "AlpineB2B";
    34    let productName = product.name || "detail";
    35    let newHref = `/${storeName}/s/product/${productName}/${productId}`;
    36    window.location.href = newHref;
    37  },
    38});
  10. Edit your component’s Helper.js file, using this example for guidance (for example, force-app/main/default/aura/<project_name>/<component_name>Helper.js).

    1({
    2  loadProductRecommendations: function (cmp, event, helper) {
    3    try {
    4      cmp.set("v.loading", true);
    5      var action = cmp.get("c.getRecs");
    6      action.setParams({
    7        recommender: cmp.get("v.recommender"),
    8        anchorValues: cmp.get("v.anchorValues"),
    9        cookie: document.cookie,
    10      });
    11      // Create a callback that is executed after
    12      // the server-side action returns
    13      action.setCallback(this, function (response) {
    14        var state = response.getState();
    15        if (state === "SUCCESS") {
    16          try {
    17            let data = JSON.parse(response.getReturnValue());
    18            let products = data.productPage.products;
    19            // Keep it simple, only show 4 products
    20            cmp.set("v.products", products.slice(0, 4));
    21            cmp.set("v.uuid", data.uuid);
    22            cmp.set("v.loading", false);
    23            let showProducts = products.length > 0;
    24            cmp.set("v.showProducts", showProducts);
    25            // Only send the viewReco activity when we display the product
    26            // recommendations
    27            if (showProducts) {
    28              helper.sendViewRecoActivity(cmp, helper);
    29            }
    30          } catch (err) {
    31            console.error("Error fetching recommendations", err);
    32            cmp.set("v.loading", false);
    33          }
    34        }
    35      });
    36      $A.enqueueAction(action);
    37    } catch (error) {
    38      console.error("Failed to load recommendations: ", error);
    39      cmp.set("v.loading", false);
    40    }
    41  },
    42  // The recommender names we pass into the Connect API are in a different format
    43  // than the recommender names we pass into the activities api.
    44  recommenderNames: {
    45    RecentlyViewed: "recently-viewed",
    46    SimilarProducts: "similar-products",
    47    MostViewedByCategory: "most-viewed-by-category",
    48    TopSelling: "top-selling",
    49    Upsell: "upsell",
    50  },
    51
    52  formatPrice: function (price, curr) {
    53    return new Intl.NumberFormat("en-US", { style: "currency", currency: curr }).format(price);
    54  },
    55
    56  sendViewRecoActivity: function (cmp, helper) {
    57    let trackViewReco = cmp.find("activitiesApi").trackViewReco;
    58    let recName = helper.recommenderNames[cmp.get("v.recommender")];
    59    let products = cmp.get("v.products").map((p) => ({ id: p.id }));
    60    let uuid = cmp.get("v.uuid");
    61    trackViewReco(recName, uuid, products);
    62  },
    63
    64  getProductDetailProductId: function () {
    65    let pageProductIdMatch = window.location.href.match(new RegExp("01t[a-zA-Z0-9]{15}"));
    66    let pid = pageProductIdMatch ? pageProductIdMatch[0] : null;
    67    return pid;
    68  },
    69});
  11. After you create the custom Einstein Recommendations component, deploy it from Visual Studio Code and place it in your store with Experience Builder.

  12. Before publishing your site from Experience Builder, click Preview to see how the custom component looks in a desktop browser window and on a mobile device.

See Also