How can I display the membership selling plans on my membership product?

Once you've configured your membership perks and pricing, you'll want to add the selling plans (pricing options) to your membership product within Shopify. There are 3 ways to do this, all explained in detail on the Installation tab of the membership details page, as well as in the tutorial below. If you have any trouble at all with the installation process, don't hesitate to contact us for additional assistance.

App Embed

The easiest way to add selling plans to your membership product page is via an App Embed. This is a special widget that you enable within your Shopify theme that will attempt to automatically add selling plans to your membership product. You can access the App Embed directly by clicking the button on the Installation tab. 

Note that the button will open a generic product page. Be sure to select your membership product so you can see how the selling plans look. Make sure to save your changes after you're done editing!

To return to the App Embeds directly in the future, start by going to your theme customizer and selecting App Embeds on the left-hand side menu.

Below is an example of where the App Embed shows up and what it may look like within your theme.

Online Store 2.0

If your theme supports Shopify's new Online Store 2.0 format, you can add the selling plans into your theme the same way you would add blocks and sections.

Start by opening your theme's Customize section and navigating to your membership product. On the left side, click either "Add section" or "Add block" and select "Membership Selling Plans".

You can also optionally add a "Membership Price" block that will update the price based on the selling plan selected.

Below is an example of where the App block shows up and what it may look like within your theme.

Advanced

If you'd prefer to add selling plans directly using HTML, you can use the code below. Paste it into your product form within your product template to display the selling plans. If you're having issues with the product form from the featured product sections or quick buy buttons, the snippet below might help you get this working.

Make sure to use the appropriate manual snippet depending on your product configuration regarding the Upfront Charges:


If there is no upfront charge:

<script type="application/json" id="selling-plan-json-{{ block.id }}">
  {{ all_products[product.handle] | json }}
</script>

{% assign selling_plan_selected = blank %}
{% assign has_selling_plans = false %}

{% for variant in product.variants %}
  {% if variant.id == product.selected_or_first_available_variant.id %}
    {% for allocation in variant.selling_plan_allocations %}
      {% assign selling_plan_selected = allocation.selling_plan.id %}
      {% break %}
    {% endfor %}
  {% endif %}
  {% if variant.selling_plan_allocations.size > 0 %}{% assign has_selling_plans = true %}{% endif %}
  {% if selling_plan_selected != blank %}{% break %}{% endif %}
{% endfor %}

{% if has_selling_plans %}

  <fieldset class="selling-plan-fieldset" id="selling-plan-fieldset-{{ block.id }}" data-id="{{ block.id }}">
    <legend>Plans</legend>

    {% for variant in product.variants %}
      <div data-variant="{{ variant.id }}" class="selling-plans {% if variant.id != product.selected_or_first_available_variant.id %}selling-plan-hide{% endif %}">
        {% for allocation in variant.selling_plan_allocations %}

          {% assign selling_plan_frequency = blank %}
          {% for option in allocation.selling_plan.options %}
            {% if option.name == 'Charged every' %}
              {% capture selling_plan_frequency %}every {{ option.value | remove: "1" }}{% endcapture %}
            {% endif %}
          {% endfor %}          
      
           {% assign initial_price = '' %}
           {% assign recurrent_price = '' %}
            {% for adjustment in allocation.price_adjustments %}
              {% if forloop.length == 1 %}
                {% capture recurrent_price %}{{ adjustment.price | money }}{% endcapture %}
              {% elsif forloop.first %}
                {% if adjustment.price == 0 %}
                   {% capture initial_price %}Free trial, then {% endcapture %}
                {% else %}
                   {% capture initial_price %}{{ adjustment.price | money }} upfront, then {% endcapture %}
                {% endif %}
              {% else %}
                 {% capture recurrent_price %}{{ adjustment.price | money }}{% endcapture %}
              {% endif %}
            {% endfor %}

            <label for="selling_plan_{{ block.id }}_{{ allocation.selling_plan.id }}" class="selling-plan-label">
              <input type="radio" name="selling_plan_option" id="selling_plan_{{ block.id }}_{{ allocation.selling_plan.id }}" value="{{ allocation.selling_plan.id }}" {% if selling_plan_selected == allocation.selling_plan.id %}checked="checked"{% endif %} data-price="{{ per_delivery_price | money_with_currency | replace:'"',"'" }} {{ selling_plan_frequency }}">
               {{ initial_price }}{{ recurrent_price }}&nbsp;{{ selling_plan_frequency }}       
            </label>

        {% endfor %}
      </div>
    {% endfor %}
  </fieldset>
  <input name="selling_plan" type="hidden" value="{{ selling_plan_selected }}" hidden>

