Skip to main content
The Order Protection widget integrates with Ordergroove and your Shopify store, enhancing the subscription order experience for users with all relevant information in one place.The widget addition to the cart simplifies the claim-filing process for users reducing friction and eliminating the need for manual navigation to the Order Protection website.

Installation

1

Generate an API Token

Access the Ordergroove admin page: Ordergroove Admin
  • Go to Developers menu and click on API Keys
  • Click on Create button
  • Enter a Name and Email
  • Click on Save button
  • Copy the API Key for Step 3
API Token
2

Create a Webhook

Since Ordergroove does not have a way to automate the creation of webhooks, we will need to create them manually.
  • Go to Developers menu and click on Webhooks
  • Click on Create button
  • Enter a Name and Email
    • Webhook Name: Order Protection
    • Webhook URL: https://subscriptions.production.orderprotection.com/api/v1/webhooks/ordergroove/[PLATFORM_DOMAIN]/webhook
      • Replace [PLATFORM_DOMAIN] with your platform domain. e.g. for Shopify it would be example.myshopify.com
  • For events, tick only subscription.create
    • Note: there is a similar event called subscriber.create, please note to tick the correct one Webhook
  • No actions on Advance Settings
  • Click on Save button
  • Copy the Verification Key for Step 3
Webhook
3

Integrate Ordergroove within the Order Protection Platform

Within the Order Protection app, do the following:
  • Navigate to Integrations in the left navigation.
  • Find the Ordergroove block under the Available tab.
  • Take the API token you created in Step 1 and the Webhook Verification Key you created in Step 2 and add it to the corresponding fields and save.
  • Once saved, click on the Active tab and the API token and Webhook Verification Key fields within the Ordergroove block will have a hidden value. This means that your token and webhook successfully uploaded. ordergroove api token in op app
4

