HubBox Software Development Kit (SDK) Integration

Introduction

This guide provides an overview of integrating HubBox pickup point functionality into custom-built ecommerce websites, headless commerce setups, or heavily customized platform checkouts using the HubBox Software Development Kit (SDK).

The SDK offers the highest degree of flexibility and control over the user experience and integration logic, allowing you to seamlessly embed HubBox features within your unique checkout flow. This approach requires frontend development to interact with HubBox components and backend development to handle data persistence, filtering logic, and order processing.

Prerequisites

SDK vs. Tag: If you need a quicker, lower-code frontend solution with less customization required, consider the HubBox Tag Script. The SDK provides maximum control but involves more development effort.

Useful Reference Guides

Core Concepts of SDK Integration

The SDK integration typically involves several key pieces working together:

  1. Launch Experience UI (Retailer Built): You design and build the initial UI elements within your checkout that allow the customer to choose between Home Delivery and Local Pickup (e.g., buttons, toggles, radio options, or integrating pickup as a shipping method choice). Alternatively, you can also implement HubBox's web components for a toggle launch experience.

  2. HubBox Map Widget (SingleWidgetManager): This core piece, managed by the HubBox @hubbox/single-widget-manager JavaScript library, loads and displays the interactive map and list of pickup locations within an iframe. It can be presented either embedded directly on the page or within a modal pop-up.

  3. HubBox Web Components (@hubbox/web-components): Optional but recommended helper components (e.g., <hubbox-modal>, <hubbox-pickup-confirmation-modern>) that simplify implementing the modal container and the post-selection confirmation display.

  4. Event-Driven Communication: Your frontend JavaScript interacts with the SingleWidgetManager using a publish/subscribe event system (events.subscribe, events.emit) to trigger searches, open the widget, and receive data about the user's selection.

  5. Configuration: Key settings like your configId (defining networks, styling), locale, and environment (sandbox/production) are passed during the initialization of the SingleWidgetManager.

  6. Backend Interaction (Retailer Built): Your frontend JavaScript needs to communicate the selected pickup point data (address, ID, network) to your backend server (e.g., via AJAX/Fetch). Your backend logic must then:

    • Update the user's session or cart/checkout object with the chosen location data and pickup flag.
    • Potentially filter available shipping rates based on the pickup selection.
    • Persist the HubBox location details when the order is placed.
  7. Order Data Sharing (Recommended): Sending details of completed pickup orders back to HubBox via Order Processing API or Tracking Pixel allows you to leverage HubBox data dashboards.

Critical Integration Path (Steps)

Here's a typical workflow for implementing the SDK:

Step 1: Installation

Install the necessary HubBox packages into your frontend project using NPM or Yarn:

npm install @hubbox/single-widget-manager @hubbox/web-components
# or
yarn add @hubbox/single-widget-manager @hubbox/web-components

Alternatively, if you don't use NPM/Yarn, contact HubBox Integrations for CDN URLs or minified versions.

Remember to import the components if using TypeScript or certain module bundlers:

import { SingleWidgetManager } from '@hubbox/single-widget-manager';
import "@hubbox/web-components"; // Imports and registers custom elements like <hb-widget-modal>

Step 2: Design & Implement Launch Experience UI

Choose a Pattern: Decide how customers will opt into pickup:

Build the UI: Create the necessary HTML buttons, radio inputs, search fields, etc., within your checkout template. Add appropriate event listeners (e.g., click handlers) to these elements. Alternatively use <hubbox-local-pickup-toggles> and <hubbox-home-delivery-toggles> (or <hubbox-local-pickup-multi-button> and <hubbox-home-delivery-multi-button>) from the HubBox Web Components.

Pickup as a Shipping Method

This approach presents HubBox pickup options directly alongside your standard home delivery rates within the shipping method selection step of your checkout.

