<aside> 💡

お客様アカウントを「新しいお客様アカウント」に設定されているお客様は、【新しいお客様アカウント】退会ボタンのテーマへの導入方法をご確認ください。

</aside>

スクリーンショット 2024-09-10 22.23.08.png

テーマブロックでの追加(推奨)

テーマエディタでアカウントページにテーマアプリ拡張機能を追加するには、以下の手順に従ってください:

必要に応じてブロックの設定からCSSを調整してください。

コードでの追加(アプリブロックで追加できない場合のみ)

コードでアカウントページに退会ボタンを追加するには、以下のコード例をご利用ください。

※アカウントページ(例:sections/main-account.liquid)への実装を想定しています。当アプリのアプリプロキシ(/apps/customer/ がストアから届く設定)が有効であることが前提です。

【退会アンケート】次のコード例では、GET /apps/customer/cancellation-survey-config のレスポンス(enabled / choices / freeTextEnabled / required)だけを使い、チェックボックスの選択肢ラベルはサーバーが返す choices 配列から自動生成します(テーマ側で固定列挙しません)。退会時は POST /apps/customer/disactivatesurvey_choices と任意で survey_free_text を付与します。表示文言はコード内の S を編集して調整できます。

※2026年6月更新:退会アンケート機能に対応した最新のコードに差し替えました。以前掲載していた旧コードをご利用の場合、退会アンケートを「オン(必須)」に設定すると退会時にエラーとなり退会処理が実行されません。お手数ですが以下の最新のコードへの差し替えをお願いいたします。

コード例(以下のコードをアカウントページの任意の場所に追加してください。スタイルなどは自由に変更可能です。):

