Point-based Discount

Developer docs

How to set up members, points rules, discounts, and product features. Prefer theme app extensions; Custom Liquid is only for theme developers.

Members

Loyalty is opt-in. Customers join from the member center; merchants can also add or link members in Admin. Do not bulk-sync the whole customer list.

Member center and joining

Copy the member center link from Members and place it in a menu, button, or campaign. Customers sign in with their Shopify account, accept membership terms, and then earn or redeem points. The same link is used for referrals.

Admin member management

In Members you can search, filter by tier, view a profile, adjust points, ban or unban, export CSV, and delete a member. Banned members see a notice in the member center and cannot earn or redeem. Tiers are Silver, Gold, and Platinum based on points.

  1. Open Members and copy the member center link into the store menu or a page.
  2. Ask customers to open the link, sign in, and join loyalty.
  3. To add someone already in Shopify Admin, use Link as member — this is a one-person opt-in, not a store-wide import.

Points rules

Rules control how members earn and redeem points. Turn a rule on or off in Points Rules. Storefront redeem and lucky wheel also need the matching theme app block.

Purchase rewards

Members earn points for every $1 spent on paid orders (merchandise after discounts, excluding shipping and tax). Orders that contain a Points Card product skip this rule.

Signup bonus

One-time points when a customer successfully joins the loyalty program.

Referral

The inviter earns points after the invited customer joins loyalty and completes a paid order. Share the member center link so the referral code can be stored.

Birthday bonus

Points are granted once per year on the birthday saved in the member profile. Members should keep birthday information up to date.

Profile task

One-time points after the member completes the required profile fields in the member center.

Daily check-in

Members water a sapling once per calendar day to earn daily points. After 30 watering days they can harvest a bonus (days do not need to be consecutive). Paid orders can add fertilizer that skips 5 watering days — except when the order only contains products on the exclusion list.

Points Card

Assign selected products as a Points Card. Buying those products grants a fixed point amount and does not also earn Purchase Rewards. Checkout redeem is hidden while a Points Card is in the cart.

Points redeem

Set how many points equal a dollar discount, the max points per order, and the max discount percent of the order subtotal. Customers redeem on the cart (theme app block or embed) or at checkout (Checkout UI extension). Redemption applies as an order discount and lowers the order total; it does not add a fee.

  1. Configure the exchange rate, order points cap, and max discount percent in Points Rules → Points redeem.
  2. On other store discounts, open Combinations and enable Order discounts if you want points to stack. If Order discounts is unchecked, points cannot apply alongside that discount.
  3. Add Loyalty points redeem on the cart page, or enable App embeds → Loyalty storefront.
  4. Also enable the Loyalty checkout redeem UI extension in Checkout and accounts.

Checkout redeem

The checkout block lets signed-in members enter points and tap Apply. No theme file edits are required. Enable the extension in Checkout and accounts.

Lucky Wheel

Create a wheel with prizes (points, product, or thanks), probabilities, stock, and optional entry cost. Each wheel has a short code. Paste only that code into the Lottery wheel app block — not a full HTML embed.

  1. Create a Lucky Wheel in Points Rules and copy the short code (for example AB12).
  2. Add the Loyalty lottery wheel app block in the theme editor.
  3. Paste the short code into the block’s Wheel code setting.
Advanced: Custom Liquid (theme developers)

Theme developers can use the Custom Liquid template below. Keep YOUR_WHEEL_CODE as a placeholder, then replace only that token with the short code.

Dynamic data in this snippet is limited to the YOUR_WHEEL_CODE placeholder.

Custom Liquid wheel

Replace YOUR_WHEEL_CODE with the short code from Points Rules. Do not bake a shop-specific code into the theme.