User Flow:

  1. Customer enters their shipping address.
  2. Your ecommerce platform calculates available shipping methods based on the address, cart contents, and your backend configuration. This list includes both your standard home delivery rates AND the specific pickup point rate(s) you've configured (e.g., "Standard Pickup").
  3. The checkout frontend displays this list of shipping methods, typically as radio buttons or selectable options.
  4. If the customer selects a shipping method designated as a HubBox pickup option:
    • Your frontend JavaScript detects this specific selection.
    • The HubBox SDK map Widget (usually modal) is triggered, allowing the customer to choose a specific location for that selected service.
  5. Once a location is confirmed via the SDK, its details are stored, and the customer proceeds to payment with the pickup rate and location selected.

Key Considerations:

Pickup presented as toggles

This user experience pattern presents customers with a clear, high-level choice between "Home Delivery" and "Pickup Points" early in the shipping step of your checkout, often before they interact with the main shipping address form fields.

User Flow:

  1. Customer arrives at the shipping stage of the checkout.
  2. They are immediately presented with distinct options, usually styled as clickable boxes, tabs, or large radio buttons (we'll call these "toggles"), labelled e.g., "Deliver to my Address" and "Collect from Pickup Point".
  3. If the customer selects "Pickup":
    • The UI visually highlights the pickup choice.
    • The standard home delivery address input fields are typically hidden or disabled (locked) via JavaScript.
    • The HubBox map widget (usually modal) is triggered, prompting the user to search for and select a specific pickup location.
    • Once a location is confirmed via the SDK, its details are saved, and appropriate (filtered) shipping rates for pickup are displayed.
  4. If the customer selects "Home Delivery":
    • The UI visually highlights the home delivery choice.
    • The standard home delivery address input fields are displayed and enabled.
    • Any previously selected pickup location data is cleared.
    • Standard home delivery shipping rates are displayed.

Key Considerations:

Step 3: Initialize SingleWidgetManager

Instantiate the manager in your frontend JavaScript. Choose one of the following modes:

Modal Mode (Recommended for most checkouts): The widget appears in a pop-up modal.

import { SingleWidgetManager } from '@hubbox/single-widget-manager';

const singleWidgetManager = new SingleWidgetManager({
    deferRender: true, // IMPORTANT: Tells manager not to render iframe immediately
    iframeUrl: SingleWidgetManager.iframeUrls.PRODUCTION, // Or SingleWidgetManager.iframeUrls.SANDBOX for testing
    // healthCheck: false, // Optional: Disable polling check if causing issues
    // isDebug: true, // Optional: Enable verbose logging during development
    iframeParams: {
    configId: "YOUR_HUBBOX_CONFIG_ID", // Provided by HubBox
    locale: "en-GB" // Match your store's locale (e.g., en-US, fr-FR)
    // Add other config params like 'translations' if needed
    }
});
*You'll also need to create/manage the `<hubbox-modal>` component (see Step 4) or use your own modal UI.*

Embedded Mode: The widget appears directly within a container on your page.

import { SingleWidgetManager } from '@hubbox/single-widget-manager';

// Ensure this div exists in your HTML: <div id="hubbox-widget-container"></div>
const widgetContainer = document.getElementById("hubbox-widget-container");

const singleWidgetManager = new SingleWidgetManager({
    container: widgetContainer, // REQUIRED: The DOM element to inject iframe into
    iframeUrl: SingleWidgetManager.iframeUrls.PRODUCTION, // Or SANDBOX
    // isDebug: true,
    iframeParams: {
    configId: "YOUR_HUBBOX_CONFIG_ID",
    locale: "en-GB"
    }
});

Step 4: Implement Widget Triggering

Connect your Launch Experience UI (from Step 2) to the SingleWidgetManager.

For Modal Mode:

  1. Add the modal component to your page (can be done on load or dynamically):

    <hubbox-modal></hubbox-modal>
    
    // Or create dynamically
    // const hubboxModal = document.createElement("hubbox-modal");
    // document.body.appendChild(hubboxModal); // Append somewhere appropriate
    
  2. Configure the modal and link the iframe (usually after manager initializes):

    const hubboxModal = document.querySelector("hubbox-modal");
    if (hubboxModal && singleWidgetManager.iframeElem) {
        hubboxModal.locale = "en-GB"; // Match config locale
        hubboxModal.open = false; // Start closed
        hubboxModal.appendChild(singleWidgetManager.iframeElem); // Place iframe inside modal
    }
    
  3. Add an event listener to your "Choose Pickup Location" button/link:

    const pickupButton = document.getElementById('choose-pickup-button');
    const hubboxModal = document.querySelector("hubbox-modal");
    const searchInput = document.getElementById('pickup-search-input'); // Optional search input
    
    pickupButton.addEventListener('click', () => {
        if (searchInput && searchInput.value) {
            // Optional: Pre-fill search if user entered postcode first or if you have user's location
            singleWidgetManager.events.emit(singleWidgetManager.topics.emit.MAP_SEARCH_QUERY, searchInput.value);
        }
        if (hubboxModal) {
            hubboxModal.open = true; // Open the modal
        }
    });
    

For Embedded Mode:

  1. Ensure the container (#hubbox-widget-container) is initially hidden (e.g., via CSS class).

  2. Add event listeners to your search input/button:

    const searchButton = document.getElementById("hubbox-search-button");
    const searchInput = document.getElementById("hubbox-search-input");
    const widgetContainer = document.getElementById("hubbox-widget-container");
    
    searchButton.addEventListener("click", function () {
        const searchQuery = searchInput.value;
        if (searchQuery && widgetContainer) {
            singleWidgetManager.events.emit(singleWidgetManager.topics.emit.MAP_SEARCH_QUERY, searchQuery);
            widgetContainer.style.display = 'block'; // Show the widget container
            // Or remove a hidden class: widgetContainer.classList.remove("hubbox-hidden");
        }
    });
    

Step 5: Handle Core Callbacks (events.subscribe)

Subscribe to events emitted by the SingleWidgetManager to react to user actions within the widget iframe.

// --- Essential Callback: Point Confirmed ---
const onCollectPointConfirmed = (messageAndTopic) => {
  const collectPointData = messageAndTopic.message; // Contains selected location details
  console.log("Pickup Point Confirmed:", collectPointData);

  // 1. Populate Frontend Address Fields (Example)
  document.querySelector('#shipping-address1').value = collectPointData.address.street1 || '';
  document.querySelector('#shipping-company').value = collectPointData.name || ''; // Use 'name' for company
  document.querySelector('#shipping-address2').value = collectPointData.address.street2 || ''; // Or use for carrier codes if needed
  document.querySelector('#shipping-city').value = collectPointData.address.city || '';
  document.querySelector('#shipping-postcode').value = collectPointData.address.postcode || '';
  document.querySelector('#shipping-state').value = collectPointData.address.region || ''; // Map 'region' to state/county
  // Trigger change events if your checkout requires it for validation/updates
  document.querySelector('#shipping-postcode').dispatchEvent(new Event('change', { bubbles: true }));

  // 2. Send Data to Your Backend (CRITICAL)
  const payload = {
    action: 'select_pickup',
    collectPointId: collectPointData.id,
    collectPointName: collectPointData.name,
    collectPointNetwork: collectPointData.network, // e.g., 'ups', 'dpd', 'dhl'
    collectPointType: collectPointData.type,     // e.g., 'public', 'private'
    address: collectPointData.address          // Full address object
  };
  fetch('/your-checkout-api/update-shipping', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', /* Add CSRF token etc. */ },
    body: JSON.stringify(payload)
  })
  .then(response => response.json())
  .then(data => {
      console.log('Backend updated:', data);
      // 3. Trigger Shipping Rate Recalculation (if needed by your platform)
      // yourPlatform.recalculateShippingRates();

      // 4. Show Confirmation UI (See Step 6)
      showPickupConfirmation(collectPointData);

      // 5. Close Modal (if using modal mode)
      const hubboxModal = document.querySelector("hb-widget-modal");
      if (hubboxModal) hubboxModal.open = false;

      // 6. Potentially move to next checkout step automatically
      // yourPlatform.goToNextStep();
  })
  .catch(error => console.error('Error sending pickup data to backend:', error));
};

// --- Essential Callback: Point De-Selected (User cancels/closes modal) ---
const onCollectPointUnselected = (messageAndTopic) => {
  console.log("Pickup Point Unselected");

  // 1. Clear Frontend Address Fields (or restore original address)
  // ... your logic to clear fields ...

  // 2. Send Deselection Event to Backend
  fetch('/your-checkout-api/update-shipping', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', /* CSRF etc. */ },
      body: JSON.stringify({ action: 'deselect_pickup' })
  })
  .then(() => {
      console.log('Backend updated: Pickup deselected');
      // 3. Trigger Shipping Rate Recalculation (show home delivery rates)
      // yourPlatform.recalculateShippingRates();

      // 4. Hide Confirmation UI
      hidePickupConfirmation();
  })
  .catch(error => console.error('Error sending deselection to backend:', error));
};