{% endif %}

<script>
(function () {

  function CMupdateProductForm(product_id, selling_plan_id, price) {
    document.querySelectorAll('.selling-plan-form-' + product_id).forEach(function(productForm) {
      if(productForm.querySelector('input[name="selling_plan"]')) {
        productForm.querySelector('input[name="selling_plan"]').value = selling_plan_id;
      } else {
        var selling_plan_input = document.createElement('input');
        selling_plan_input.name = "selling_plan";
        selling_plan_input.value = selling_plan_id;
        selling_plan_input.type = "hidden";
        productForm.appendChild(selling_plan_input);
      }
    });
    document.querySelectorAll('.selling-plan-price-' + product_id).forEach(function(priceContainer) {
      priceContainer.innerHTML = price;
    });
  }

  document.addEventListener("DOMContentLoaded", function () {
   cmLoadScripts()
  });

  cmLoadScripts()

  function cmLoadScripts() {

    // if this is an embedded app, dynamically add selling plans
    if(typeof CMSellingPlans == 'function') {
      CMSellingPlans();
    }

    document.querySelectorAll('.selling-plan-fieldset').forEach(function(sellingPlanContainer) {
      var product = JSON.parse(document.getElementById('selling-plan-json-' + sellingPlanContainer.dataset.id).innerHTML);

      // Get all forms for this product
      for(var i in product.variants) {
        document.querySelectorAll('form[action*="/cart/add"]').forEach(function(productForm) {
          var variant_field = productForm.querySelector('[name="id"]');
          
          var external_variant_field = false;
          if(!variant_field && productForm.getAttribute('id') !== undefined) {
            variant_field = document.querySelector('[name="id"][form="' + productForm.getAttribute('id') + '"]');
            external_variant_field = true;
          }
          if(variant_field && (variant_field.value == product.variants[i].id || (variant_field.value == '' && variant_field.options !== undefined && variant_field.options[0].value == product.variants[i].id))) {

            // add a selector to make it easy to identify
            productForm.classList.add('selling-plan-form-' + product.id);

            // if it's an external variant field, add a change there
            if(window.getComputedStyle(variant_field).display == 'none' || external_variant_field) {
              variant_field.addEventListener('change', function (e) {
                setTimeout(function() {
                  sellingPlanContainer.querySelectorAll('.selling-plans:not([data-variant="' + this.value + '"])').forEach(function(planGroup) { planGroup.classList.add('selling-plan-hide'); });
                  sellingPlanContainer.querySelectorAll('.selling-plans[data-variant="' + this.value + '"]').forEach(function(planGroup) { planGroup.classList.remove('selling-plan-hide'); });
  
                  var selling_plan = sellingPlanContainer.querySelector('.selling-plans[data-variant="' + this.value + '"] input[name="selling_plan_option"]');
                  selling_plan.checked = true;
                  CMupdateProductForm(product.id, selling_plan.value, selling_plan.dataset.price);
                }.bind(this), 50);
              });
            }

            // if it's a hidden field, it's complicated.
            if(window.getComputedStyle(variant_field).display == 'none' || (external_variant_field && variant_field.type == 'hidden')) {
              let previousValue = variant_field.value;
              setInterval(function() {
                  // 1: check if value changed
                  if (variant_field.value !== previousValue) {
                      previousValue = variant_field.value;
                      // 2: trigger `change` event
                      variant_field.dispatchEvent(new Event('change'));
                  }
              }, 500);
            }

            productForm.addEventListener('change', function (e) {
              if(e.srcElement.name != "selling_plan_option") {
                setTimeout(function() {
                  var variant_field = this.querySelector('[name="id"]');
                  sellingPlanContainer.querySelectorAll('.selling-plans:not([data-variant="' + variant_field.value + '"])').forEach(function(planGroup) { planGroup.classList.add('selling-plan-hide'); });
                  sellingPlanContainer.querySelectorAll('.selling-plans[data-variant="' + variant_field.value + '"]').forEach(function(planGroup) { planGroup.classList.remove('selling-plan-hide'); });
  
                  var selling_plan = sellingPlanContainer.querySelector('.selling-plans[data-variant="' + variant_field.value + '"] input[name="selling_plan_option"]');
                  selling_plan.checked = true;
                  CMupdateProductForm(product.id, selling_plan.value, selling_plan.dataset.price);
                }.bind(this), 50);
              }
            });

          }
        });
      }

      // Watch for selling plan option change to update the selected selling plan
      sellingPlanContainer.querySelectorAll('input[type="radio"]').forEach(function (radio) {
          radio.addEventListener('change', function () {
            CMupdateProductForm(product.id, this.value, this.dataset.price);
          });
      });

      // when the page first loads, make sure the selling plan id is added to the forms
      var selected_radio = sellingPlanContainer.querySelectorAll('input[type="radio"]:checked').forEach(function(radio) {
          CMupdateProductForm(product.id, radio.value, radio.dataset.price);
      });
    });
  }


})();
</script>
<style>
  label.selling-plan-label {
  display: flex;
    align-items: center;
  }
  label.selling-plan-label input[type="radio"] {
    margin-right: 10px;
    min-height: unset;
  }
  fieldset.selling-plan-fieldset {
    padding: 2px 12px 10px;
      max-width: 300px;
      margin: 0 auto 2.5rem 0;
      flex-basis: 100%;
  }
  .selling-plan-hide {
    display: none;
  }
