Liquid Snippets by ALSEL
📄 ページテンプレート中級

ギフトカード受け取りページ

ギフトカード受け取り時の専用ページテンプレート。カード額面、残高、QR コード、カードコードをコピー可能な形式で表示し、ギフトカード購入者が受け取ったカード情報を確認・管理できる。

用途
顧客がメール内のリンクをクリックしてギフトカードを受け取るとき。カード額面と残高を確認し、カードコードをコピーして保存する。
設置場所
templates/gift_card.liquid に設置。Shopify がギフトカード受け取りメール内の URL から自動的にこのテンプレートを読み込む。theme.liquid の代わりに `{% layout none %}` で独立した HTML を出力する。
注意点
Storefront API ではギフトカード情報が取得できないため、このテンプレートは admin API データをベースに Liquid で直接レンダリングされる仕様。QR コード生成に Shopify 提供の vendor/qrcode.js を使うため、asset URL が正しく解決されていることを確認する。settings.currency_code_enabled が有効な場合は通貨コード付き金額(例: ¥10,000 JPY)、無効な場合は通貨記号のみ(例: ¥10,000)で表示される。expired や enabled == false 判定でカード失効時の「Expired」バッジを出すため、ギフトカード有効期限の設定がなければバッジは表示されない。
タグ:gift-cardtemplateqr-codecheckoutcustomer

コード

204 行 / liquid
{% comment %} Unfortunataly gift cards are not supported yet via the Storefront API {% endcomment %}
{% comment %} Based on: https://github.com/Shopify/dawn/blob/v7.0.1/templates/gift_card.liquid {% endcomment %}
{% layout none %}

