Skip to main content
Shopify Plus Checkout Premium Installation
Shevaughn avatar
Written by Shevaughn
Updated over 2 years ago

First step is to make a small liquid tweak to hide our pricing line items inline, so that when our price fix javascript runs, we don't get a very noticeable collapsing of the item list table as we remove the pricing item lines. Edit layout/checkout.liquid and find the following:

<div class="sidebar__content">
            ##{{ content_for_order_summary }}
          </div>

Replace that with:

<div class="sidebar__content">
            ##{{ content_for_order_summary | replace: 'data-product-type="SHOPSTORM_HIDDEN_PRODUCT"', 'data-product-type="SHOPSTORM_HIDDEN_PRODUCT" style="display:none;"' }}
</div>

Then, insert the following javascript to fix the main product prices just before the </body> closing tag (this is important, as it won't work properly in the document head):

<script type="text/javascript">
  // http://help.shopstorm.com/article/420-shopify-plus-checkout-premium-installation
  // Version: 3
  // Date: 2017-08-03
  
  // Assumptions:
  // * We assume a certain page structure, and bail out aggressively if things
  //   aren't as we expect (e.g. Shopify changed the layout)
  // * Each pricing item is proceeded by an associated main product
  // * An event named 'page:change' is fired on the document each time the 
  //   checkout page is ajax-refreshed
  // * Line item prices use a dot as decimal separator (1,299.99) or no decimals.
  //   If decimal separator is comma (1.299,99), code will NOT work.  // Hide any pricing items, and add their cost to the associated main product line
  function fixPaidCustomizationProducts(node) {
    // Money (not cents) string w/o currency. Only digits, commas, and dots are preserved.
    // e.g. '$50.00' becomes '50.00', '$1,234.99' becomes '1,234.99'
    // @param moneyString [String]
    // @return [String]
    var getMoneyStringWithoutCurrency = function(moneyString) {
      return moneyString.replace(/[^\d.,]/g, '');
    };
    
    // Money (not cents) number.
    // e.g. '$50.00' becomes 50, '$1,234.99' becomes 1234.99
    // @param moneyString [String]
    // @return [Number, NaN] Number if conversion went well, NaN otherwise.
    var getMoneyNumber = function(moneyString) {
      return +getMoneyStringWithoutCurrency(moneyString).replace(/[^\d.]/g, '');
    };    // @param element [HTMLElement] will have its textContent replaced.
    // @param rawPriceString [String] money string, without currency
    // @param pricingItemLinePrice [Number] price to add to raw price string
    var replacePriceTextAddingCustomization = function(element, rawPriceString, pricingItemLinePrice) {
      var priceCustomized = (getMoneyNumber(rawPriceString) + pricingItemLinePrice).toFixed(2);
      element.textContent = element.textContent.replace(rawPriceString, priceCustomized);
    };    var pricingItemRows = node.querySelectorAll('[data-product-type="SHOPSTORM_HIDDEN_PRODUCT"]');
    for (var i = 0; i < pricingItemRows.length; i++) {
      var pricingItemRow = pricingItemRows[i];
      var productPriceEl = pricingItemRow.getElementsByClassName('product__price')[0];
      if (!productPriceEl) {
        return;
      }
      // Pricing item line price as money (not cents) Number, e.g. '$50.00' becomes 50, '$1.99' becomes 1.99
      var pricingItemLinePrice = getMoneyNumber(productPriceEl.textContent);
      if (!pricingItemLinePrice) {
        return;
      }
      var mainItemRow = pricingItemRow.previousElementSibling;
      if (!mainItemRow) {
        return;
      }
      var mainItemLinePriceEl = mainItemRow.getElementsByClassName('product__price')[0];
      if (!mainItemLinePriceEl) {
       return;
      }
      // Main item line price element may include a crossed out original price (usually in a <del> tag)
      // which results in a non-numeric string, e.g. '106.0090.10'. We need
      // the final line price, assumed to be in the last child element.
      var mainItemLinePriceStrikeThru = mainItemLinePriceEl.getElementsByTagName('del')[0];
      var mainItemLinePriceLastChild = mainItemLinePriceEl.lastElementChild;
      var mainItemLinePriceRaw = getMoneyStringWithoutCurrency(mainItemLinePriceLastChild.textContent);
      if (isNaN(+mainItemLinePriceRaw)) {
          return;
      }      // update the main product price
      if (!mainItemLinePriceLastChild) {
        return;
      }      replacePriceTextAddingCustomization(
        mainItemLinePriceLastChild,
        mainItemLinePriceRaw,
        pricingItemLinePrice
      );
      
      if (mainItemLinePriceStrikeThru) {
        console.debug(mainItemLinePriceStrikeThru);
        var mainItemLinePriceStrikeThruRaw = getMoneyStringWithoutCurrency(mainItemLinePriceStrikeThru.textContent);
        replacePriceTextAddingCustomization(
          mainItemLinePriceStrikeThru,
          mainItemLinePriceStrikeThruRaw,
          pricingItemLinePrice
        );
      }      // remove the pricing item row
      pricingItemRow.parentElement.removeChild(pricingItemRow);
    }
  }  // fix the paid customization products on page load.
  fixPaidCustomizationProducts(document);
  // fix the paid customization products on ajax refresh (this results in a small but noticeable flash as the main product price is fixed)
  document.addEventListener('page:change', function() { fixPaidCustomizationProducts(document); });
</script>

Less common cases

In addition to the above changes, the plus checkout may include a customized order summary loop summary, which shows the hidden product. For example,

	<tr data-product-id="##{{ line.product_id }}" data-variant-id="##{{ line.variant_id }}" data-product-type="##{{ line.product.type }}">

Replace with

	<tr data-product-id="##{{ line.product_id }}" data-variant-id="##{{ line.variant_id }}" data-product-type="##{{ line.product.type }}" {% if line.product.type == 'SHOPSTORM_HIDDEN_PRODUCT' %} style="display:none;"{% endif %}>

Additionally, if the main product(s) show the _pc_pricing line item properties at checkout, we have to detect and skip those properties in the line.properties loop.

    {% for prop in line.properties %}
        {%- capture prop_first_char -%}##{{ prop.first | slice: 0, 1 }}{%- endcapture -%}
        {%- if prop_first_char == '_' -%}{% continue %}{%- endif -%}
Did this answer your question?