<!-- Loyalty lottery wheel — Custom Liquid only. Replace YOUR_WHEEL_CODE with the short code from Points Rules. -->
<link rel="stylesheet" href="/apps/loyalty/lottery-asset?file=css">
<section
  class="ll ll--embed"
  data-loyalty-lottery
  data-api="/apps/loyalty/lottery-api"
  data-code="YOUR_WHEEL_CODE"
  data-customer-id="{{ customer.id }}"
  data-login-url="{{ routes.account_login_url | default: '/account/login' }}"
  data-join-url="/apps/loyalty/member"
  data-asset-coin="/apps/loyalty/lottery-asset?file=coin"
  data-asset-coins="/apps/loyalty/lottery-asset?file=coins"
  data-asset-gift="/apps/loyalty/lottery-asset?file=gift"
  data-asset-thanks="/apps/loyalty/lottery-asset?file=thanks"
  data-asset-coins-gift="/apps/loyalty/lottery-asset?file=coins-gift"
>
  <div class="ll-wheel-stage">
    <div class="ll-wheel-wrap is-loading" data-wheel-wrap>
      <img class="ll-decor ll-decor--coin" src="/apps/loyalty/lottery-asset?file=coins-gift" alt="" width="240" height="160" aria-hidden="true">
      <img class="ll-decor ll-decor--gift" src="/apps/loyalty/lottery-asset?file=gift" alt="" width="130" height="130" aria-hidden="true">
      <canvas class="ll-wheel-canvas" data-lottery-canvas width="640" height="640"></canvas>
      <button class="ll-spin" type="button" data-lottery-spin disabled>
        <span class="ll-spin__title">SPIN</span>
        <span class="ll-spin__sub" data-spin-sub>Loading…</span>
      </button>
      <img class="ll-pointer" src="/apps/loyalty/lottery-asset?file=pointer" alt="" width="40" height="75" aria-hidden="true">
      <div class="ll-wheel-loading" data-lottery-loading role="status" aria-live="polite">
        <span class="ll-wheel-spinner" aria-hidden="true"></span>
        <span data-lottery-loading-text>Loading wheel…</span>
      </div>
    </div>
  </div>
  <div class="ll-modal" data-ll-modal hidden>
    <div class="ll-modal__backdrop"></div>
    <div class="ll-modal__panel" role="dialog" aria-modal="true" aria-labelledby="ll-modal-title">
      <button type="button" class="ll-modal__x" data-ll-close aria-label="Close">
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
          <path d="M6 6l12 12"></path>
          <path d="M18 6L6 18"></path>
        </svg>
      </button>
      <div class="ll-modal__icon" data-ll-modal-icon></div>
      <h3 class="ll-modal__title" id="ll-modal-title" data-ll-modal-title></h3>
      <p class="ll-modal__text" data-ll-modal-text></p>
      <div class="ll-modal__extra" data-ll-modal-extra hidden></div>
      <div class="ll-modal__actions">
        <button type="button" class="ll-btn ll-btn--primary ll-btn--block" data-ll-modal-primary></button>
        <button type="button" class="ll-modal__cancel" data-ll-modal-secondary hidden>Cancel</button>
      </div>
      <p class="ll-modal__hint" data-ll-modal-hint hidden></p>
    </div>
  </div>
</section>
<script src="/apps/loyalty/lottery-asset?file=js" defer></script>
<script>if (window.LoyaltyLotteryBoot) window.LoyaltyLotteryBoot();</script>

Discounts

Four discount types. Create them in Discounts, then add the matching app block or embed on the storefront. Discounts apply at Shopify checkout through the discount function — do not send buyers offsite.

A + B + C bundle

Pair one or more main products (A) with companions B and C. When a customer buys any A together with B and C, the discount applies to B and C — not to A.

  1. Create the discount and choose main products (A) plus companions B and C.
  2. Add the Loyalty A+B+C bundle app block on product pages.
  3. Customers toggle Add B & C on those product pages; the discount applies automatically at checkout.

Bundle accessories

Attach extra accessories to one or more main products. The more accessories a customer adds, the larger the discount on those extras.

  1. Create the discount, then choose main products, the accessory list, and discount tiers.
  2. Add the Loyalty accessories app block on product pages.
  3. Customers select accessories to unlock the matching tier; the discount applies at checkout.

Buy A, get B free