</style>

If upfront charge:

<script type="application/json" id="selling-plan-json-{{ block.id }}">
  {{ all_products[product.handle] | json }}
</script>

{% assign selling_plan_selected = blank %}
{% assign has_selling_plans = false %}

{% for variant in product.variants %}
  {% if variant.id == product.selected_or_first_available_variant.id %}
    {% for allocation in variant.selling_plan_allocations %}
      {% assign selling_plan_selected = allocation.selling_plan.id %}
      {% break %}
    {% endfor %}
  {% endif %}
  {% if variant.selling_plan_allocations.size > 0 %}{% assign has_selling_plans = true %}{% endif %}
  {% if selling_plan_selected != blank %}{% break %}{% endif %}
{% endfor %}

{% if has_selling_plans %}

  <fieldset class="selling-plan-fieldset" id="selling-plan-fieldset-{{ block.id }}" data-id="{{ block.id }}">
    <legend>Plans</legend>

    {% for variant in product.variants %}
      <div data-variant="{{ variant.id }}" class="selling-plans {% if variant.id != product.selected_or_first_available_variant.id %}selling-plan-hide{% endif %}">
        {% for allocation in variant.selling_plan_allocations %}

          {% assign selling_plan_frequency = blank %}
          {% for option in allocation.selling_plan.options %}
            {% if option.name == 'Charged every' %}
              {% capture selling_plan_frequency %}every {{ option.value | remove: "1" }}{% endcapture %}
            {% endif %}
          {% endfor %}          
      
           {% assign initial_price = '' %}
           {% assign recurrent_price = '' %}
            {% for adjustment in allocation.price_adjustments %}
              {% if forloop.length == 1 %}
                {% capture recurrent_price %}{{ adjustment.price | money }}{% endcapture %}
              {% elsif forloop.first %}
                {% if adjustment.price == 0 %}
                   {% capture initial_price %}Free trial, then {% endcapture %}
                {% else %}
                   {% capture initial_price %}{{ adjustment.price | money }} upfront, then {% endcapture %}
                {% endif %}
              {% else %}
                 {% capture recurrent_price %}{{ adjustment.price | money }}{% endcapture %}
              {% endif %}
            {% endfor %}

            <label for="selling_plan_{{ block.id }}_{{ allocation.selling_plan.id }}" class="selling-plan-label">
              <input type="radio" name="selling_plan_option" id="selling_plan_{{ block.id }}_{{ allocation.selling_plan.id }}" value="{{ allocation.selling_plan.id }}" {% if selling_plan_selected == allocation.selling_plan.id %}checked="checked"{% endif %} data-price="{{ per_delivery_price | money_with_currency | replace:'"',"'" }} {{ selling_plan_frequency }}">
               {{ initial_price }}{{ recurrent_price }}&nbsp;{{ selling_plan_frequency }}       
            </label>

        {% endfor %}
      </div>
    {% endfor %}
  </fieldset>
  <input name="selling_plan" type="hidden" value="{{ selling_plan_selected }}" hidden>

{% endif %}

