A shopper searches for a product, sees the regular price, and never learns that members pay less or earn points. The loyalty program exists. The product page explains it. Yet the benefit is absent from Google because the store has not translated its rules into data Google can connect to each product.

The fix is not a line of generic SEO copy. It is a consistent data model across Shopify, the storefront, Google Merchant Center, and structured data.

Google currently supports loyalty program information in Search in Australia, Canada, the UK, the US, and several other countries. The feature can cover member prices and earned points, but eligibility never guarantees that Google will display the enhancement. Google documents the supported countries and this display limitation.

This guide shows how to build the connection without creating two conflicting versions of the same loyalty benefit.

Decide which system owns the loyalty data

There are two supported routes:

  1. Configure the loyalty program in Google Merchant Center and send product-level benefits in product data.
  2. Define the program on your site with MemberProgram structured data, then reference its tiers from each eligible Product offer.

You can use both, but only if they stay synchronized. Google says that when Merchant Center settings and markup are both present, Merchant Center settings take precedence. That makes Merchant Center the practical source of truth for stores already using the Google & YouTube sales channel or another feed workflow.

Before implementation, write one row per benefit:

Market Program Tier Regular price Member price Points earned Currency
US Example Rewards Member 100 90 100 USD
UK Example Rewards Member 80 72 80 GBP
Canada Example Rewards Member 135 121.50 135 CAD
Australia Example Rewards Member 150 135 150 AUD

These are example values, not conversion rates. Every production row must come from the price, currency, tier, and points rules that a shopper actually sees in that market.

Make the storefront promise explicit

Google should not be the only place where the benefit exists. A product page should make the regular price, member benefit, eligibility, and sign-up path understandable to a shopper.

Confirm that:

  • the loyalty program has a stable public name;
  • every tier has a stable identifier;
  • the member price shown to Google matches the landing page for that market;
  • points are calculated from the same product price and rounding rule used after purchase;
  • joining conditions and tier requirements are available on a public policy or rewards page;
  • the program is not hidden behind a login wall that prevents Google from reading its administrative details.

This is the important boundary: structured data describes a real offer. It should not manufacture an offer that the page, cart, or account cannot honor.

Route one: configure loyalty in Merchant Center

For a feed-led setup, create the program and tiers in Merchant Center first. Then map Shopify product and variant data to the matching program labels and tier labels.

Google’s loyalty program product attribute can carry:

  • program label;
  • tier label;
  • member price;
  • loyalty points;
  • member price effective dates;
  • a member shipping label.

Google requires loyalty points in this attribute to be whole numbers. The member-price currency must match the regular or sale-price currency, and the submitted member price should be the lowest price available to that tier. Review the complete loyalty program attribute rules before changing a feed.

The mapping must happen at variant level when variants have different prices or benefits. A product-level default is unsafe if a size, subscription option, or market-specific variant changes what the member receives.

Shopify’s Google & YouTube channel synchronizes products and store information with Merchant Center. Do not assume that the base product sync automatically includes custom loyalty rules. Inspect the attributes that reach Merchant Center and add a controlled supplemental or custom feed process when the required loyalty fields are missing.

Route two: add MemberProgram structured data

Google’s model has two connected layers.

The organization layer defines the program and tiers. Place MemberProgram under Organization or OnlineStore on the page that explains the business policies and loyalty program. Google requires a program name, description, and at least one tier. Each tier needs a name and at least one benefit. If a tier has no hasTierRequirement value, Google treats it as free to join.

A minimal example looks like this:

{
  "@context": "https://schema.org",
  "@type": "OnlineStore",
  "@id": "https://example.com/#store",
  "name": "Example Store",
  "url": "https://example.com/",
  "hasMemberProgram": {
    "@type": "MemberProgram",
    "name": "Example Rewards",
    "description": "Members receive special prices and earn points.",
    "url": "https://example.com/pages/rewards",
    "hasTiers": [{
      "@type": "MemberProgramTier",
      "@id": "https://example.com/pages/rewards#member",
      "name": "Member",
      "hasTierBenefit": [
        "https://schema.org/TierBenefitLoyaltyPrice",
        "https://schema.org/TierBenefitLoyaltyPoints"
      ],
      "membershipPointsEarned": 1
    }]
  }
}