// --- Other Useful Callbacks ---
const onWidgetReady = (messageAndTopic) => {
  console.log("HubBox Widget Iframe Ready:", messageAndTopic.message);
  // Widget iframe content is loaded, safe to emit initial search etc.
};

const onBootFailed = (messageAndTopic) => {
  console.error("HubBox Widget Boot Failed:", messageAndTopic.message);
  // Handle errors - maybe hide pickup option, show error message
};

const onCollectPointSelected = (messageAndTopic) => {
    console.log("Point Selected (but not confirmed):", messageAndTopic.message);
    // Optional: Use for preliminary UI updates before confirmation
};

// Subscribe to the events
singleWidgetManager.events.subscribe(singleWidgetManager.topics.subscribe.COLLECT_POINT_CONFIRMED, onCollectPointConfirmed);
singleWidgetManager.events.subscribe(singleWidgetManager.topics.subscribe.COLLECT_POINT_UNSELECTED, onCollectPointUnselected);
singleWidgetManager.events.subscribe(singleWidgetManager.topics.subscribe.WIDGET_READY, onWidgetReady);
singleWidgetManager.events.subscribe(singleWidgetManager.topics.subscribe.BOOT_FAILED, onBootFailed);
singleWidgetManager.events.subscribe(singleWidgetManager.topics.subscribe.COLLECT_POINT_SELECTED, onCollectPointSelected);