<script>
(function () {

  function CMupdateProductForm(product_id, selling_plan_id, price) {
    document.querySelectorAll('.selling-plan-form-' + product_id).forEach(function(productForm) {
      if(productForm.querySelector('input[name="selling_plan"]')) {
        productForm.querySelector('input[name="selling_plan"]').value = selling_plan_id;
      } else {
        var selling_plan_input = document.createElement('input');
        selling_plan_input.name = "selling_plan";
        selling_plan_input.value = selling_plan_id;
        selling_plan_input.type = "hidden";
        productForm.appendChild(selling_plan_input);
      }
    });
    document.querySelectorAll('.selling-plan-price-' + product_id).forEach(function(priceContainer) {
      priceContainer.innerHTML = price;
    });
  }

  document.addEventListener("DOMContentLoaded", function () {
   cmLoadScripts()
  });

  cmLoadScripts()

  function cmLoadScripts() {

    // if this is an embedded app, dynamically add selling plans
    if(typeof CMSellingPlans == 'function') {
      CMSellingPlans();
    }

    document.querySelectorAll('.selling-plan-fieldset').forEach(function(sellingPlanContainer) {
      var product = JSON.parse(document.getElementById('selling-plan-json-' + sellingPlanContainer.dataset.id).innerHTML);

      // Get all forms for this product
      for(var i in product.variants) {
        document.querySelectorAll('form[action*="/cart/add"]').forEach(function(productForm) {
          var variant_field = productForm.querySelector('[name="id"]');
          
          var external_variant_field = false;
          if(!variant_field && productForm.getAttribute('id') !== undefined) {
            variant_field = document.querySelector('[name="id"][form="' + productForm.getAttribute('id') + '"]');
            external_variant_field = true;
          }
          if(variant_field && (variant_field.value == product.variants[i].id || (variant_field.value == '' && variant_field.options !== undefined && variant_field.options[0].value == product.variants[i].id))) {

            // add a selector to make it easy to identify
            productForm.classList.add('selling-plan-form-' + product.id);

            // if it's an external variant field, add a change there
            if(window.getComputedStyle(variant_field).display == 'none' || external_variant_field) {
              variant_field.addEventListener('change', function (e) {
                setTimeout(function() {
                  sellingPlanContainer.querySelectorAll('.selling-plans:not([data-variant="' + this.value + '"])').forEach(function(planGroup) { planGroup.classList.add('selling-plan-hide'); });
                  sellingPlanContainer.querySelectorAll('.selling-plans[data-variant="' + this.value + '"]').forEach(function(planGroup) { planGroup.classList.remove('selling-plan-hide'); });
  
                  var selling_plan = sellingPlanContainer.querySelector('.selling-plans[data-variant="' + this.value + '"] input[name="selling_plan_option"]');
                  selling_plan.checked = true;
                  CMupdateProductForm(product.id, selling_plan.value, selling_plan.dataset.price);
                }.bind(this), 50);
              });
            }

            // if it's a hidden field, it's complicated.
            if(window.getComputedStyle(variant_field).display == 'none' || (external_variant_field && variant_field.type == 'hidden')) {
              let previousValue = variant_field.value;
              setInterval(function() {
                  // 1: check if value changed
                  if (variant_field.value !== previousValue) {
                      previousValue = variant_field.value;
                      // 2: trigger `change` event
                      variant_field.dispatchEvent(new Event('change'));
                  }
              }, 500);
            }

            productForm.addEventListener('change', function (e) {
              if(e.srcElement.name != "selling_plan_option") {
                setTimeout(function() {
                  var variant_field = this.querySelector('[name="id"]');
                  sellingPlanContainer.querySelectorAll('.selling-plans:not([data-variant="' + variant_field.value + '"])').forEach(function(planGroup) { planGroup.classList.add('selling-plan-hide'); });
                  sellingPlanContainer.querySelectorAll('.selling-plans[data-variant="' + variant_field.value + '"]').forEach(function(planGroup) { planGroup.classList.remove('selling-plan-hide'); });
  
                  var selling_plan = sellingPlanContainer.querySelector('.selling-plans[data-variant="' + variant_field.value + '"] input[name="selling_plan_option"]');
                  selling_plan.checked = true;
                  CMupdateProductForm(product.id, selling_plan.value, selling_plan.dataset.price);
                }.bind(this), 50);
              }
            });

          }
        });
      }

      // Watch for selling plan option change to update the selected selling plan
      sellingPlanContainer.querySelectorAll('input[type="radio"]').forEach(function (radio) {
          radio.addEventListener('change', function () {
            CMupdateProductForm(product.id, this.value, this.dataset.price);
          });
      });

      // when the page first loads, make sure the selling plan id is added to the forms
      var selected_radio = sellingPlanContainer.querySelectorAll('input[type="radio"]:checked').forEach(function(radio) {
          CMupdateProductForm(product.id, radio.value, radio.dataset.price);
      });
    });
  }


})();
</script>
<style>
  label.selling-plan-label {
  display: flex;
    align-items: center;
  }
  label.selling-plan-label input[type="radio"] {
    margin-right: 10px;
    min-height: unset;
  }
  fieldset.selling-plan-fieldset {
    padding: 2px 12px 10px;
      max-width: 300px;
      margin: 0 auto 2.5rem 0;
      flex-basis: 100%;
  }
  .selling-plan-hide {
    display: none;
  }
</style>
Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us