Code Integration Shipping protection only

Overview

In this guide we will be cloning your live theme for code editing. Then we will locate the Subtotal Container on your Cart page, which will be used to house the Shipping Protection Offer Element. Once identified both on page and in your Cart page's code you will then add in the code necessary to populate the Shipping Offer within the Cart page.

Estimated time of completion: 1 Hour


Configuration

🚧

Follow this guide to setup and run your theme locally using Stencil CLI here


Shipping Protection Snippet

Create extend-integration folder within templates/components and create 2 files:

  1. extend-checkoutSP.html
  2. extend.js

extend-checkoutSP.html

<!-- Extend script tags - these can be moved into script manager to apply only to product and cart pages -->
<script src='https://sdk.helloextend.com/extend-sdk-client/v1/extend-sdk-client.min.js'></script>
<script src='https://sdk.helloextend.com/extend-sdk-client-bigcommerce-addon/v1/extend-sdk-client-bigcommerce-addon.min.js'></script>
<script>Extend.config({ storeId: 'STORE_ID_GOES_HERE', environment: 'production' })</script>
<!-- Extend script tags end -->

<!-- Extend - checkout SP offers -->
<script>
  'use strict';

  let listeners = [], doc = window.document, MutationObserver = window.MutationObserver || window.WebKitMutationObserver, observer;

  const productList =  '.productList' // Products container inside the order summary at checkout
  const singleProduct = '.product' // Line item in order summary at checkout
  const productTitle = '.product-title' // Product title in order summary used to check if title equals Extend Protection Plan
  const productImageContainer = '.product-figure' // Product in order summary Adds Extend logo to Extend line item
  const shippingSection = '#checkout-shipping-options' // Shipping step in checkout - code will wait until we find this selector
  const shippingMethods = '#checkout-shipping-options' // Shipping methods container - we insertBefore the continue button after this query

  function ready(selector, fn) {
    // Store the selector and callback to be monitored
    listeners.push({
      selector: selector,
      fn: fn
    });
    if (!observer) {
      // Watch for changes in the document
      observer = new MutationObserver(check);
      observer.observe(doc.documentElement, {
        childList: true,
        subtree: true
      });
    }
    // Check if the element is currently in the DOM
    check();
  }

  function check() {
    // Check the DOM for elements matching a stored selector
    for (let i = 0, len = listeners.length, listener, elements; i < len; i++) {
      listener = listeners[i];
      // Query for elements matching the specified selector
      elements = doc.querySelectorAll(listener.selector);
      for (let j = 0, jLen = elements.length, element; j < jLen; j++) {
        element = elements[j];
        // Make sure the callback isn't invoked with the same element more than once
        if (!element.ready) {
          element.ready = true;
          // Invoke the callback with the element
          listener.fn.call(element, element);
        }
      }
    }
  }

  function renderShippingProtectionOffer(spCart) {
      if (spCart) {
        const itemsMapped = ExtendBigCommerce.formatCartItemsForSp(spCart);
        if (Extend.shippingProtection._instance !== null) {
          Extend.shippingProtection.update({ items: itemsMapped });
        } else {
          Extend.shippingProtection.render({
            selector: '#extend-shipping-offer',
            items: itemsMapped,
            isShippingProtectionInCart:
                    ExtendBigCommerce.isShippingProtectionInCart(spCart),
            onEnable: function (quote) {
              const callback = (error) => {
                if (error) {
                  console.error('EXTEND: ', error)
                  return;
                } else {
                  window.location.reload();
                }
              };
              return ExtendBigCommerce.addSpPlanToCart(
                      { quote, cart: spCart },
                      callback
              );
            },
            onDisable: function (quote) {
              const callback = (error, res) => {
                if (error) {
                  console.error('EXTEND: ', error)
                  return;
                } else {
                  window.location.reload();
                }
              };
              return ExtendBigCommerce.removeSpPlanFromCart(
                      { quote, cart: spCart },
                      callback
              );
            },
            onUpdate: function (quote) {
              const callback = (error, data) => {
                if (error) {
                  console.error('EXTEND: ', error)
                  return;
                } else if (data.spPlanUpdated) {
                  window.location.reload();
                }
              };
              return ExtendBigCommerce.getCart((error, cart) => {
                if (cart) {
                  ExtendBigCommerce.updateSpPlanInCart({quote, cart}, callback);
                }
                else {
                  if (error) {
                    console.error('Error: ', error)
                  }
                }
              });
            },
          });
        }
      }
  }

  window.addEventListener('load', () => {
    if (window.Extend && window.ExtendBigCommerce) {
      /**********************/
      /*  Global Variables  */
      /**********************/

      // Create shipping protection empty div
      const shippingProtectionOffer = document.createElement('div');
      shippingProtectionOffer.id = 'extend-shipping-offer';
      shippingProtectionOffer.style.marginTop = '50px';
      shippingProtectionOffer.style.marginBottom = '-50px';

      // Get's updated cart object
      ExtendBigCommerce.getCart(function (error, cart) {
        if (cart) {

          // Wait for shipping section to be found on DOM before running SP Code
          ready(shippingSection, () => {
            // Run normalization and handle shipping protection
            ExtendBigCommerce.updateExtendLineItems(
                    { balanceCart: true },
                    function (error, data) {
                      if (data && (data.updates || data.additions)) {
                        window.location.reload();
                      } else if(error) {
                        console.error('EXTEND: ', error)
                      }

                      // Find the shippingSpot querySelector
                      let shippingSpot = document.querySelector(shippingMethods).parentElement;

                      // Append Shipping protection offer to shippingSpot before form-actions and renderShippingProtectionOffer
                      shippingSpot.insertBefore(shippingProtectionOffer, shippingSpot.querySelector('.form-actions'));
                      renderShippingProtectionOffer(data.cart);
                    }
            );
          })

          // Wait for product list to be found
          ready(productList, () =>{
            const products = document.querySelectorAll(singleProduct);
            
            if(products){
              const warrantyProducts = Array.from(products).filter((item) => {
                const productTitle = item.querySelector(productTitle).textContent;
  
                // Conditionally render product image if Extend product protection or Extend shipping protection
                if(productTitle.includes('Extend Protection') && item.querySelector(productImageContainer)){
                  item.querySelector(productImageContainer).innerHTML = `<img alt='${productTitle}' data-test='cart-item-image' src='{{cdn 'img/extend_logo.png'}}'>`
                } else if(productTitle.includes('Extend Shipping') && item.querySelector('.product-figure')){
                  item.querySelector('.product-figure').innerHTML = `<img alt='${productTitle}' data-test='cart-item-image' src='{{cdn 'img/extend_shipping.png'}}'>`
                }
              });
            }
          })
        }
      });
    }
  });