<!doctype html>
<html lang="{{ request.locale.iso_code }}">
  <head>
    <script src="{{ 'vendor/qrcode.js' | shopify_asset_url }}" defer></script>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="theme-color" content="{{ settings.color_background }}">
    <link rel="canonical" href="{{ canonical_url }}">
    <link rel="preconnect" href="https://cdn.shopify.com" crossorigin>

    {%- if settings.favicon != blank -%}
      <link rel="icon" type="image/png" href="{{ settings.favicon | image_url: width: 32, height: 32 }}">
    {%- endif -%}

    {%- unless settings.type_header_font.system? -%}
      <link rel="preconnect" href="https://fonts.shopifycdn.com" crossorigin>
    {%- endunless -%}

    {%- assign formatted_initial_value = gift_card.initial_value | money_without_trailing_zeros | strip_html -%}

    <title>{{ "Here's your {{ value }} gift card for {{ shop }}!" | t: value: formatted_initial_value, shop: shop.name }}</title>

    <meta name="description" content="Your gift card">

    {{ content_for_header }}

    {%- liquid
      assign body_font_bold = settings.type_body_font | font_modify: 'weight', 'bold'
      assign body_font_italic = settings.type_body_font | font_modify: 'style', 'italic'
      assign body_font_bold_italic = body_font_bold | font_modify: 'style', 'italic'
    %}

    {% style %}
      {{ settings.type_body_font | font_face: font_display: 'swap' }}
      {{ body_font_bold | font_face: font_display: 'swap' }}
      {{ body_font_italic | font_face: font_display: 'swap' }}
      {{ body_font_bold_italic | font_face: font_display: 'swap' }}
      {{ settings.type_header_font | font_face: font_display: 'swap' }}

      :root {
        --font-body-family: {{ settings.type_body_font.family }}, {{ settings.type_body_font.fallback_families }};
        --font-body-style: {{ settings.type_body_font.style }};
        --font-body-weight: {{ settings.type_body_font.weight }};

        --font-heading-family: {{ settings.type_header_font.family }}, {{ settings.type_header_font.fallback_families }};
        --font-heading-style: {{ settings.type_header_font.style }};
        --font-heading-weight: {{ settings.type_header_font.weight }};

        --font-body-scale: {{ settings.body_scale | divided_by: 100.0 }};
        --font-heading-scale: {{ settings.heading_scale | times: 1.0 | divided_by: settings.body_scale }};

        --color-base-text: {{ settings.colors_text.red }}, {{ settings.colors_text.green }}, {{ settings.colors_text.blue }};
        --color-shadow: {{ settings.colors_text.red }}, {{ settings.colors_text.green }}, {{ settings.colors_text.blue }};
        --color-base-background-1: {{ settings.colors_background_1.red }}, {{ settings.colors_background_1.green }}, {{ settings.colors_background_1.blue }};
        --color-base-background-2: {{ settings.colors_background_2.red }}, {{ settings.colors_background_2.green }}, {{ settings.colors_background_2.blue }};
        --color-base-solid-button-labels: {{ settings.colors_solid_button_labels.red }}, {{ settings.colors_solid_button_labels.green }}, {{ settings.colors_solid_button_labels.blue }};
        --color-base-outline-button-labels: {{ settings.colors_outline_button_labels.red }}, {{ settings.colors_outline_button_labels.green }}, {{ settings.colors_outline_button_labels.blue }};
        --color-base-accent-1: {{ settings.colors_accent_1.red }}, {{ settings.colors_accent_1.green }}, {{ settings.colors_accent_1.blue }};
        --color-base-accent-2: {{ settings.colors_accent_2.red }}, {{ settings.colors_accent_2.green }}, {{ settings.colors_accent_2.blue }};

        --gradient-base-background-1: {% if settings.gradient_background_1 != blank %}{{ settings.gradient_background_1 }}{% else %}{{ settings.colors_background_1 }}{% endif %};

        --page-width: {{ settings.page_width | divided_by: 10 }}rem;
        --page-width-margin: {% if settings.page_width == '1600' %}2{% else %}0{% endif %}rem;

        --buttons-radius: {{ settings.buttons_radius }}px;
        --buttons-border-width: {% if settings.buttons_border_opacity > 0 %}{{ settings.buttons_border_thickness }}{% else %}0{% endif %}px;
        --buttons-border-opacity: {{ settings.buttons_border_opacity | divided_by: 100.0 }};
        --buttons-shadow-opacity: {{ settings.buttons_shadow_opacity | divided_by: 100.0 }};
        --buttons-shadow-horizontal-offset: {{ settings.buttons_shadow_horizontal_offset }}px;
        --buttons-shadow-vertical-offset: {{ settings.buttons_shadow_vertical_offset }}px;
        --buttons-shadow-blur-radius: {{ settings.buttons_shadow_blur }}px;
        --buttons-border-offset: {% if settings.buttons_radius > 0 or settings.buttons_shadow_opacity > 0 %}0.3{% else %}0{% endif %}px;

        --inputs-radius: {{ settings.inputs_radius }}px;
        --inputs-border-width: {{ settings.inputs_border_thickness }}px;
        --inputs-border-opacity: {{ settings.inputs_border_opacity | divided_by: 100.0 }};
        --inputs-shadow-opacity: {{ settings.inputs_shadow_opacity | divided_by: 100.0 }};
        --inputs-shadow-horizontal-offset: {{ settings.inputs_shadow_horizontal_offset }}px;
        --inputs-shadow-vertical-offset: {{ settings.inputs_shadow_vertical_offset }}px;
        --inputs-shadow-blur-radius: {{ settings.inputs_shadow_blur }}px;
      }
    {% endstyle %}

    {%- unless settings.type_body_font.system? -%}
      <link rel="preload" as="font" href="{{ settings.type_body_font | font_url }}" type="font/woff2" crossorigin>
    {%- endunless -%}
    {%- unless settings.type_header_font.system? -%}
      <link rel="preload" as="font" href="{{ settings.type_header_font | font_url }}" type="font/woff2" crossorigin>
    {%- endunless -%}

    {{ 'template-giftcard.css' | asset_url | stylesheet_tag }}
  </head>

  <body class="gradient">
    <header class="gift-card__title">
      <span class="h2">{{ shop.name }}</span>
      <h1 class="gift-card__heading">Your gift card</h1>
      <div class="gift-card__price">
        <p>
          {% if settings.currency_code_enabled %}
            {{ gift_card.initial_value | money_with_currency }}
          {% else %}
            {{ gift_card.initial_value | money }}
          {% endif %}
        </p>
        {%- if gift_card.enabled == false or gift_card.expired -%}
          <p class="gift-card__label badge badge--background-1">Expired</p>
        {%- endif -%}
      </div>

      {% if settings.currency_code_enabled %}
        {%- assign gift_card_balance = gift_card.balance | money_with_currency -%}
      {% else %}
        {%- assign gift_card_balance = gift_card.balance | money -%}
      {% endif %}
      {%- if gift_card.balance != gift_card.initial_value -%}
        <p class="gift-card__label caption-large">{{ "Remaining {{ balance }}" | t: balance: gift_card_balance }}</p>
      {%- endif -%}
    </header>

    <main class="gift-card">
      <div class="gift-card__image-wrapper">
        <img src="{{ 'gift-card/card.svg' | shopify_asset_url }}" alt="" class="gift-card__image" height="{{ 570 | divided_by: 1.5 }}" width="570" loading="lazy">
      </div>
      <div class="gift-card__qr-code" data-identifier="{{ gift_card.qr_identifier }}"></div>
      <div class="gift-card__information">
        <input
          type="text"
          class="gift-card__number"
          value="{{ gift_card.code | format_code }}"
          aria-label="Gift card code"
          readonly
        >
        <div class="gift-card__copy-code">
          <button class="link gift-card__copy-link">Copy code</button>
          <span class="gift-card__copy-success form__message" role="status"></span>
          <template>
            {%- render 'icon-success' -%}Code copied successfully
          </template>
        </div>
        {%- if gift_card.pass_url -%}
          <a href="{{ gift_card.pass_url }}" class="gift_card__apple-wallet">
            <img src="{{ 'gift-card/add-to-apple-wallet.svg' | shopify_asset_url }}" width="120" height="40" alt="{{ 'gift_cards.issued.add_to_apple_wallet' | t }}" loading="lazy">
          </a>
        {%- endif -%}
        <div class="gift-card__buttons no-print">
          <a
            href="{{ shop.url }}"
            class="button"
            target="_blank"
            rel="noopener"
            aria-describedby="a11y-new-window-message"
          >
            Continue shopping
          </a>
          <button
            class="button button--secondary"
            onclick="window.print();"
          >
            Print
          </button>
        </div>
      </div>
    </main>

    <div hidden>
      <span id="a11y-new-window-message">Opens in a new window.</span>
    </div>
  </body>