Install Scripts in Ordergroove's Subscription Manager

  • Go to Subscriptions menu and click on Subscription Manager
  • Click on Advanced toggle button
  • On the side menu, click on View
  • Click on ADD NEW FILE, and name it orderprotection.liquid with content:
    orderprotection.liquid
    {# required non-null: order, subscriptions, addresses, current_order_items #}
    <div style="padding-left: 12px; padding-right: 12px;">
      <div id="order-protection-{{ order.public_id }}"></div>
    </div>
    {{ 'bootstrapOrderProtection({ order, subscriptions, addresses, current_order_items })' | js }}
    
  • Go to /views/order/main.liquid, enter new line after {% include 'order-summary/main' %} and add:
    order/main.liquid
    `{% include 'orderprotection' %}`
    
    Like below: Paste code
  • Lastly, go to /scripts/script.js and add the following code to the bottom of the file:
    script.js
    /*
    * BEGIN ORDERPROCTION ORDERGROOVE MANAGER
    */
    
    /* Environment variables */
    const OP_CORE_CSS_URL = 'https://cdn.orderprotection.com/widget/core/latest/style.css';
    const OP_SCRIPT_URL = 'https://cdn.orderprotection.com/widget/subscriptions/latest/orderprotection.js';
    const OP_DEBUG = true;
    const OP_LOG_PREFIX = '[OP]';
    const OP_MAX_RETRIES = 5;
    
    /* State variables */
    let opScriptLoaded = false;
    const opWidgets = [];
    
    /* Utilities */
    function opLog(...message) {
      if (OP_DEBUG) console.log(OP_LOG_PREFIX, ...message);
    }
    
    function opDelay(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }
    
    function opCreateCurrencyFormatter() {
      const formatter = new Intl.NumberFormat(undefined, {
        style: 'currency',
        currency: window.Shopify.currency.active,
      });
      const symbol = formatter.formatToParts(1).find(p => p.type === 'currency').value;
      return {
        format: cents => formatter.format(cents / 100),
        symbol,
      };
    }
    
    function opQuerySelector(orderId, selector) {
      return document.querySelector(`.og-content-wrapper[data-order-id="${orderId}"] ${selector}`);
    }
    
    function opCreateElement(tag, attrs = {}, children = []) {
      const el = document.createElement(tag);
      Object.assign(el, attrs);
      el.append(...children);
      return el;
    }
    
    function opCreateCostBreakdownItem(label, value) {
      return opCreateElement('div', { className: 'op-cost-breakdown-item' }, [
        opCreateElement('dt', { textContent: label }),
        opCreateElement('dd', { className: 'og-text-align-right', textContent: value }),
      ]);
    }
    
    function opOnWidgetToggledHandler(orderId) {
      const opWidget = window.orderProtection?.[orderId];
      if (!opWidget || !opWidget.cart.subtotal || !opWidget.price) return;
    
      const { format, symbol: currency } = opCreateCurrencyFormatter();
      const total = opWidget.enabled ? opWidget.cart.subtotal + opWidget.price : opWidget.cart.subtotal;
      const formattedTotal = format(total);
    
      // Update price heading
      const priceHeadingEl = opQuerySelector(orderId, '.og-summary-heading.og-price-heading');
      if (priceHeadingEl) {
        priceHeadingEl.textContent = formattedTotal;
      } else {
        opLog('priceHeading not found:', orderId);
      }
    
      // Update total element
      const totalEl = opQuerySelector(orderId, '.og-total');
      if (totalEl) {
        totalEl.childNodes.forEach(child => {
          if (child.textContent.includes(currency)) {
            child.textContent = formattedTotal;
          }
        });
      } else {
        opLog('totalEl not found:', orderId);
      }
    
      // Update cost breakdown
      const costBreakdownEl = opQuerySelector(orderId, 'div.og-cost-breakdown > dl');
      if (costBreakdownEl) {
        const existingItem = costBreakdownEl.querySelector('.op-cost-breakdown-item');
    
        if (opWidget.enabled && !existingItem) {
          const item = opCreateCostBreakdownItem('Shipping Protection', format(opWidget.price));
          costBreakdownEl.appendChild(item);
        }
    
        if (!opWidget.enabled) {
          costBreakdownEl.querySelectorAll('.op-cost-breakdown-item').forEach(el => el.remove());
        }
      } else {
        opLog('costBreakdownEl not found:', orderId);
      }
    }
    
    /* Load assets */
    function opLoadAsset({ tag, attrs, parent = document.head }) {
      return new Promise((resolve, reject) => {
        const el = Object.assign(document.createElement(tag), attrs, {
          onload: () => resolve(el),
          onerror: reject,
        });
        parent.appendChild(el);
      });
    }
    
    Promise.all([
      opLoadAsset({ tag: 'link', attrs: { rel: 'stylesheet', href: OP_CORE_CSS_URL } }).then(() =>
        opLog('Core CSS loaded:', OP_CORE_CSS_URL),
      ),
      opLoadAsset({ tag: 'script', attrs: { src: OP_SCRIPT_URL }, parent: document.body }).then(() => {
        opScriptLoaded = true;
        opLog('script loaded:', opScriptLoaded);
      }),
    ]);
    
    /* Functions */
    // Bootstrap Order Protection, for use in the liquid template
    function bootstrapOrderProtection(params) {
      initOrderProtectionWidget(params);
    
      return '';
    }
    
    // Initialize the Order Protection widget
    async function initOrderProtectionWidget(params, retries = 0) {
      const { order } = params;
    
      // Check if the script has loaded
      if (!opScriptLoaded) {
        if (retries >= OP_MAX_RETRIES) {
          opLog('Script failed to load after max retries:', order.public_id);
          return;
        }
        await opDelay(300);
        opLog('Script not loaded, retrying...', order.public_id);
    
        return initOrderProtectionWidget(params, retries + 1);
      }
      if (opWidgets.some(widget => widget.subscription === order.public_id)) {
        return opLog('Already bootstrapped:', order.public_id);
      }
    
      opLog('Bootstrapping Order Protection for subscription:', order.public_id);
    
      // Create the Order Protection widget
      const opWidget = OrderProtection.createOrderProtectionWidget({
        instanceName: order.public_id,
        integration: 'ordergroove',
        attach: true,
        debug: OP_DEBUG,
        locations: [{ id: `order-protection-${order.public_id}` }],
        version: 'v2',
        preexistingData: params,
      });
    
      opWidget.cart.debug = OP_DEBUG;
      opWidget.widget.on('toggled', () => opOnWidgetToggledHandler(order.public_id));
    
      opWidgets.push({ subscription: order.public_id });
    
      // Wait for the cart to be ready, for final setup and initialization
      try {
        while (!opWidget.cart.isReady()) {
          opLog(order.public_id, 'Cart not ready, retrying...');
          opWidget.cart.subscription = {};
          await opWidget.cart.loadSubscription();
          await opDelay(500);
        }
    
        opWidget.cart.setup();
        await opWidget.init();
      } catch (error) {
        console.error(error);
      }
    }
    
    /*
    * END ORDERPROCTION ORDERGROOVE MANAGER
    */
    
Once set up, Order Protection will be added to all cart instances within Shopify subscription orders using Ordergroove. Customers can choose to opt-in or opt out of Order Protection for their subscription orders.
Customers will be able to file/edit claims per your normal store settings once an order confirmation email has been sent.