Key: Implement robust logic within COLLECT_POINT_CONFIRMED and COLLECT_POINT_UNSELECTED to update frontend fields, communicate with your backend, and trigger necessary UI changes/recalculations.

For detailed event schemas and more topics, see the Manager.js reference documentation.

Step 6: Implement Confirmation UI

After a user confirms a pickup point, display a clear confirmation message.

Option A (HubBox Component): Use the <hubbox-pickup-confirmation-modern> component.

  1. Add to your HTML (initially hidden):

    <hb-core-pickup-confirmation id="hubbox-confirmation" class="hubbox-hidden" locale="en-GB"></hb-core-pickup-confirmation>
    
  2. In your onCollectPointConfirmed callback, populate and show it:

    function showPickupConfirmation(collectPointData) {
        const confirmationElement = document.getElementById('hubbox-confirmation');
        if (confirmationElement) {
            confirmationElement.address = collectPointData.address;
            confirmationElement.openingTimes = collectPointData.openingTimes; // Pass opening times data if available
            confirmationElement.name = collectPointData.name;
            // Optionally set map coordinates if you want component to show map
            // confirmationElement.coords = { lat: collectPointData.address.latitude, lng: collectPointData.address.longitude };
            confirmationElement.classList.remove('hubbox-hidden');
            confirmationElement.style.display = 'block'; // Ensure it's visible
        }
    }
    function hidePickupConfirmation() {
            const confirmationElement = document.getElementById('hubbox-confirmation');
            if (confirmationElement) {
                confirmationElement.style.display = 'none';
                confirmationElement.classList.add('hubbox-hidden');
            }
    }
    

