For Salesforce retailers, HubBox provides a cartridge that can be installed on your site to provide the pickup option at checkout. HubBox supports SFRA versions of Salesforce but older Site Genesis sites can also be supported upon request.
The first step for setting up HubBox software on your SFRA site is to upload the HubBox cartridge. Upload the cartridge in the same way you would normally build and upload a cartridge. HubBox recommends using npm run uploadCartridge
.
Download SFRA Cartridge
You should add the HubBox Cartridge as far forward in the cartridge path as possible. HubBox should be placed ahead of the main storefront cartridge.
Your cartridge path once modified should look something like this: int_hubbox_sfra:app_storefront_base:link_xxxx:plugin_yyyy:plugin_apple_pay:plugin_facebook:plugin_pinterest_commerce:plugin_web_payments:bc_content:core
Next, you will need to upload the metadata. Inside the SFRA cartridge, all required metadata can be found in the cartridge/metadata
folder.
HubBox.CustomObject.xml
and HubBox.SitePreferences.xml
. The HubBox.CustomObject.xml
is the authentication token data holder for HubBox API and HubBox.SitePreferences.xml
contains all HubBox related site preferencesHubBox.Shipment.xml
. This object is an extension of the system shipping object - it extends it with 4 new properties that hold all HubBox related informationHubBox.Services.xml
Once you have imported the metadata for the HubBox Cartridge you will need to set the Site Preference for HubBox.
HubBox.SitePreference.xml
)Key | Friendly | Description | Provided |
---|---|---|---|
HubBoxUsername |
API Username | Username used to authenticate with the HubBox API | Provided by HubBox |
HubBoxPassword |
API Key | API key used for authentication with the HubBox API | Provided by HubBox |
HubBoxBaseUrl |
Front-end Base URL | Environment used for gathering locations and processing orders (Production should only be used for your live environment, in all other cases Sandbox should be used) | N/A |
HubBoxMapUrl |
Front-end Map URL | Environment used for loading the Single Widget’s Map (Production should only be used for your live environment, in all other cases Sandbox should be used) | N/A |
HubBoxConfigId |
Retailer Config Id | Unique Id for you map configuration | Provided by HubBox |
HubBoxServiceEmail |
Service Email | This is the email that will be used to send HubBox information on issues with processing orders to help us investigate. | Provided by HubBox |
HubBoxNetwork |
HubBox Network | The Network that you integration will connect to | N/A |
The HubBox Cartridge will override the checkout/shipping/shipmentCard.isml
template in order to include the hubbox/launch.isml
template. The hubbox/launch.isml
template is the launch experience for Local Pickup and can be customized within this template.
If using the HubBox Components, these can be styled via CSS using the parts API. More information and CSS examples can be found in the HubBox Components documentation.
The HubBox Widget is the UI which presents customers with available pickup locations via a map and pin interface. The Widget sits inside a responsive iframe which can either be embedded on the checkout page or displayed as a modal. More information and configuration examples can be found in the Widget Configuration documentation.
Retailers with diverse product catalogs may be required to exclude certain products from the pickup service. In this instance, you will need to hide the pickup option for baskets that contain ineligible items. For example, if the customer has an item in their basket that is too large for a pickup location, you will need to ensure that they cannot see the pickup option at checkout.
If you use multiple carriers or wish to offer pickup at a different price/service level, you will need to show and hide particular shipping rates depending on whether the customer has chosen home delivery or pickup.
In order to manage and filter the rates that are displayed to a customer when pickup is selected, HubBox can recommend two different options:
In implementing via the software-defined method, you will be first need to modify the file: script/checkout/shippingHeplers
. This file contains the following method: getApplicableShippingMethods
. This method takes a shipment and the address then collects the rates that are available for the user to select.
Inside the HubBox cartridge, we provide an example of how you can override it and filter methods and detect the type of Pickup Point selected.
Reference the int_hubbox_sfra/cartridge/script/checkout/shippingHeplers.js
file.
superShippingHelpers.getApplicableShippingMethods = function (shipment, address) {
if (!shipment) return null;
var shipmentShippingModel = ShippingMgr.getShipmentShippingModel(shipment);
var shippingMethods;
if (address) {
shippingMethods = shipmentShippingModel.getApplicableShippingMethods(address);
} else {
shippingMethods = shipmentShippingModel.getApplicableShippingMethods();
}
// Filter out whatever the method associated with in store pickup
var filteredMethods = [];
collections.forEach(shippingMethods, function (shippingMethod) {
if (HubBoxHelper.isShipmentClickAndCollect(shipment)) {
/**
* If the shipment is going to a HubBox UK collect point and the ID is one of the methods that's appropriate for
* the HubBox UK Network add it into the collected methods
*/
if (HubBoxHelper.isShipmentHubBox(shipment) && (shippingMethod.id == 241 || shippingMethod.id == 212 || shippingMethod == 45)) {
filteredMethods.push(new ShippingMethodModel(shippingMethod, shipment));
}
if (HubBoxHelper.isShipmentInstore(shippingMethod) && (shippingMethod.id == 34 || shippingMethod.id == 99)) {
filteredMethods.push(new ShippingMethodModel(shippingMethod, shipment));
}
} else {
if (shippingMethod.id == 12 || shippingMethod.id == 62) {
filteredMethods.push(new ShippingMethodModel(shippingMethod, shipment));
}
}
if (!shippingMethod.custom.storePickupEnabled) {
filteredMethods.push(new ShippingMethodModel(shippingMethod, shipment));
}
});
return filteredMethods;
};
Extend the Shipping Method System Object so that there are per network flags for including the method. The following metadata as a group of 3 checkboxes onto each shipping method within Business Manager. This will allow Business Manager users to select 1 of the 3 delivery types per shipping rate so the appropriate rates can be displayed to the customer in the checkout.
<?xml version="1.0" encoding="UTF-8"?>
<metadata xmlns="http://www.demandware.com/xml/impex/metadata/2006-10-31">
<type-extension type-id="ShippingMethod">
<custom-attribute-definitions>
<attribute-definition attribute-id="isHomeDeliveryMethod">
<display-name xml:lang="x-default">Is Home Delivery Method?</display-name>
<description xml:lang="x-default">Can be displayed to customer when using home/alternative Address delivery</description>
<type>bool</type>
<mandatory-flag>false</mandatory-flag>
<externally-managed-flag>false</externally-managed-flag>
</attribute-definition>
<attribute-definition attribute-id="isInstoreDeliveryMethod">
<display-name xml:lang="x-default">Is Instore Delivery Method?</display-name>
<description xml:lang="x-default">Can be displayed to customer when customer is collecting order from store</description>
<type>bool</type>
<mandatory-flag>false</mandatory-flag>
<externally-managed-flag>false</externally-managed-flag>
</attribute-definition>
<attribute-definition attribute-id="isHubBoxDeliveryMethod">
<display-name xml:lang="x-default">Is HubBox Delivery Method?</display-name>
<description xml:lang="x-default">Can be displayed to customer when customer has selected pickup option</description>
<type>bool</type>
<mandatory-flag>false</mandatory-flag>
<externally-managed-flag>false</externally-managed-flag>
</attribute-definition>
</custom-attribute-definitions>
<group-definitions>
<attribute-group group-id="hubbox">
<display-name xml:lang="x-default">Click And Collect Filtering</display-name>
<description xml:lang="x-default">Click and collect method filtering information</description>
<attribute attribute-id="isHomeDeliveryMethod"/>
<attribute attribute-id="isInstoreDeliveryMethod"/>
<attribute attribute-id="isHubBoxDeliveryMethod"/>
</attribute-group>
</group-definitions>
</type-extension>
</metadata>
Within shippingHeplers.js
we can now implement logic that reads the new custom attributes for each method and allows us to filter based on this rather than having hardcoded IDs.
superShippingHelpers.getApplicableShippingMethods = function (shipment, address) {
if (!shipment) return null;
var shipmentShippingModel = ShippingMgr.getShipmentShippingModel(shipment);
var shippingMethods;
if (address) {
shippingMethods = shipmentShippingModel.getApplicableShippingMethods(address);
} else {
shippingMethods = shipmentShippingModel.getApplicableShippingMethods();
}
// Filter out whatever the method associated with in store pickup
var filteredMethods = [];
collections.forEach(shippingMethods, function (shippingMethod) {
if (HubBoxHelper.isShipmentClickAndCollect(shipment)) {
if (HubBoxHelper.isShipmentHubBox(shipment) && shippingMethod.custom.isHubBoxDeliveryMethod) {
filteredMethods.push(new ShippingMethodModel(shippingMethod, shipment));
}
if (HubBoxHelper.isShipmentInstore(shippingMethod) && shippingMethod.custom.isInstoreDeliveryMethod) {
filteredMethods.push(new ShippingMethodModel(shippingMethod, shipment));
}
} else {
if (shippingMethod.custom.isHomeDeliveryMethod) {
filteredMethods.push(new ShippingMethodModel(shippingMethod, shipment));
}
}
if (!shippingMethod.custom.storePickupEnabled) {
filteredMethods.push(new ShippingMethodModel(shippingMethod, shipment));
}
});
return filteredMethods;
};
HubBox overrides the default/checkout/shipping/shipmentCard.isml template in order to include 2 HubBox templates to run the pickup UI.
shipmentCard.isml
in the storefront cartridgelaunch.isml
<fieldset class="hubbox-toggles-block">
<isinclude template="hubbox/launch" />
</fieldset>
HubBox appends to the CheckoutShippingServices
controller in order to pass information from the form input down to the cartridge backend.
HubBox has its own controller for toggling pickup CheckoutShippingServices-HubBox
. The controller is called when HubBox is set or cleared. The two main callbacks are hubBoxPointSelectedCallback
and hubBoxPointUnSelectedCallback
.
A store sync can be used to synchronize store data between the Salesforce platform and HubBox. You will need to provide HubBox with an externally accessible URL for the store sync.
This is only required if you are passing your own store data from Salesforce to HubBox so that your store locations are presented within the HubBox widget.
The HubBox cartridge provides a controller and service that loads locations out of the Salesforce Backend and renders them into JSON on the controller. A service within HubBox then automatically scrapes the location data and keeps everything in sync.
When you have set up the cartridge on your development or sandbox instance, you will need to provide HubBox with an externally accessible URL for the store sync controller. For example: https://USER_NAME:PASSWORD@YOUR_SANDBOX_OR_DEV_URL.demandware.net/on/demandware.store/YOUR_SITE_NAME/en/HubBoxStoreSync-Stores
Please note: if using a custom domain the SSL Certificate for that name must be valid and externally signed by a common Certificate Authority
The default behavior of the cartridge is to assume all stores loaded into Salesforce Commerce Cloud are available as pickup locations. If you have stores that are not available for pickup (for example, concessions within department stores, certain malls or shopping centers with delivery restrictions), you will need to exclude these from the HubBox sync.
To do this there are two methods described below. Using isClickAndCollectEnabled
property is recommended.
"use strict";
var HubBoxStoreHelper = require("~/cartridge/scripts/helpers/HubBoxStoreHelper");
var server = require("server");
server.get("Stores", function (req, res, next) {
var storeList = HubBoxStoreHelper.getPickupInStoreEnabledStores();
storeList.map(function (store) {
return {
id: store.ID,
name: store.name,
isStoreLocatorEnabled: store.isStoreLocatorEnabled(),
isPosEnabled: store.isPosEnabled(),
isClickAndCollectEnabled: store.custom.isClickAndCollectEnabled,
hours: store.getStoreHours() != null ? store.getStoreHours().getMarkup() : "",
phone: store.phone,
address: {
street1: store.address1,
street2: store.address2,
city: store.city,
countryCode: store.countryCode.getValue(),
latitude: store.latitude,
longitude: store.longitude,
postcode: store.getPostalCode()
}
};
});
res.setStatusCode(200);
res.setContentType("application/json");
res.json({
stores: storeList
});
return next();
});
module.exports = server.exports();
Once the cartridge is installed and configured, you will see the HubBox integration in the checkout. Upon customer selecting a pickup location, the address of the chosen location will populate the shipping address of the order.
After the customer has placed the order, the order XML will contain custom attributes with data relating to the customer's chosen pickup location.
<custom-attribute attribute-id="collectPointId">CP12345678</custom-attribute>
<custom-attribute attribute-id="collectPointName">ACME CONVENIENCE STORE</custom-attribute>
<custom-attribute attribute-id="collectPointNetwork">networkname</custom-attribute>
<custom-attribute attribute-id="collectPointType">public</custom-attribute>
Full order XML examples:
UPS DPD<?xml version="1.0" encoding="UTF-8"?>
<orders xmlns="http://www.demandware.com/xml/impex/order/2006-10-31">
<order order-no="00000301">
<order-date>2021-09-17T08:54:19.444Z</order-date>
<created-by>storefront</created-by>
<original-order-no>00000301</original-order-no>
<currency>USD</currency>
<customer-locale>en_US</customer-locale>
<taxation>net</taxation>
<invoice-no>00001501</invoice-no>
<customer>
<customer-name>Jane Doe</customer-name>
<customer-email>janedoe@test.com</customer-email>
<billing-address>
<first-name>Jane</first-name>
<last-name>Doe</last-name>
<address1>123 Main Street</address1>
<city>ATLANTA</city>
<postal-code>30303</postal-code>
<state-code>OTHER</state-code>
<country-code>US</country-code>
<phone>07223223123</phone>
</billing-address>
</customer>
<status>
<order-status>OPEN</order-status>
<shipping-status>NOT_SHIPPED</shipping-status>
<confirmation-status>CONFIRMED</confirmation-status>
<payment-status>NOT_PAID</payment-status>
</status>
<current-order-no>00000301</current-order-no>
<product-lineitems>
<product-lineitem>
<net-price>38.00</net-price>
<tax>1.90</tax>
<gross-price>39.90</gross-price>
<base-price>38.00</base-price>
<lineitem-text>Gold Stretch Bangle</lineitem-text>
<tax-basis>38.00</tax-basis>
<position>1</position>
<product-id>013742002928M</product-id>
<product-name>Gold Stretch Bangle</product-name>
<quantity unit="">1.0</quantity>
<tax-rate>0.05</tax-rate>
<shipment-id>00001501</shipment-id>
<shipping-lineitem>
<net-price>10.00</net-price>
<tax>0.50</tax>
<gross-price>10.50</gross-price>
<base-price>10.00</base-price>
<lineitem-text>Item Shipping Cost (Surcharge)</lineitem-text>
<tax-basis>10.00</tax-basis>
<quantity unit="">1.0</quantity>
<tax-rate>0.05</tax-rate>
<type>surcharge</type>
</shipping-lineitem>
<gift>false</gift>
</product-lineitem>
</product-lineitems>
<shipping-lineitems>
<shipping-lineitem>
<net-price>5.99</net-price>
<tax>0.30</tax>
<gross-price>6.29</gross-price>
<base-price>5.99</base-price>
<lineitem-text>Shipping</lineitem-text>
<tax-basis>5.99</tax-basis>
<item-id>STANDARD_SHIPPING</item-id>
<shipment-id>00001501</shipment-id>
<tax-rate>0.05</tax-rate>
</shipping-lineitem>
</shipping-lineitems>
<shipments>
<shipment shipment-id="00000001">
<status>
<shipping-status>NOT_SHIPPED</shipping-status>
</status>
<shipping-method>002</shipping-method>
<shipping-address>
<first-name>Jane</first-name>
<last-name>Doe</last-name>
<address1>CVS STORE # 10043</address1>
<city>ATLANTA</city>
<postal-code>30303</postal-code>
<state-code>GA</state-code>
<country-code>US</country-code>
<phone>9876543211</phone>
</shipping-address>
<gift>false</gift>
<totals>
<merchandize-total>
<net-price>148.00</net-price>
<tax>7.40</tax>
<gross-price>155.40</gross-price>
</merchandize-total>
<adjusted-merchandize-total>
<net-price>148.00</net-price>
<tax>7.40</tax>
<gross-price>155.40</gross-price>
</adjusted-merchandize-total>
<shipping-total>
<net-price>11.99</net-price>
<tax>0.60</tax>
<gross-price>12.59</gross-price>
</shipping-total>
<adjusted-shipping-total>
<net-price>11.99</net-price>
<tax>0.60</tax>
<gross-price>12.59</gross-price>
</adjusted-shipping-total>
<shipment-total>
<net-price>159.99</net-price>
<tax>8.00</tax>
<gross-price>167.99</gross-price>
</shipment-total>
</totals>
<custom-attributes>
<custom-attribute attribute-id="collectPointId">U99960535</custom-attribute>
<custom-attribute attribute-id="collectPointName">CVS STORE # 10043</custom-attribute>
<custom-attribute attribute-id="collectPointNetwork">ups</custom-attribute>
<custom-attribute attribute-id="collectPointType">public</custom-attribute>
</custom-attributes>
</shipment>
</shipments>
<totals>
<merchandize-total>
<net-price>38.00</net-price>
<tax>1.90</tax>
<gross-price>39.90</gross-price>
</merchandize-total>
<adjusted-merchandize-total>
<net-price>38.00</net-price>
<tax>1.90</tax>
<gross-price>39.90</gross-price>
</adjusted-merchandize-total>
<shipping-total>
<net-price>15.99</net-price>
<tax>0.80</tax>
<gross-price>16.79</gross-price>
</shipping-total>
<adjusted-shipping-total>
<net-price>15.99</net-price>
<tax>0.80</tax>
<gross-price>16.79</gross-price>
</adjusted-shipping-total>
<order-total>
<net-price>53.99</net-price>
<tax>2.70</tax>
<gross-price>56.69</gross-price>
</order-total>
</totals>
<payments>
<payment>
<credit-card>
<card-type>VISA</card-type>
<card-number>XXXX-XXXX-XXXX-1111</card-number>
<card-holder>Jane Doe</card-holder>
<card-token>dscvse6xdkj</card-token>
<expiration-month>2</expiration-month>
<expiration-year>2025</expiration-year>
</credit-card>
<amount>56.69</amount>
<processor-id>BASIC_CREDIT</processor-id>
<transaction-id>00000301</transaction-id>
</payment>
</payments>
<remoteHost>86.147.199.105</remoteHost>
</order>
</orders>
<?xml version="1.0" encoding="UTF-8"?>
<orders xmlns="http://www.demandware.com/xml/impex/order/2006-10-31">
<order order-no="00000301">
<order-date>2021-09-17T08:54:19.444Z</order-date>
<created-by>storefront</created-by>
<original-order-no>00000301</original-order-no>
<currency>GBP</currency>
<customer-locale>en_GB</customer-locale>
<taxation>net</taxation>
<invoice-no>00001501</invoice-no>
<customer>
<customer-name>Jane Doe</customer-name>
<customer-email>janedoe@test.com</customer-email>
<billing-address>
<first-name>Jane</first-name>
<last-name>Doe</last-name>
<address1>123 Test Street</address1>
<city>London</city>
<postal-code>SE1 9RA</postal-code>
<state-code>OTHER</state-code>
<country-code>GB</country-code>
<phone>07223223123</phone>
</billing-address>
</customer>
<status>
<order-status>OPEN</order-status>
<shipping-status>NOT_SHIPPED</shipping-status>
<confirmation-status>CONFIRMED</confirmation-status>
<payment-status>NOT_PAID</payment-status>
</status>
<current-order-no>00000301</current-order-no>
<product-lineitems>
<product-lineitem>
<net-price>38.00</net-price>
<tax>1.90</tax>
<gross-price>39.90</gross-price>
<base-price>38.00</base-price>
<lineitem-text>Gold Stretch Bangle</lineitem-text>
<tax-basis>38.00</tax-basis>
<position>1</position>
<product-id>013742002928M</product-id>
<product-name>Gold Stretch Bangle</product-name>
<quantity unit="">1.0</quantity>
<tax-rate>0.05</tax-rate>
<shipment-id>00001501</shipment-id>
<shipping-lineitem>
<net-price>10.00</net-price>
<tax>0.50</tax>
<gross-price>10.50</gross-price>
<base-price>10.00</base-price>
<lineitem-text>Item Shipping Cost (Surcharge)</lineitem-text>
<tax-basis>10.00</tax-basis>
<quantity unit="">1.0</quantity>
<tax-rate>0.05</tax-rate>
<type>surcharge</type>
</shipping-lineitem>
<gift>false</gift>
</product-lineitem>
</product-lineitems>
<shipping-lineitems>
<shipping-lineitem>
<net-price>5.99</net-price>
<tax>0.30</tax>
<gross-price>6.29</gross-price>
<base-price>5.99</base-price>
<lineitem-text>Shipping</lineitem-text>
<tax-basis>5.99</tax-basis>
<item-id>STANDARD_SHIPPING</item-id>
<shipment-id>00001501</shipment-id>
<tax-rate>0.05</tax-rate>
</shipping-lineitem>
</shipping-lineitems>
<shipments>
<shipment shipment-id="00001501">
<status>
<shipping-status>NOT_SHIPPED</shipping-status>
</status>
<shipping-method>001</shipping-method>
<shipping-address>
<first-name>Jane</first-name>
<last-name>Doe</last-name>
<address1>Snappy Snaps</address1>
<city>London S2SGB14496</city>
<postal-code>WC2E 9AX</postal-code>
<state-code>OTHER</state-code>
<country-code>GB</country-code>
<phone>07223223123</phone>
</shipping-address>
<gift>false</gift>
<totals>
<merchandize-total>
<net-price>38.00</net-price>
<tax>1.90</tax>
<gross-price>39.90</gross-price>
</merchandize-total>
<adjusted-merchandize-total>
<net-price>38.00</net-price>
<tax>1.90</tax>
<gross-price>39.90</gross-price>
</adjusted-merchandize-total>
<shipping-total>
<net-price>15.99</net-price>
<tax>0.80</tax>
<gross-price>16.79</gross-price>
</shipping-total>
<adjusted-shipping-total>
<net-price>15.99</net-price>
<tax>0.80</tax>
<gross-price>16.79</gross-price>
</adjusted-shipping-total>
<shipment-total>
<net-price>53.99</net-price>
<tax>2.70</tax>
<gross-price>56.69</gross-price>
</shipment-total>
</totals>
<custom-attributes>
<custom-attribute attribute-id="collectPointId">GB14496</custom-attribute>
<custom-attribute attribute-id="collectPointName">Snappy Snaps</custom-attribute>
<custom-attribute attribute-id="collectPointNetwork">dpd</custom-attribute>
<custom-attribute attribute-id="collectPointType">public</custom-attribute>
</custom-attributes>
</shipment>
</shipments>
<totals>
<merchandize-total>
<net-price>38.00</net-price>
<tax>1.90</tax>
<gross-price>39.90</gross-price>
</merchandize-total>
<adjusted-merchandize-total>
<net-price>38.00</net-price>
<tax>1.90</tax>
<gross-price>39.90</gross-price>
</adjusted-merchandize-total>
<shipping-total>
<net-price>15.99</net-price>
<tax>0.80</tax>
<gross-price>16.79</gross-price>
</shipping-total>
<adjusted-shipping-total>
<net-price>15.99</net-price>
<tax>0.80</tax>
<gross-price>16.79</gross-price>
</adjusted-shipping-total>
<order-total>
<net-price>53.99</net-price>
<tax>2.70</tax>
<gross-price>56.69</gross-price>
</order-total>
</totals>
<payments>
<payment>
<credit-card>
<card-type>VISA</card-type>
<card-number>XXXX-XXXX-XXXX-1111</card-number>
<card-holder>Jane Doe</card-holder>
<card-token>dscvse6xdkj</card-token>
<expiration-month>2</expiration-month>
<expiration-year>2025</expiration-year>
</credit-card>
<amount>56.69</amount>
<processor-id>BASIC_CREDIT</processor-id>
<transaction-id>00000301</transaction-id>
</payment>
</payments>
<remoteHost>86.147.199.105</remoteHost>
</order>
</orders>
HubBox can also offer additional features upon request:
These features require HubBox customization so please contact the HubBox Integrations team if you would like to find out more at clientsupport@hub-box.com.