New features available! Check the changelog
Subscribfy
StorefrontCustomer Portal

VIP Savings Block (Legacy Portal)

The VIP Savings card for the old customer portal: a theme snippet with static colors and a small placement script that inserts the card into the membership dashboard

The same VIP Savings figure - total order discounts since a member's VIP start date - adapted for the legacy customer portal at /apps/subscribfy-api/store/manage-membership-dashboard. Because the old dashboard is an App Proxy page with no block editor, the card ships as a theme snippet and a small script positions it inside the dashboard.

On the new portal, use the VIP Savings Block instead. That version drops into the Sections editor as a Custom Liquid block and inherits your portal branding automatically. This legacy version is only for stores still on the old manage-membership-dashboard route.


What's Different From the New Portal

Static Colors

The legacy dashboard has no --exison-* theme variables, so the card uses plain CSS values. It ships with intentionally loud placeholder colors (hot pink card, yellow amount, lime and cyan text) so nobody forgets to replace them.

Placement Script

There is no page template to drop a Custom Liquid section into. The code lives in a theme snippet (JavaScript included), stays hidden, and a script moves the card into the dashboard above the membership status block when the customer is on the dashboard URL.


Requirements

RequirementDetail
VIP start date metafieldThe customer needs the exison.vip_start_date metafield. See the main VIP Savings doc for how it is written.
A real storefront sessionThe Liquid customer object must exist. The legacy dashboard authenticates with cid and hash in the URL, which does not log the customer into the storefront. A visitor who only follows that link sees no card. Customers logged into their store account see it normally.

Install

Create the snippet

In the store theme, go to Online Store -> Themes -> Edit code -> Snippets -> Add a new snippet and name it:

subscribfy-vip-savings

Paste the full snippet

Paste the entire code block below into the snippet. The Liquid, the <style>, and the <script> all go into this one file.

The JavaScript must stay inside the snippet. It is what positions the card inside the dashboard. Do not split it out into a separate asset that loads at a different time.

snippets/subscribfy-vip-savings.liquid
{%- assign vip_start_raw = customer.metafields.exison.vip_start_date -%}
{%- assign vip_start_ts = vip_start_raw | date: '%s' | plus: 0 -%}

{%- assign member_savings = 0 -%}
{%- assign member_orders = 0 -%}

{%- for order in customer.orders -%}
  {%- assign order_ts = order.created_at | date: '%s' | plus: 0 -%}
  {%- if vip_start_ts > 0 and order_ts >= vip_start_ts -%}
    {%- assign member_savings = member_savings | plus: order.total_discounts -%}
    {%- if order.total_discounts > 0 -%}
      {%- assign member_orders = member_orders | plus: 1 -%}
    {%- endif -%}
  {%- endif -%}
{%- endfor -%}

{%- if vip_start_ts > 0 -%}
<div id="exm-vip-savings-src" style="display:none">
  <div class="exm-vip-savings">
    <span class="exm-vip-savings__label">VIP Savings</span>
    <span class="exm-vip-savings__value">{{ member_savings | money }}</span>
    <span class="exm-vip-savings__meta">{{ member_orders }} Orders</span>
  </div>
</div>

<style>
.exm-vip-savings {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
  background: hotpink;
  color: yellow;
  border: 0;
  border-radius: 12px;
  padding: 12px;
  margin-bottom: 16px;
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}
.exm-vip-savings__label {
  font-weight: 600;
  color: lime;
}
.exm-vip-savings__value {
  font-size: 2.25rem;
  line-height: 2.5rem;
  font-weight: 700;
}
.exm-vip-savings__meta {
  color: cyan;
}

@media (min-width: 750px) {
  .exm-vip-savings {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas:
      'label value'
      'meta  value';
    align-items: center;
    column-gap: 24px;
    row-gap: 2px;
    text-align: left;
  }
  .exm-vip-savings__label { grid-area: label; }
  .exm-vip-savings__meta  { grid-area: meta; }
  .exm-vip-savings__value { grid-area: value; }
}
</style>

<script>
(function () {
  if (window.location.pathname.indexOf('/apps/subscribfy-api/store/manage-membership-dashboard') === -1) {
    return;
  }

  var tries = 0;
  var timer = setInterval(function () {
    var target = document.querySelector('.exm_membership_status_block');
    var src = document.getElementById('exm-vip-savings-src');

    if (target && src) {
      clearInterval(timer);
      target.parentNode.insertBefore(src, target);
      src.style.display = '';
    }

    if (++tries > 50) {
      clearInterval(timer);
    }
  }, 200);
})();
</script>
{%- endif -%}

Render it in the theme layout

The portal is an App Proxy URL wrapped in the theme layout, so render the snippet in layout/theme.liquid, just before </body>:

layout/theme.liquid
{% render 'subscribfy-vip-savings' %}

Rendering it globally is safe. The built-in URL check makes the script do nothing outside /apps/subscribfy-api/store/manage-membership-dashboard, and the card markup only renders for logged-in customers with a VIP start date.


Where the Card Lands

The dashboard skeleton is server-rendered by resources/views/manage-membership-dashboard.blade.php and wrapped in the store theme by the App Proxy. The card is inserted above the membership status block:

Dashboard structure
.exm_customer_info
  h1.page_title            "Manage My Membership"
  h3.exm_customer_name
  .exm_customer_email
  .exm_membership_status_block   <- the card is inserted ABOVE this element

.exm_membership_status_block exists in the initial HTML (it is not built by JS), so the short poll in the script is only a safety net against script-ordering timing.


Changing the Colors

The snippet ships with placeholder colors on purpose. Edit the values in place in the <style> block.

What you want to changeWherePlaceholder
Card background.exm-vip-savings -> backgroundhotpink
Amount color (the big number).exm-vip-savings -> color (the value inherits it)yellow
"VIP Savings" label.exm-vip-savings__label -> colorlime
"X Orders" line.exm-vip-savings__meta -> colorcyan
Corner roundness.exm-vip-savings -> border-radius12px
Card shadow.exm-vip-savings -> box-shadowsubtle; remove the line for a flat card

Color tips

  • Any CSS color format works: hex (#0a4d68), rgb(...), or a name (black).
  • Keep contrast in mind: the label and meta text must stay readable on the card background.
  • A typical dark set: background #1a1a1a, color: #ffffff, label/meta #d9d9d9. A light set: background #f5f5f5, color: #1a1a1a, label/meta #666666.

How the Numbers Work

Savings total

The sum of order.total_discounts for every order placed since vip_start_date. Only real discount applications count: VIP Function and automatic discounts, plus discount codes.

Computed in Liquid

The values are computed entirely in Liquid at page render. The script only repositions the already-rendered card; it calculates nothing.

Selling-plan pricing and member variant prices are not discount applications, so they are not included in the total. Only discounts that Shopify records against the order count.


Advanced: Placing the Card Elsewhere

To put the card somewhere other than above the membership status block, change the target and/or the insert position in the script:

  • Change querySelector('.exm_membership_status_block') to a different element on the dashboard.
  • To insert below the target instead of above it, swap insertBefore(src, target) for target.parentNode.insertBefore(src, target.nextSibling).

Troubleshooting


Was this page helpful?

On this page

AI Chat

Ask a question about Subscribfy