At this layer, membershipPointsEarned describes points earned per currency unit spent. Use your real earning rate. Google’s MemberProgram reference defines the required tier properties.

Connect a member price to the Product offer

The product layer uses UnitPriceSpecification under Offer. Keep the current regular price on the Offer and put the lower member price in a price specification linked to the tier:

{
  "@context": "https://schema.org/",
  "@type": "Product",
  "name": "Example Product",
  "offers": {
    "@type": "Offer",
    "url": "https://example.com/products/example-product",
    "price": 100,
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "priceSpecification": {
      "@type": "UnitPriceSpecification",
      "price": 90,
      "priceCurrency": "USD",
      "validForMemberTier": {
        "@type": "MemberProgramTier",
        "@id": "https://example.com/pages/rewards#member"
      }
    }
  }
}

Google requires a current regular price when validForMemberTier marks a member price. Do not put priceType and validForMemberTier on the same UnitPriceSpecification; Google says that price specification will be ignored.

For points earned on a specific purchase, the same tier reference can sit beside membershipPointsEarned:

{
  "@type": "UnitPriceSpecification",
  "membershipPointsEarned": 100,
  "validForMemberTier": {
    "@type": "MemberProgramTier",
    "@id": "https://example.com/pages/rewards#member"
  }
}

Product-level membershipPointsEarned is a whole-number purchase benefit. It is currently a beta property, so Google warns that an effect may not appear immediately. This is different from the program-level earning rate.

Fit the markup into a Shopify theme safely

Shopify’s Liquid structured_data filter can generate baseline Product JSON-LD from a product object. Shopify documents the output of the structured_data filter. A theme, app embed, or custom snippet may already output another Product node.

Implementation advice: inspect the rendered HTML before adding anything. Extend the authoritative Product offer when the theme architecture allows it. If a separate graph is necessary, connect stable nodes with matching @id values and keep price, availability, URL, SKU, and currency consistent. Avoid several unconnected Product objects that disagree about the active offer.

For multi-market stores, render the benefit for the request’s active market and currency. Do not expose USD member pricing on a CAD landing page or connect a UK tier to an Australian offer.

Validate the complete path

Test data at four checkpoints:

  1. Shopify: confirm the selected variant, market price, tier, and points rule.
  2. Storefront HTML: view the rendered source and find the final JSON-LD, not only the Liquid template.
  3. Google: run the product and policy URLs through the Rich Results Test, then inspect the live URL in Search Console.
  4. Merchant Center: compare processed attributes, program labels, tier labels, currencies, and diagnostics with the storefront.

Use a small launch set first. Select one simple product, one variant-heavy product, one discounted product, and one product in each active currency. Fix the model before expanding it across the catalog.

Common reasons the benefit stays invisible

  • Merchant Center and JSON-LD use different program or tier identifiers.
  • The member price exists in data but is missing from the landing page.
  • Regular price and member price use different currencies.
  • The Product offer describes a different variant from the canonical page.
  • A theme and an app output competing Product nodes.
  • The tier policy page is blocked, incomplete, or not linked.
  • Points are fractional where Google expects a whole product-level value.
  • The property is valid but still in beta, or Google chooses not to show the enhancement.

Passing a validator proves that Google can parse the markup. It does not prove that the values match Shopify, that Merchant Center agrees, or that the benefit will appear in Search.

Build one reliable source of truth

The durable solution is a small mapping layer with explicit ownership: Shopify holds product and market data, the loyalty system holds tier rules, and one integration produces Merchant Center attributes and storefront markup from the same normalized values.

If your loyalty prices or points are trapped inside theme logic, account-only data, or an incomplete product feed, discuss your Shopify project with MerchWeave. We can map the data flow, define the smallest safe implementation, and help validate it from Shopify to Google.