</html>

<script>
  var string = { qrImageAlt: "QR code — scan to redeem gift card" };
  document.addEventListener('DOMContentLoaded', function() {
   new QRCode( document.querySelector('.gift-card__qr-code'), {
    text: document.querySelector('.gift-card__qr-code').dataset.identifier,
    width: 120,
    height: 120,
    imageAltText: string.qrImageAlt
    });
  });

  var template = document.getElementsByTagName("template")[0];
  var clonedTemplate = template.content.cloneNode(true);

  var isMessageDisplayed = false
  document
  .querySelector('.gift-card__copy-link')
  .addEventListener('click', () => {
    navigator.clipboard.writeText(document.querySelector('.gift-card__number').value).then(function () {
      if (!isMessageDisplayed) {
        document.querySelector('.gift-card__copy-success').appendChild(clonedTemplate);
        isMessageDisplayed = true
      }
    });
  });
</script>

出典・ライセンス

License:
MIT

このコードは instantcommerce 著作の MIT ライセンスソースです。 原本の著作権は instantcommerce が保有します。日本語訳は ALSEL によるものです。

関連項目

📄 ページテンプレート初級

ページテンプレート

Shopify の汎用ページテンプレート。ページのタイトルと本文コンテンツを Shopify 管理画面から取得して表示する基本的な構造。

📁 liquid·MIT·5
📄 ページテンプレート初級

リダイレクト

ヘッドレステーマで Shopify ストアフロントへのリダイレクト機能を提供するセクション。管理画面に「リダイレクト」という名称で登場し、ページから別 URL への誘導を実装する基盤となる。

📁 shopify-headless-theme·MIT·7
📄 ページテンプレート初級

トップページの基本テンプレート

Shopify Liquid の最小限のテンプレート例。HTML に日付変数を埋め込み、商品ページへのリンクを配置する基本構造を示す。

📁 liquid·MIT·7
📄 ページテンプレート初級

固定ページのテンプレート

Shopify 管理画面で作成した固定ページ(利用規約、プライバシーポリシーなど)を表示するテンプレート。ページタイトルと本文コンテンツを描画する。

📁 mcliquid-theme·MIT·8
📄 ページテンプレート初級

ページの基本テンプレート

固定ページ(About など)の基本レイアウト。ページタイトルとコンテンツを HTML の article 要素でマークアップし、管理画面で編集したテキストを表示する。

📁 liquid·MIT·9
📄 ページテンプレート初級

固定ページのテンプレート

固定ページ(ポリシー、会社概要、利用規約など)の基本レイアウト。ページタイトルと本文を中央揃えで表示する。

📁 ks-bootshop·MIT·10