Choose a trigger product and a free gift variant. When the trigger is in the cart, the gift is added and kept in sync. The gift is 100% off at checkout.

  1. Create the gift discount and select the main product plus gift variant.
  2. Enable App embeds → Loyalty storefront so the gift is added and removed automatically.
  3. Do not ask merchants to paste gift code into theme files.

Member coupon

Create an amount or percent offer and share one link. Logged-in loyalty members open the link to receive their own checkout code. No theme app block is required.

  1. Create a member coupon and set the discount amount or percent.
  2. Copy the share link and send it to customers (email, social, SMS, and so on).
  3. Members open the link while signed in, then enter the unique code at checkout.

Products

Use Products to pick storefront recommendations. Wishlist is a Pro feature enabled in Settings. Prefer the Loyalty wishlist app block instead of editing theme files.

Member center recommendations

Plus plan. The products you select appear at the bottom of the member center. Set a heading and save. No theme code is required.

Thank-you page recommendations

Plus plan. These products appear in the Loyalty recommended products block on the order thank-you page. Enable that checkout UI block in the checkout editor. Content is merchant-configured store products, not app ads.

Wishlist

Pro plan. After enabling Wishlist in Settings, add the Loyalty wishlist app block on product pages. Members view saved products in the member center.

  1. Turn on Wishlist in Settings.
  2. Open the theme editor and add the Loyalty wishlist app block on the product template.
  3. Preview the product page while signed in as a customer.
Advanced: Custom Liquid (theme developers)

If you maintain a custom theme, add a Custom Liquid section in the theme editor. Do not paste into layout/theme.liquid.

1. Load CSS and JS (once)

Add once as a Custom Liquid section if you are not using the app block.

{% comment %} Loyalty wishlist assets — add once as a Custom Liquid section. Prefer the Loyalty wishlist app block, which already loads CSS/JS. {% endcomment %}
<link rel="stylesheet" href="/apps/loyalty/wishlist-asset?file=css">
<script src="/apps/loyalty/wishlist-asset?file=js" defer></script>

2. Product page button

Custom Liquid near Add to cart on the product template.

{% comment %} Loyalty wishlist — Custom Liquid on the product template, near Add to cart. Prefer the Loyalty wishlist app block. {% endcomment %}
<button
  class="lw"
  type="button"
  hidden
  data-loyalty-wishlist
  data-api="/apps/loyalty/wishlist-api"
  data-product-id="{{ product.id }}"
  data-variant-id="{{ product.selected_or_first_available_variant.id }}"
  data-product-title="{{ product.title | escape }}"
  data-variant-title="{{ product.selected_or_first_available_variant.title | escape }}"
  data-price="{{ product.selected_or_first_available_variant.price | money_without_currency }}"
  data-image="{% if product.featured_image %}{{ product.featured_image | image_url: width: 400 }}{% endif %}"
  data-handle="{{ product.handle | escape }}"
  data-customer-id="{{ customer.id }}"
  data-login-url="{{ routes.account_login_url }}"
  data-join-url="/apps/loyalty/member"
>
  <span class="lw__icon" aria-hidden="true"><svg class="lw__svg" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true"><path fill="currentColor" d="M903.714133 350.208c-10.820267-65.655467-47.4624-117.504-108.936533-154.146133-35.293867-21.060267-75.025067-32.187733-114.858667-32.187734-50.875733 0-99.140267 18.619733-135.8848 52.4288-11.383467 10.478933-21.794133 21.930667-31.829333 33.0752-31.214933-38.0416-63.095467-61.559467-100.061867-73.8816-64.8704-21.6064-129.4848-13.277867-188.859733 24.405334-70.587733 44.782933-106.103467 108.168533-105.540267 188.433066 0.3072 47.2576 16.059733 92.1088 48.110934 137.096534 34.4064 48.3328 76.7488 97.536 133.239466 154.862933 38.024533 38.587733 77.397333 76.9536 115.473067 114.0736l37.1712 36.266667c3.310933 3.242667 6.690133 6.4512 10.069333 9.6256l4.1984 3.908266c7.901867 6.587733 24.388267 17.646933 45.994667 17.646934 17.885867 0 34.850133-7.338667 50.551467-21.9136 64.631467-60.8768 133.495467-126.993067 199.048533-198.0416 33.9456-36.795733 72.8064-80.0768 104.448-128.085334 34.286933-52.053333 46.967467-107.093333 37.666133-163.566933m-544.273066-28.5184s-52.309333-18.926933-85.879467 34.218667c0 0-19.7632 31.726933 0 68.317866 0 0 12.561067 13.397333 4.164267 26.0608-8.3968 12.6464-26.333867 14.5408-33.809067 1.4336s-49.0496-80.520533 19.9168-145.851733c0 0 42.24-38.690133 95.607467-25.070933 0 0 39.662933 5.614933 35.566933 28.552533s-28.125867 17.749333-35.566933 12.3392z"/></svg></span>
  <span class="lw__label" data-wish-label>Add to wishlist</span>