(Remember to call hidePickupConfirmation() in your onCollectPointUnselected callback).

Option B (Custom UI): Build your own confirmation display using the collectPointData received in the callback. Ensure you clearly show the selected location name and address.

Step 7: Implement Backend Logic

Your server-side checkout logic needs to:

  1. Receive Data: Have an API endpoint (e.g., /your-checkout-api/update-shipping) that accepts the pickup selection data (ID, name, network, address, pickup flag) sent from the frontend JS.
  2. Update Session/Cart: Store this data in the user's session or associate it with their cart/checkout object on the server. Mark the shipment as a pickup order.
  3. Persist on Order: When the order is placed, save the relevant HubBox details (at minimum the collectPointId, collectPointNetwork, collectPointName) with the order data in your database. This is needed for fulfillment and manifesting.
  4. Filtering Logic (If needed): Implement logic to filter shipping rates or validate product eligibility based on the pickup flag stored in the session/cart (see Advanced Features below).

Methods for Sending Selected Pickup Data to Your Backend

Once the onCollectPointConfirmed callback fires in the frontend JavaScript, you must send the selected location's details (like collectPointData.id, collectPointData.network, collectPointData.name, and the collectPointData.address object) to your server-side ecommerce application. This allows your backend to update the user's session/cart, apply correct shipping logic, and save the data when the order is placed. Here are common methods:

  1. Hidden Input Fields (Traditional Forms):

How: Add hidden <input> fields to your main checkout <form>. In the onCollectPointConfirmed callback, use JavaScript to set the value of these hidden fields with the relevant HubBox data (e.g., location ID, network). When the user submits the form (e.g., proceeds to payment), this data is sent along with other form data.

Pros: Relatively simple for traditional multi-page checkouts relying on standard form submissions. Easy to debug by inspecting form data.

Cons: Not suitable for Single Page Application (SPA) checkouts or headless setups where the page doesn't fully reload and data is typically sent via APIs.

Example HTML:

<input type="hidden" name="shipping[hubbox_location_id]" id="hubbox-location-id">
<input type="hidden" name="shipping[hubbox_network]" id="hubbox-network">
<input type="hidden" name="shipping[hubbox_location_name]" id="hubbox-location-name">

Example JS (within onCollectPointConfirmed / sendPickupDataToBackend):

document.getElementById('hubbox-location-id').value = collectPointData.id || '';
document.getElementById('hubbox-network').value = collectPointData.network || '';
document.getElementById('hubbox-location-name').value = collectPointData.name || '';

Deselection (onCollectPointUnselected): You would typically clear the values of these hidden fields.

  1. Custom REST API Call (Recommended for SPAs/Headless/Modern Checkouts):

How: Create a dedicated API endpoint on your backend server (e.g., /api/checkout/set-hubbox-selection). In the onCollectPointConfirmed callback, use JavaScript's Workspace or XMLHttpRequest (AJAX) to send a POST request containing the collectPointData (usually as JSON) to this endpoint. Your backend endpoint receives the data, updates the server-side session/cart object, and typically returns a success/failure response.

Pros: Standard approach for modern web applications (SPAs, headless). Allows for more complex data exchange and immediate feedback/validation from the backend. Doesn't rely on hidden form fields.

Cons: Requires backend API development.

Example JS (within sendPickupDataToBackend):

