Skip to content

Gift card support guide

Component reference: the live component page is Gift card.

Diagnostic and troubleshooting reference for the gift-card landing page — JSON+sections migration, locale-aware date, QR code Custom Element, clipboard copy, Apple Wallet integration, Google Wallet pass setup, recipient form explanation, and the 3 distinct visual states (Active / Expired / Depleted). For merchant setup and configuration, see Gift card guide.


FAQ

Q1: Gift-card expiry date shows in English on a French / Arabic / Japanese store

This was a known English-leakage defect (D-43) in the pre-06.9 theme. The gift-card date used:

liquid
{{ gift_card.expires_on | date: '%B %e, %Y' }}

This always outputs English month names regardless of locale.

Fixed in Phase 06.9 via a localized translation key plus the time_tag filter:

liquid
{{ 'gift_card.expires_on_html' | t: expires_on: gift_card.expires_on | time_tag }}

The gift_card.expires_on_html key carries the localized sentence, and time_tag outputs a locale-aware <time> element that Shopify renders with the store's active locale.

To verify the fix is in place:

bash
curl -s https://your-store.myshopify.com/gift_cards/{code} | grep 'expires_on\|time>'
# Expected: <time datetime="...">29 avril 2027</time> (on a French store)

If you are still seeing English dates after upgrading to Uisce 06.9, confirm the theme was pushed to the live store. The safe default is the non-destructive sync — it preserves live settings and deletes nothing:

bash
bash scripts/sync-theme-safe.sh <Preset>

scripts/push-presets.sh is the destructive full-reset path (overwrites live settings with committed defaults) — use it only per docs/deploy/RUNBOOK.md.

Q2: QR code doesn't render — blank or broken image