</button>

3. Collection / product card icon

Custom Liquid inside the product-card image wrapper. That wrapper needs position: relative (or class lw-card-host).

{% comment %} Loyalty wishlist — Custom Liquid inside a product-card image wrapper (position: relative or class lw-card-host). {% endcomment %}
{% assign wish_product = card_product | default: product %}
{% if wish_product %}
<button
  class="lw-card-btn"
  type="button"
  hidden
  aria-label="Add to wishlist"
  data-loyalty-wishlist
  data-api="/apps/loyalty/wishlist-api"
  data-product-id="{{ wish_product.id }}"
  data-variant-id="{{ wish_product.selected_or_first_available_variant.id }}"
  data-product-title="{{ wish_product.title | escape }}"
  data-variant-title="{{ wish_product.selected_or_first_available_variant.title | escape }}"
  data-price="{{ wish_product.selected_or_first_available_variant.price | money_without_currency }}"
  data-image="{% if wish_product.featured_image %}{{ wish_product.featured_image | image_url: width: 400 }}{% endif %}"
  data-handle="{{ wish_product.handle | escape }}"
  data-customer-id="{{ customer.id }}"
  data-login-url="{{ routes.account_login_url }}"
  data-join-url="/apps/loyalty/member"
>
  <span class="lw__icon" aria-hidden="true"><svg class="lw__svg" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true"><path fill="currentColor" d="M903.714133 350.208c-10.820267-65.655467-47.4624-117.504-108.936533-154.146133-35.293867-21.060267-75.025067-32.187733-114.858667-32.187734-50.875733 0-99.140267 18.619733-135.8848 52.4288-11.383467 10.478933-21.794133 21.930667-31.829333 33.0752-31.214933-38.0416-63.095467-61.559467-100.061867-73.8816-64.8704-21.6064-129.4848-13.277867-188.859733 24.405334-70.587733 44.782933-106.103467 108.168533-105.540267 188.433066 0.3072 47.2576 16.059733 92.1088 48.110934 137.096534 34.4064 48.3328 76.7488 97.536 133.239466 154.862933 38.024533 38.587733 77.397333 76.9536 115.473067 114.0736l37.1712 36.266667c3.310933 3.242667 6.690133 6.4512 10.069333 9.6256l4.1984 3.908266c7.901867 6.587733 24.388267 17.646933 45.994667 17.646934 17.885867 0 34.850133-7.338667 50.551467-21.9136 64.631467-60.8768 133.495467-126.993067 199.048533-198.0416 33.9456-36.795733 72.8064-80.0768 104.448-128.085334 34.286933-52.053333 46.967467-107.093333 37.666133-163.566933m-544.273066-28.5184s-52.309333-18.926933-85.879467 34.218667c0 0-19.7632 31.726933 0 68.317866 0 0 12.561067 13.397333 4.164267 26.0608-8.3968 12.6464-26.333867 14.5408-33.809067 1.4336s-49.0496-80.520533 19.9168-145.851733c0 0 42.24-38.690133 95.607467-25.070933 0 0 39.662933 5.614933 35.566933 28.552533s-28.125867 17.749333-35.566933 12.3392z"/></svg></span>
</button>
{% endif %}