function sendPickupDataToBackend(collectPointData) {
    const payload = {
        action: 'select_pickup',
        collectPointId: collectPointData.id,
        collectPointName: collectPointData.name,
        collectPointNetwork: collectPointData.network,
        collectPointType: collectPointData.type,
        address: collectPointData.address
    };
    fetch('/api/checkout/set-hubbox-selection', { // Your backend endpoint
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            // Include necessary headers like CSRF tokens, Auth tokens etc.
            // 'X-CSRF-Token': getCsrfToken()
        },
        body: JSON.stringify(payload)
    })
    .then(response => {
        if (!response.ok) { throw new Error('Network response was not ok'); }
        return response.json();
    })
    .then(data => {
        console.log('Backend successfully updated for pickup:', data);
        // Trigger UI updates based on response if needed
        // e.g., yourPlatform.recalculateShippingRates();
    })
    .catch(error => {
        console.error('Error sending pickup selection to backend:', error);
        // Handle error - maybe revert UI, show message
    });
}

Backend Example Concept (Platform-Specific - SFCC Example Provided for Illustration): The backend endpoint needs to receive the JSON payload, validate it, update the cart/shipment object, and potentially recalculate shipping. The following is a conceptual example using SFCC server-side JS:

// Example SFCC Controller Action (Conceptual - from original doc)
"use strict";
var server = require("server");
server.extend(module.superModule);
var csrfProtection = require("*/cartridge/scripts/middleware/csrf");
var HubBoxHelper = require("*/cartridge/scripts/helpers/HubBoxHelper"); // Assumes helper exists
var BasketMgr = require("dw/order/BasketMgr");
// ... other requires ...

// Endpoint logic (simplified)
server.post("SetHubboxSelection", server.middleware.https, csrfProtection.validateAjaxRequest, function (req, res, next) {
    var currentBasket = BasketMgr.getCurrentBasket();
    if (!currentBasket) { /* Handle error */ }
    var shipment = currentBasket.defaultShipment;

    // Example using data passed in request body (req.body) or form (req.form)
    var requestData = JSON.parse(req.body); // If using fetch with JSON body

    if (requestData.action === 'select_pickup' && requestData.collectPointId /* Add validation */) {
        // Use helper or logic to update the SFCC Shipment object
        HubBoxHelper.copyHubBoxDataToShipment(
            shipment,
            requestData.collectPointId,
            requestData.collectPointName,
            requestData.collectPointType,
            requestData.collectPointNetwork
            // Pass address object too if needed by helper
        );
        // Update session/recalculate shipping etc.
        res.json({ success: true, message: "HubBox selection saved." });
    } else if (requestData.action === 'deselect_pickup') {
        HubBoxHelper.clearHubBoxDataFromShipment(shipment);
            // Update session/recalculate shipping etc.
        res.json({ success: true, message: "HubBox selection cleared." });
    } else {
        res.setStatusCode(400);
        res.json({ error: true, message: "Invalid request." });
    }
    return next();
});
module.exports = server.exports();

Deselection (onCollectPointUnselected): Make a similar Workspace call to your endpoint, perhaps with action: 'deselect_pickup', so the backend can clear the relevant data and revert to home delivery logic.

  1. Cookies (Fallback Method):

How: In the onCollectPointConfirmed callback, use document.cookie to set a cookie containing the necessary HubBox data (e.g., location ID). Your backend code then needs to read this cookie value during checkout processing to identify the pickup selection.

Pros: Can sometimes work as a last resort on highly restrictive platforms where modifying forms or making JS API calls is impossible.

Cons: Less reliable (cookies can be blocked/cleared), limited data size, potential security concerns if not handled carefully (use HttpOnly, Secure flags where appropriate on server-set cookies, be mindful of data in client-set cookies). Generally not recommended unless other methods fail.

Deselection (onCollectPointUnselected): You would need to delete the cookie or set its value to indicate deselection.

Choose the method that best suits your checkout architecture (Traditional Form vs. SPA/Headless) and technical constraints. Using a Custom REST API is generally the most robust and recommended approach for modern web applications.

Step 8: Manifesting and Labelling Pickup Orders

Depending on your chosen carrier for pickup services, you may need to make some changes to the way in which your WMS creates shipments and/or generates labels.