The QR code is generated by the <gift-card-qr> Custom Element using qrcode.js (shipped in the theme's assets/). Three possible causes:

  1. qrcode.js failed to load. DevTools → Network → filter qrcode — should return HTTP 200. If 404, the asset is missing from the theme push.
  2. <gift-card-qr> Custom Element failed to upgrade. Run the console snippet below → inspect gift_card.qr. If false, the <canvas> was not rendered — the Custom Element may have failed to initialize.
  3. Canvas API blocked. Some browser extensions (privacy shields, canvas fingerprint protection) block canvas.getContext('2d'). Test in an incognito window or a different browser.

Diagnostic steps:

  1. Run the console snippet → gift_card.qr — should be true if the canvas rendered
  2. DevTools → Console → check for any error from gift-card.js
  3. DevTools → Elements → find <gift-card-qr> — should have a <canvas> child element

Expected QR code size: 160×160 px, on screen and in print — the print stylesheet does not resize the QR code.

Q3: Copy code button doesn't work

The <gift-card-copy> Custom Element uses the Clipboard API (navigator.clipboard.writeText()). Two requirements:

  1. HTTPS context. The Clipboard API requires a secure origin. Dev stores use HTTPS — this should not be an issue on Shopify-hosted storefronts.
  2. Browser support. navigator.clipboard is available in all modern browsers (Chrome 66+, Firefox 63+, Safari 13.1+). Edge 79+.

If the copy button still doesn't work:

  1. Run the console snippet → gift_card.copy — should be true if <gift-card-copy> element is present
  2. Clipboard permission denied does not break the button: the rejection is handled and routes to a hidden-textarea document.execCommand('copy') fallback, so the button keeps working and no DOMException surfaces in the console.
  3. If navigator.clipboard is undefined (very outdated browser), the same execCommand fallback runs — the button still copies. Shopify storefronts are always HTTPS, so a non-secure context is not a factor there.
  4. If neither path can copy (execCommand blocked as well), the button fails silently — no "Copied" feedback appears. Use manual selection (below).

Fallback behavior: The gift-card code is also rendered as plain text in a <span class="gift-card-page__code"> element styled with user-select: all — a single click or tap selects the whole code for manual copying if the button fails.

Q4: Apple Wallet button is missing

The Apple Wallet button only appears when gift_card.pass_url is non-nil. This is populated by Shopify when the Apple Gift Card Passbook integration is active.

To check:

  1. Run the console snippet → gift_card.apple_wallet — will be true if the button is present
  2. If false: the Apple integration is not enabled in your Shopify admin

To enable:

  1. Shopify admin → Settings → Payments → Gift cards
  2. Enable Apple Wallet passes
  3. Complete the Apple developer certificate setup (requires an Apple developer account for pass signing)
  4. After setup, new gift cards will have pass_url populated automatically

Note: The Apple Wallet button cannot be faked or added manually — it requires Shopify to generate a valid Apple Wallet pass URL for each gift card. Existing gift cards issued before enabling the integration may not have pass_url until re-issued.

Q5: Google Wallet button is missing

The Google Wallet button appears when section.settings.google_wallet_url_template is non-empty in the theme editor.

To configure:

  1. Install a Google Wallet pass provider app (Walletly, PassKit, etc.)
  2. Obtain the URL template from the app
  3. Theme editor → Sections → Gift card → Google Wallet URL template
  4. Paste the URL template with &#123;&#123;code&#125;&#125; and &#123;&#123;balance&#125;&#125; placeholders
  5. Save

The template must start with https://. Any other scheme — http://, a protocol-relative //host/path, or anything else — is dropped and the button does not render. The setting is text rather than url because the URL validator rejects the &#123;&#123;code&#125;&#125; placeholder, so the theme applies the scheme check itself; http:// is excluded deliberately rather than as an oversight, since a wallet pass URL is always TLS.

Verify the button is rendering:

Run the console snippet → gift_card.google_wallet — should be true if the <div class="gift-card-page__google-wallet"> wrapper is present (the class is on the div; the anchor inside it is unclassed).

Q6: Recipient name field is missing from the gift-card landing page

The landing page intentionally has no recipient form (D-40 decision) — the recipient is set at purchase time, on the gift-card product page. The theme ships Shopify's native recipient form there: buyers tick "I want to send this as a gift" above the Add to cart button and fill in the recipient's email, name, message, and an optional send date. Shopify then emails the gift card directly to the recipient on the chosen date.

The form posts Shopify's reserved recipient properties (these — and only these — trigger the native send-to-recipient email):

  • properties[__shopify_send_gift_card_to_recipient] — the opt-in checkbox
  • properties[Recipient email] — Shopify sends the gift card link to this address (required once opted in)
  • properties[Recipient name] — optional
  • properties[Message] — optional personal message (200-character max)
  • properties[Send on] — optional scheduled send date (up to 90 days out)

The gift-card landing page (/gift_cards/{code}) is a redemption artifact — the recipient has already been set. Adding a recipient form on the landing page would allow anyone with the URL to change the recipient, which is a security issue.

Recipient form not appearing on the product page? Confirm the product is a real gift-card product (Shopify marks it gift_card — the form only renders when product.gift_card? is true) and that JavaScript is enabled; the checkbox activates via progressive enhancement.


Console diagnostic snippet

Paste into Chrome DevTools console (F12 → Console tab) on any page of the live store. Reports gift-card state when on a gift-card page; also reports footer state on every page.

js
(function () {
  var path = location.pathname;
  var report = { path: path, surfaces: {} };
  // Footer always
  var footer = document.querySelector('.footer');
  report.surfaces.footer = {
    present: !!footer,
    policies: !!document.querySelector('.footer__policies'),
    localization: !!document.querySelector('.footer__localization'),
    newsletter: !!document.querySelector('.footer__newsletter-block customer-form'),
    payment_icons_aria: Array.from(document.querySelectorAll('.footer__payment span[aria-label]')).length,
  };
  // 404
  if (document.querySelector('.section-404')) {
    report.surfaces['404'] = {
      hero: !!document.querySelector('.section-404__hero svg'),
      search: !!document.querySelector('.section-404__search'),
      recently_viewed: !!document.querySelector('recently-viewed'),
      noindex: document.querySelector('meta[name="robots"]')
        ? document.querySelector('meta[name="robots"]').content
        : null,
    };
  }
  // Password
  if (document.querySelector('.password-page')) {
    report.surfaces.password = {
      logo: !!document.querySelector('.password-page__logo img'),
      hero: !!document.querySelector('.password-page__hero svg'),
      countdown: !!document.querySelector('count-down'),
      newsletter: !!document.querySelector('.password-page__newsletter customer-form'),
      social: !!document.querySelector('.password-page__social'),
      localization: !!document.querySelector('.password-page__localization'),
    };
  }
  // Gift-card
  if (document.querySelector('.gift-card-page')) {
    report.surfaces.gift_card = {
      qr: !!document.querySelector('gift-card-qr canvas'),
      copy: !!document.querySelector('gift-card-copy'),
      balance: document.querySelector('.gift-card-page__balance')
        ? document.querySelector('.gift-card-page__balance').textContent.trim()
        : null,
      state:
        ['active', 'expired', 'depleted'].filter(function (s) {
          return !!document.querySelector('.gift-card-page--state-' + s);
        })[0] || 'active',
      apple_wallet: !!document.querySelector('.gift-card-page__apple-wallet'),
      google_wallet: !!document.querySelector('.gift-card-page__google-wallet'),
    };
  }
  console.table(report.surfaces);
  return report;
})();

What to look for on a gift-card page:

  • gift_card.qr === true — QR code <canvas> rendered successfully
  • gift_card.copy === true<gift-card-copy> element is present
  • gift_card.balance — current balance text (e.g., "$25.00")
  • gift_card.state — one of active, expired, depleted
  • gift_card.apple_wallet === true — Apple Wallet button present (requires Shopify integration)
  • gift_card.google_wallet === true — Google Wallet button present (requires google_wallet_url_template setting)

Curl cheat-sheet (D-67)

bash
curl -L -s -o /dev/null -w "%{http_code}" https://your-store.myshopify.com/policies/privacy-policy
# Expected: 200

Topic 2 — 404 status code (for reference)

bash
curl -I https://your-store.myshopify.com/not-a-real-path-12345
# Expected: HTTP/2 404

Topic 3 — Password form action (for reference)

bash
curl -I https://your-store.myshopify.com/password
# Expected: HTTP/2 200

Topic 4 — Gift-card noindex,nofollow header

Verify the gift-card landing page emits the correct noindex,nofollow signal:

bash
curl -I https://your-store.myshopify.com/gift_cards/{your-gift-card-code}
# Expected headers:
#   x-robots-tag: noindex, nofollow   (Shopify platform header)
# AND in page source:
#   <meta name="robots" content="noindex,nofollow">  (Uisce theme layout/gift-card.liquid)

This defense-in-depth approach prevents gift-card redemption URLs from being indexed by search engines.

Topic 5 — Apple Wallet pass URL availability

bash
# Inspect page source for the pass URL
curl -s -b cookies.txt https://your-store.myshopify.com/gift_cards/{code} | grep 'apple-wallet'
# Expected: <div class="gift-card-page__apple-wallet"> wrapping an unclassed <a href="https://...">
# (plus the add-to-apple-wallet.svg badge-image line)

# Verify the pass URL responds
curl -I {apple_pass_url_from_page_source}
# Expected: HTTP/2 200

Topic 6 — Google Wallet template substitution

Verify the &#123;&#123;code&#125;&#125; and &#123;&#123;balance&#125;&#125; placeholders were substituted correctly:

bash
curl -s -b cookies.txt https://your-store.myshopify.com/gift_cards/{code} | grep 'google-wallet'
# Expected: <div class="gift-card-page__google-wallet"> wrapping an unclassed <a href="...">
# The href is your configured template with the code and balance placeholders replaced by
# real values (query parameter names are whatever your template defines; the balance
# interpolates as a raw subunit amount, e.g. 5000 for $50.00).

If &#123;&#123;code&#125;&#125; or &#123;&#123;balance&#125;&#125; appear literally in the href, the URL template substitution failed — check the google_wallet_url_template setting uses &#123;&#123;code&#125;&#125; and &#123;&#123;balance&#125;&#125; as placeholders exactly.

Topic 7 — Cache-control for gift-card assets

bash
curl -I https://your-store.myshopify.com/cdn/shop/t/1/assets/gift-card.css
# Expected: cache-control: public, max-age=31536000 (Shopify CDN strong cache)

curl -I https://your-store.myshopify.com/cdn/shop/t/1/assets/gift-card.js
# Expected: cache-control: public, max-age=31536000

After pushing theme changes, the CDN URL includes a new version parameter — cache busts automatically. No manual cache invalidation required.

Built for the Shopify Theme Store.