</script>
<!-- Extend - End Extend Code -->

Configure Extend storeID

At the top of the snippet you pasted in, ensure you update your Extend StoreID. If you need assistance locating your StoreID refer to this guide on Finding your StoreID


Configure Extend Shipping Protection Offer Placement

  • On the checkout page, right click on the summary box and click “Inspect element”
  • Find the element that contains the cart-section element
  • Copy the class name

  • In the snippet you pasted, near the top of the 'load' event, set this to the class or selector of where you would like the Extend Shipping Protection offer appended.


Add extend.js

In extend-integration folder within templates/components Create a file called extend.js and paste the snippet from below into the file

// Import stencil utils
import utils from '@bigcommerce/stencil-utils';

// Handles removing item from cart and hard refreshes page
const removeCartItem = (itemId) => {
    // Remove passed in itemId from the cart
    utils.api.cart.itemRemove(itemId, (error, response) => {
        if (response.data.status === 'succeed') {
            window.location.reload();
        } else if(error) {
            console.error('EXTEND: ', error)
        }
    });
}

// initializeCartOffers() - Handles cart normalization and balancing 
export function initializeCartOffers() {
    if (window.Extend && window.ExtendBigCommerce) {
      ExtendBigCommerce.getCart(function (error, cart) {
        if (cart) {
            // If there is customItems in the cart
            if(cart.lineItems.customItems.length >= 1){
                // Check if custom item is Extend Shipping Protection and if so, remove it from cart
                if(cart.lineItems.customItems.filter(itm => itm.name === 'Extend Shipping Protection Plan')[0]){
                    const spItemToRemove = cart.lineItems.customItems.filter(itm => itm.name === 'Extend Shipping Protection Plan')[0].id

                    // cartRemoveItem(spItemToRemove) - Removes SP from cart
                    removeCartItem(spItemToRemove)
                }
            }
        } else if(error) {
            console.error('EXTEND: ', error);
        }
      });
    }
}

Import and invoke initializeCartOffers()

Locate assets/js/theme/cart.js

a. Import initializeCartOffers at the top of cart.js, below all other imports:

// Extend - Import initializeCartOffers from extend.js
import { initializeCartOffers } from './extend';
// Extend - End code

b. within the same file locate the onReady() function and add the snippet below:

// Extend - invokes initializeCartOffers on cart load
initializeCartOffers();
// Extend - End Code

c. within the same file locate the getContent() function and add the following snippet at the bottom of the function:

// Extend - invokes initializeCartOffers on cart load
initializeCartOffers();
// Extend - End Code


Hide Shipping Protection Line Item

Now we will add some additional code to ensure that the Shipping Protection Line Item is hidden on the preview cart and main cart pages. This is meant to clean up the Cart page experience overall.


Locate content.html

  • Navigate to templates/components/cart/content.html

Edit

  • Find where the code is looping through each cart item and at the top of the loop add the following snippet:
<!-- Extend Hide SP Items -->
{{#if name '!==' 'Extend Shipping Protection Plan'}}

  • Scroll down to the bottom of the file and find where the loop is ending, add the following snippet below:
{{/if}}
  <!-- Extend - End code -->

Great job, you have implemented the code pieces needed to facilitate Shipping Protection Offers on your BigCommerce Store. Next we will go over some Verification steps needed to complete the project.

If you run into any issues during this integration process or have questions please reach out to our team through your Merchant Portal.


Checklist