In some instances, you will need to take the ID property from the COLLECT_POINT_CONFIRMED event and either place this code within the shipping address for the order or store as metadata.

Please refer to our documentation on Manifesting Pickup Order for full details or contact HubBox if you require support.

Sharing Order Data with HubBox (Recommended for Insights)

Purpose

Sharing details of completed pickup orders back to HubBox enables powerful data analysis and reporting features within the HubBox Console dashboard. This provides insights into pickup adoption rates, ROI, carrier performance, and operational trends. We strongly encourage implementing one of these methods.

Methods

  1. Order Processing API (Server-to-Server):

    • How it works: After a customer successfully places an order that used a HubBox pickup point, your backend server makes a direct, secure API call to the HubBox Order Processing API endpoint.
    • Data: You send key details like your Order ID, the selected HubBox Location ID (collectPointId), Network (collectPointNetwork), order value, currency, item count, and potentially anonymized customer details.
    • Pros: Most reliable method as it's server-to-server and less prone to browser issues (ad blockers etc.). Allows for retries on failure.
    • Action: Review the detailed HubBox Order Processing API Documentation for endpoint details, required fields, authentication, and implement the API call within your backend order completion workflow.
  2. Tracking Pixel (Frontend):

    • How it works: You add a small HubBox JavaScript snippet (the "Pixel") to your Order Confirmation / Thank You page. When this page loads in the customer's browser, the script gathers order details (from data layer variables or page content) and sends them directly to HubBox from the browser. This method is often a simpler frontend implementation and doesn't require backend changes specifically for HubBox data sharing.
    • Action: Review the HubBox Tracking Pixel Documentation for the script snippet and instructions on how to populate it with dynamic order data on your confirmation page.

Key Data Points (Typical)

Choose the method that best fits your technical capabilities and reliability requirements. The Order Processing API is generally preferred for accuracy. Data is anonymised and does not include any personally identifiable information (PII) or payment/card information (PCI).

Customization

Styling

Launch / Confirmation Components: If using HubBox Components for the launch experience, style them using CSS ::part() and CSS Custom Properties (:root variables) as detailed in the HubBox Components Documentation.

Widget Modal/iframe: The internal look and feel of the map widget itself (colors, pins, map style within the iframe) is primarily controlled by the configId you use during SingleWidgetManager initialization. This configuration is managed within the HubBox platform. See the Widget Configuration Documentation.

Text & Language (Translations)

Set Locale: Pass the correct locale code (e.g., "fr-FR") in the iframeParams.locale when initializing SingleWidgetManager.

Override Text: Pass a translations object within iframeParams to override default text within the widget iframe. For text in Components rendered directly on your page (like the confirmation component), set the .translations property on the component instance after creating it. Refer to HubBox Components Documentation for keys.

Advanced Features (Implemented by Retailer)

Implementing features like eligibility and rate filtering with the SDK requires custom logic within your application, triggered by SDK events.

Product Eligibility Filtering

Concept: Prevent users from selecting pickup if their cart contains ineligible items.

Implementation: 1. Develop backend logic to determine if a cart is eligible based on its contents (checking product attributes, categories, dimensions etc. against your rules). Expose this eligibility status (e.g., via an API endpoint or data attribute on the page). 2. In your frontend JavaScript, before triggering the HubBox widget (e.g., before opening the modal or running a search), check the cart's eligibility status (fetch from backend or read from page data). 3. If ineligible, disable or hide your "Choose Pickup Location" button/UI elements. Optionally display a message explaining why pickup is unavailable.

Below is an example snippet for running eligibility logic to show or hide the pickup UI.

function isBasketEligibleForPickup(cartItems) {
  return cartItems.every((item) => item.isEligibleForPickup); // Assume 'isEligibleForPickup' exists on items
}

// ... In your checkout area ...
const cartItems = getCartItems(); // Replace with your platform's method
const pickupOptionElement = document.getElementById("pickupOption");