<style>
  #au-confirm-popup {
    display: none;
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background-color: white;
    padding: 24px;
    border: 1px solid black;
    border-radius: 5px;
    z-index: 1000;
    text-align: left;
    width: min(420px, 92vw);
    max-height: 85vh;
    overflow-y: auto;
    font-size: 1rem;
    box-sizing: border-box;
  }
  #au-confirm-popup button {
    font-size: 15px;
    padding: 8px 16px;
    margin: 6px 6px 0 0;
    cursor: pointer;
    display: inline-block;
  }
  #au-confirm-popup .au-survey-reasons label {
    display: block;
    margin: 8px 0;
    cursor: pointer;
    font-size: 0.95rem;
  }
  #au-confirm-popup textarea.au-survey-freetext {
    width: 100%;
    min-height: 88px;
    margin-top: 10px;
    font-size: 0.95rem;
    box-sizing: border-box;
    padding: 8px;
  }
  #au-popup-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0,0,0,0.5);
    z-index: 999;
  }
  </style>

  <div id="au-customer-disactivate">
    {% if customer %}
      <p class="customer"><a href="#" onclick="auOpenDisactivateFlow(); return false;">退会する</a></p>
    {% else %}
      <p class="customer">
        <a href="#" onclick="alert('退会するためにはログインしてください'); window.location.href = '/account/login'; return false;">
          退会する
        </a>
      </p>
    {% endif %}
  </div>

  <div id="au-confirm-popup" role="dialog" aria-modal="true"></div>
  <div id="au-popup-overlay" onclick="auClosePopup()"></div>

  {% if customer %}
  <script>
  (function () {
    const AU_CUSTOMER_ID = {{ customer.id | json }};
    const S = {
      intro: "退会の理由をお選びください(任意)。",
      next: "次へ",
      freeLabel: "自由記述",
      cancel: "キャンセル",
      confirm: "本当に退会しますか?",
      yes: "はい",
      no: "いいえ",
      ok: "退会処理が完了しました。",
      err: "退会処理に失敗しました",
      needSurvey: "退会アンケートの回答を入力してください。",
    };

    const byId = (id) => document.getElementById(id);
    const esc = (s) =>
      String(s ?? '')
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;');
    const escAttr = (s) => String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;');

    const norm = (data) => {
      const o = data && typeof data === 'object' ? data : {};
      const choices = Array.isArray(o.choices)
        ? o.choices.map((c) => String(c).trim()).filter(Boolean)
        : [];
      return {
        enabled: !!o.enabled,
        choices,
        freeTextEnabled: o.freeTextEnabled !== false,
        required: !!o.required,
      };
    };

    let config = norm(null);
    let picked = [];
    let freeText = '';

    const show = (html) => {
      byId('au-confirm-popup').innerHTML = html;
      byId('au-confirm-popup').style.display = 'block';
      byId('au-popup-overlay').style.display = 'block';
    };

    window.auClosePopup = () => {
      byId('au-confirm-popup').style.display = 'none';
      byId('au-popup-overlay').style.display = 'none';
    };

    const useSurvey = (c) => c.enabled && (c.choices.length > 0 || c.freeTextEnabled);

    const renderSurvey = () => {
      let h = '<p>' + esc(S.intro) + '</p><div class="au-survey-reasons">';
      for (const c of config.choices) {
        const on = picked.includes(c) ? ' checked' : '';
        h += '<label><input type="checkbox" name="au-survey-reason" value="' + escAttr(c) + '"' + on + '> ' + esc(c) + '</label>';
      }
      h += '</div>';
      if (config.freeTextEnabled) {
        h += '<label for="au-survey-freetext">' + esc(S.freeLabel) + '</label>';
        h += '<textarea id="au-survey-freetext" class="au-survey-freetext" maxlength="2000">' + esc(freeText) + '</textarea>';
      }
      h += '<div style="margin-top:14px;"><button type="button" onclick="auSurveyNext()">' + esc(S.next) + '</button> ';
      h += '<button type="button" onclick="auClosePopup()">' + esc(S.cancel) + '</button></div>';
      show(h);
    };

    const renderConfirm = () => {
      let h = '<p>' + esc(S.confirm) + '</p><div style="margin-top:14px;">';
      h += '<button type="button" onclick="auDisactivate()">' + esc(S.yes) + '</button> ';
      h += '<button type="button" onclick="auConfirmBack()">' + esc(S.no) + '</button></div>';
      show(h);
    };

    window.auSurveyNext = () => {
      picked = Array.from(document.querySelectorAll('#au-confirm-popup input[name="au-survey-reason"]:checked')).map((b) => b.value);
      const ta = byId('au-survey-freetext');
      freeText = ta ? ta.value.trim() : '';
      if (config.required) {
        const hasCh = config.choices.length > 0;
        const hasFt = config.freeTextEnabled;
        const ok =
          (!hasCh && !hasFt) ||
          (hasCh && hasFt && (picked.length > 0 || freeText.length > 0)) ||
          (hasCh && !hasFt && picked.length > 0) ||
          (!hasCh && hasFt && freeText.length > 0);
        if (!ok) {
          alert(S.needSurvey);
          return;
        }
      }
      renderConfirm();
    };

    window.auConfirmBack = () => (useSurvey(config) ? renderSurvey() : auClosePopup());

    window.auOpenDisactivateFlow = async () => {
      picked = [];
      freeText = '';
      try {
        const r = await fetch('/apps/customer/cancellation-survey-config');
        config = norm(r.ok ? await r.json() : {});
      } catch {
        config = norm(null);
      }
      useSurvey(config) ? renderSurvey() : renderConfirm();
    };

    window.auDisactivate = async () => {
      if (AU_CUSTOMER_ID == null) return;
      const body = {
        customer_id: AU_CUSTOMER_ID,
        survey_choices: picked.length ? picked : undefined,
        survey_free_text: freeText || undefined,
      };
      try {
        const r = await fetch('/apps/customer/disactivate', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(body),
        });
        if (r.status !== 200) throw new Error(S.err);
        alert(S.ok);
        window.location.href = '/account/logout';
      } catch (e) {
        alert(S.err);
        console.error("エラーが発生しました:", e);
      }
    };
  })();
  </script>
  {% endif %}