if (isBasketEligibleForPickup(cartItems)) {
  pickupOptionElement.style.display = "block"; // Show pickup option
} else {
  pickupOptionElement.style.display = "none"; // Hide pickup option
}

Shipping Rate Filtering

Concept: Show only relevant shipping rates after the user selects either Home Delivery or Local Pickup. Essential for "Toggles" style launch experiences.

Implementation: 1. In your frontend JavaScript onCollectPointConfirmed callback, send the "pickup selected" flag and potentially the chosen network/location details to your backend API. In onCollectPointUnselected, send a "home delivery selected" flag. 2. Your backend logic (which calculates/retrieves shipping rates) must receive this flag. 3. Based on the flag, filter the list of available shipping methods before returning them to the frontend for display. (e.g., If pickup selected, only return rates configured for pickup; if home selected, only return home rates). 4. Your frontend may need to trigger a refresh of the shipping rates section after the selection is made and confirmed by the backend.

More Info: Shipping Rate Filtering Documentation.

Below is an example snippet for filtering shipping rates based on whether the order is a pickup order.

function filterShippingRates(availableRates, isPickupOrder) {
  if (isPickupOrder) {
    return availableRates.filter((rate) => rate.id === "local_pickup"); // Only keep "Local Pickup"
  } else {
    return availableRates.filter((rate) => rate.id !== "local_pickup"); // Filter out "Local Pickup"
  }
}

// ... Somewhere in your checkout process ...
const isPickupOrder = order.hasFlag("pickup"); // Replace with your logic
const allShippingRates = getAvailableShippingRates(); // Replace with your platform's method
const filteredRates = filterShippingRates(allShippingRates, isPickupOrder);

// Update the UI or recalculate totals using 'filteredRates'

Testing

Thorough end-to-end testing on a staging/development environment is critical for SDK implementations.

  1. Environment: Initialize SingleWidgetManager using the SingleWidgetManager.iframeUrls.SANDBOX URL and ensure your backend is configured to use HubBox Sandbox API endpoints (if applicable).
  2. Checklist:
    • Installation: Verify NPM packages are installed correctly.
    • Initialization: Check browser console for WIDGET_READY events and no BOOT_FAILED errors.
    • UI Interaction: Test your launch experience UI (buttons/toggles/search).
    • Widget Flow: Open modal/embedded widget. Search for locations. Select a location. Confirm selection. Cancel/deselect.
    • Callback Logic: Use console.log heavily within your callbacks (onCollectPointConfirmed, onCollectPointUnselected) to verify they fire correctly and receive the expected data (messageAndTopic.message).
    • Address Population: Does the correct pickup address populate your frontend checkout fields?
    • Backend Communication: Verify the AJAX/fetch calls from your JS callbacks reach your backend successfully.
    • Session/Cart Update: Check (via debug or test API calls) that your backend correctly updates the session/cart state (pickup flag, location ID/address).
    • Filtering: Test eligibility and shipping rate filtering logic with various scenarios.
    • Confirmation UI: Does the confirmation component display correctly after selection and hide upon deselection?
    • Order Placement: Place test orders selecting pickup.
    • Order Data: Verify the HubBox Location ID, network, and name are correctly saved with the order in your database.
    • Order Data Sharing: If implemented, verify the Order Processing API call is made successfully from your backend OR the Tracking Pixel fires correctly on the confirmation page (check browser network tab and/or HubBox dashboard).
    • Cross-Browser/Device: Test on major browsers and mobile devices.

To access HubBox's UAT guide, visit the Testing Documentation.

Example Integration Project

An example project demonstrating basic SDK setup and interaction is available.

Download SDK Integration Example (Ensure you are logged in to download. Contact your HubBox representative if you need access.)

Support

The SDK offers great flexibility but requires careful implementation. If you encounter issues or have questions about specific SDK methods, events, component usage, or integration strategies, please contact the HubBox Client Support or Integrations team at clientsupport@hubbox.com. Provide relevant code snippets, configuration details, and steps to reproduce the issue.