Shopify公式チュートリアル「Add a survey to Thank you and Order status pages」を試してみた【前編】

Shopifyのカスタムアプリ開発について、「なんだか難しそう」「どこから手を付ければいいんだろう」と感じたことはありませんか?

Shopifyの公式ドキュメントにはアプリ開発にあたってとても参考になるチュートリアルがたくさん用意されています。しかし、これからカスタムアプリ構築を始める人にとっては、少しハードルが高く感じられるかもしれません。

そこで本記事では、App extensionsを使用するカスタムアプリ開発の基礎的な部分に着目し、内容を掘り下げながら公式チュートリアルを解説してみようと思います。

本記事は2部構成となります。それぞれで解説している内容は以下のとおりです。

<前編>

  • あらかじめ作成しておいたアプリにextensionを追加(+サンプルコードを適用して実装)

<後編>

近日公開予定

  • 開発サーバーで動作確認
  • デプロイ
  • 開発ストアにインストール
  • チェックアウトエディタでextensionを設置
  • ストアフロントで動作確認

チュートリアルの流れをベースに補足を交えて進めますので、手を動かしたりお手元のコードと見比べたりしながら読み進めていただけます。

実装にあたって

使用するチュートリアルについて

今回は、サンキューページ・注文確認ページにアンケートを表示するCheckout UI extensionを作成するチュートリアルを使用します。

URL:Add a survey to the thank you and order status pages

このチュートリアルでは、チェックアウト内の設置場所によって、以下の2つの異なるアンケートを表示するCheckout UI extensionを作成できます。

  • Thank you pageに設置した場合、「どのようにしてストアを知ったか」を尋ねるアンケートを表示。
  • Order status pageに設置した場合、「購入した商品をどのように楽しんでいるか」を尋ねるアンケートを表示。

このチュートリアルで学べること

  • Thank you pageとOrder status pageで機能するCheckout UI extensionを作成する
  • 1つのカスタムアプリで複数のextension targetを使用する
  • アンケートのUIコンポーネントを作成する
  • 開発サーバー(ローカル)でextensionをビルドし、テストする
  • extensionをShopifyにデプロイする
  • デプロイしたアプリを、開発ストアにインストールする
  • チェックアウトエディタでextensionを設定する
  • ストアフロントで動作確認する

実装の前提

今回の実装は、以下で進めました。

  • Shopify CLI:3.74.1
  • 開発言語:JavaScript React
  • OS:Mac OS 14.6.1

また、チュートリアルを始める前に以下の準備が必要です。

  • 「Shopifyパートナーアカウント」の作成
  • Shopify CLIが使える環境の構築
  • 開発ストア(Checkout and Customer Accounts Extensibility プレビュー が有効)の作成
  • アプリ(Shopify CLIを使って、extension-onlyテンプレート使用)の作成
    • まだの場合は、こちらに沿って作成してください

準備が整ったら、extensionを作成しましょう!

1. Checkout UI extensionを作成

まずはじめに、事前に作成しておいたアプリにextensionを作成します。

① extensionを作成する

ShopifyCLIでextensionを構築するためのスターターコードを実行し、Checkout UI extensionを作成します。

1 . ターミナルで、アプリのルートディレクトリに移動します。
(今回使うアプリのディレクトリ名:rewiredcloud-extension-demoapp)

 cd rewiredcloud-extension-demoapp

※ディレクトリ名はお使いの環境に合わせて変更してください。

2. ShopifyCLI コマンドを実行して、新しいextensionを作成します。

shopify app generate extension

3 . 作成するextensionについて、プロンプトで表示されるいくつかの質問に答えます。 今回はチュートリアル通りに実装したいので、以下のようにします:

  • extensionのタイプ:Checkout UI
  • extension名:order-status-thank-you
  • 実装する言語:JavaScript React
Type of extension?                                                      
> Checkout UI                                                            
  Post-purchase UI                                                       
  Cart and checkout validation - Function                                
  Cart transformer - Function                                            
  Delivery customization - Function                                      
  Discount — Function                                                    
  Discount orders - Function                                             
	...
?  Type of extension?
✔  Checkout UI

?  Name your extension:
✔  order-status-thank-you

?  What would you like to work in?
✔  JavaScript React

質問に答えると、Shopify CLIが必要なファイルを自動生成してくれます!これで、作成したCheckout UI extensionが/extensions/order-status-thank-you に追加されました。

/extensions/order-status-thank-you/src を見てみると、スクリプトファイルである Checkout.jsx が含まれていることがわかります。スクリプトファイルとは「作成したextensionのUIコンポーネントとロジックが定義されている主要ファイル」のことで、今回は後の工程で編集します。

なお、今回はこの段階で、チュートリアルに記載されているサンプルコードを各ファイルに適用しておきます。

1. /extensions/order-status-thank-you/src/Checkout.jsx

チュートリアルページでサンプルコードをコピーして、コードエディタで全コードを置き換えて保存します。

2. /extensions/order-status-thank-you/shopify.extension.toml

チュートリアルページでサンプルコードをコピーして、コードエディタで全コードを置き換えて保存します。

2. extensionのtargetを設定する

(手順1でサンプルコード記述済みのため、作業は不要です。実装内容を確認しましょう。)

作成した Checkout UI extensionのtargetを設定します。Checkout UI extensionのtargetは、「チェックアウトフローでextensionがレンダリングされる場所」をコントロールします。

今回開発しているのは「Thank you pageとOrder Status Pageにアンケート表示させるカスタムアプリ」なので、アンケートを表示させたい場所(=extensionをレンダリングさせたい場所)が2箇所あります。

今回のような場合は、1つのextensionに複数のtargetを追加することで、各targetごとに異なるコンポーネントを表示することが可能です。

◆ 今回使用したいtarget

extensionを
レンダリングさせたいページ
targettargetの特徴
Thank you pagepurchase.thank-you.block.renderThank you pageにのみ表示されるblock extension target*
Order status pagecustomer-account.order-status.block.renderOrder status pageに表示されるblock extension target

* block extension target:マーチャントがチェックアウトエディタを使用して設定した場所にレンダリングされるtarget

※ 各ページの特徴

  • Thank you page:すべてのチェックアウトの最後に表示される「ありがとう」ページ
  • Order status page:購入者がThank you pageを2回目以降に訪れた場合や、マイページからアクセスした場合に表示される「注文状況」ページ

それではtargetを設定に進みましょう。extensionの「スクリプトファイル」と「設定ファイル」で、それぞれ必要な設定を行います。

① スクリプトファイルからtargetをエクスポートする

まず、スクリプトファイル(script file)で各targetのエントリーポイントをexportします。

前述のように、スクリプトファイルは「作成したextensionのUIコンポーネントとロジックが定義されている主要なファイル」のことでした。今回は /extensions/order-status-thank-you/src/ Checkout.jsx が該当します。

このスクリプトファイルに「各targetごとのエントリーポイント」を作成して、それをnamed exportします。エントリーポイントとは「ここに何かを表示します!の目印」で、今回のサンプルコードでは以下のようになっています

extensionを
表示させたいページ
target表示する
コンポーネント
エントリー
ポイント名
Thank you pagepurchase.thank-you.block.renderAttribution
コンポーネント
thankYouBlock
Order status pagecustomer-account.order-status.block.renderProductReview
コンポーネント
orderDetailsBlock

Checkout.jsx に、以下の記述があることを確認します。

const thankYouBlock = reactExtension("purchase.thank-you.block.render", () => <Attribution />);
export { thankYouBlock };

const orderDetailsBlock = reactExtension("customer-account.order-status.block.render", () => <ProductReview />);
export { orderDetailsBlock };

Thank you page用とOrder status page用に、各エントリーポイントがexportされています。

設定ファイルからtargetを参照する

次に、先ほどexportしたエントリーポイントを設定ファイルから参照します。

設定ファイル(configuration file)は「extensionの基本的な情報と設定を含むファイル」のことで、作成するextensionに関わらず /extensions/ shopify.extension.toml が該当します。このファイルに以下の情報を含む「[[extensions.targeting]]セクション」を記述することで、①でexportされたtargetを参照できるようになります。

  • target (Shopifyにコードを差し込む場所を指定する識別子。スクリプトファイルからexportしたターゲットと一致する必要があります)
  • module( スクリプトファイルへのパス)
  • export (「スクリプトファイルでexportした名前」と一致する必要があります)

また、このセクションを複数記述することでそれぞれのtargetを定義でき、チェックアウトフローの複数の場所にextenisonを追加できるようになります。

shopify.extension.toml に、以下の記述があることを確認します。

[[extensions.targeting]]
target = "purchase.thank-you.block.render"
module = "./src/Checkout.jsx"
export = "thankYouBlock"

[[extensions.targeting]]
target = "customer-account.order-status.block.render"
module = "./src/Checkout.jsx"
export = "orderDetailsBlock"

各target分の[[extensions.targeting]]セクションが記述されています。

③ ①と②が一致していることを確認する

最後に、各targetについて「①(スクリプトファイルCheckout.jsxでexportしたtarget)と②(設定ファイルshopify.extension.tomlで参照しているtarget)が一致していること」を確認します。

一致しています!

設定ファイル shopify.extension.toml を編集した場合はその変更を有効にするために開発サーバーを再起動する必要がありますので、必要に応じて以下を実行します。(今回は後の工程で実行しても問題ありません)

1 . ターミナルで、アプリのルートディレクトリに移動します。

 cd rewiredcloud-extension-demoapp

ディレクトリ名はお使いの環境に合わせて変更してください。

2 . 続けてShopifyCLIコマンドを実行して、開発サーバーを起動します。

shopify app dev

3 . 「プレビューに使用するストア」を尋ねる以下のプロンプトが表示された場合は、開発ストア一覧の中から任意のストアを選んで決定します。

Which store would you like to use to view your project?

4 . 開発サーバーが正常に起動すると、ターミナルに以下のような内容が表示されます。

5. これで、設定ファイルの変更が有効になりました。 (このままプレビュー等をしない場合は、キーボードの q を押して開発サーバーを終了して構いません。)

3. アンケートのUIを構築する

(手順1でサンプルコード記述済みのため、作業は不要です。実装内容を確認しましょう。)

targetの設定が済んだので、Checkout UI extensionsコンポーネントライブラリのコンポーネントを使用して、extensionで表示させたいアンケートのUIを構築します。

今回のextesnionでは、以下3つのコンポーネントを Checkout.jsx に実装します。

  1. 2つのアンケートで共用される「Surveyコンポーネント」
  2. Thank you pageのアンケートに使われる「Attributionコンポーネント」
  3. Order status pageのアンケートに使われる「Product Reviewコンポーネント」

それでは順番に見ていきましょう。

① [Thank you page・Order status page 共用] Surveyコンポーネントを作成する

はじめに、「Survey コンポーネント」を作成します。このコンポーネントは、2つのアンケートで共用されるCheckout UI コンポーネント(フィードバックを送信するボタン・フィードバック送信後に表示されるメッセージなど)を設定するものです。

Checkout UI extensionのコンポーネントはセキュリティ上の理由から「Shopifyが公開する特定のUIコンポーネントのみ使用可能」と制限されているので、その中から必要なものを選んで使います。(APIバージョンによっても使用できるコンポーネントが異なるため、使用したいバージョンに合わせて公式ドキュメントを確認して選びましょう。今回は2024-07バージョンを使用します。)

今回はこれらのcheckout UI コンポーネントを使用します:BlockStack , View , Heading , Text , Button

Checkout.jsx に、以下の記述があることを確認します。

1 . 必要なcheckout UI コンポーネントがファイル冒頭でimportされていること

import {
  reactExtension,
  BlockStack,
  View,
  Heading,
  Text,
  ChoiceList,
  Choice,
  Button,
  useStorage,
} from '@shopify/ui-extensions-react/checkout';

2 . import済のcheckout UI コンポーネントが、同ファイル内の Surveyコンポーネント で使用されていること

function Survey({
  title,
  description,
  onSubmit,
  children,
  loading,
}) {
  const [submitted, setSubmitted] = useState(false);

  async function handleSubmit() {
    await onSubmit();
    setSubmitted(true);
  }

  if (submitted) {
    return (
      <View border="base" padding="base" borderRadius="base">
        <BlockStack>
          <Heading>Thanks for your feedback!</Heading>
          <Text>Your response has been submitted</Text>
        </BlockStack>
      </View>
    );
  }

  return (
    <View border="base" padding="base" borderRadius="base">
      <BlockStack>
        <Heading>{title}</Heading>
        <Text>{description}</Text>
        {children}
        <Button kind="secondary" onPress={handleSubmit} loading={loading}>
          Submit feedback
        </Button>
      </BlockStack>
    </View>
  );
}

② [Thank you page用] Attributionアンケートを作成する

次に、作成済のSurveyコンポーネントとCheckout UI コンポーネントを使用して、Attribution コンポーネントを作成します。このコンポーネントは Thank you pageに表示するAttributionアンケート用のインターフェイスです。

「顧客がどのようにしてその店を知ったか」を選択するためのChoiceListも含めて、「Shopifyが公開する特定のUIコンポーネント」の中からこれらを使用します:BlockStack , ChoiceList , ChoiceChoiceList内のオプション)

なお、 サンプルコードでは、このアンケートは以下のような仕様になっています:

  • アンケート回答の保存:コンソールにログを記録してアンケートの回答を保存する
  • アンケート内容:アンケート内容と選択肢がハードコーディングされている

いずれもシミュレーション用の簡易的な仕様のため、本番用のアプリでは仕様を変更する必要があります。

Checkout.jsx に、以下の記述があることを確認します。

1 . 必要なcheckout UI コンポーネントがファイル冒頭でimportされていること

import {
  reactExtension,
  BlockStack,
  View,
  Heading,
  Text,
  ChoiceList,
  Choice,
  Button,
  useStorage,
} from '@shopify/ui-extensions-react/checkout';

2 . Attribution コンポーネントで、①で作成したSurveyコンポーネントとimport済のcheckout UI コンポーネントが使用されていること

function Attribution() {
  const [attribution, setAttribution] = useState('');
  const [loading, setLoading] = useState(false);
  // Store into local storage if the attribution survey was completed by the customer.
  const [attributionSubmitted, setAttributionSubmitted] = useStorageState('attribution-submitted')

  async function handleSubmit() {
    // Simulate a server request
    setLoading(true);
    return new Promise((resolve) => {
      setTimeout(() => {
      // Send the review to the server
      console.log('Submitted:', attribution);
      setLoading(false);
      setAttributionSubmitted(true)
      resolve();
    }, 750)});
  }

  // Hides the survey if the attribution has already been submitted
  if (attributionSubmitted.loading || attributionSubmitted.data === true) {
    return null;
  }

  return (
    <Survey title="How did you hear about us ?" onSubmit={handleSubmit} loading={loading}>
      <ChoiceList
        name="sale-attribution"
        value={attribution}
        onChange={setAttribution}
      >
        <BlockStack>
          <Choice id="tv">TV</Choice>
          <Choice id="podcast">Podcast</Choice>
          <Choice id="family">From a friend or family member</Choice>
          <Choice id="tiktok">Tiktok</Choice>
        </BlockStack>
      </ChoiceList>
    </Survey>
  );
}

② [Order status page用] Product Reviewアンケートを作成する

最後に、Surveyコンポーネントとcheckout UI コンポーネントを使用して、ProductReviewコンポーネントを作成します。このコンポーネントは Order status pageに表示するProdect reviewアンケート用のインターフェイスです。

「顧客がどのように購入を楽しんでいるか」を指定するためのChoiceListも含めて、「Shopifyが公開する特定のUIコンポーネント」の中からこれらを使用します:

  • BlockStackコンポーネント(要素を垂直に積み重ねる)
  • ChoiceList コンポーネント(選択肢のリスト)
  • Choice コンポーネント(ChoiceList内のオプション。)

サンプルコードでは回答の保存等の仕様がAttributionアンケートと同様になっているため、本番用のアプリでは仕様を変更する必要があります。

Checkout.jsx に、以下の記述があることを確認します。

1 . 必要なcheckout UI コンポーネントがファイル冒頭でimportされていること

import {
  reactExtension,
  BlockStack,
  View,
  Heading,
  Text,
  ChoiceList,
  Choice,
  Button,
  useStorage,
} from '@shopify/ui-extensions-react/checkout';

2 . ProductReviewコンポーネントで、①で作成したSurveyコンポーネントとimport済のcheckout UI コンポーネントが使用されていること

function ProductReview() {
  const [productReview, setProductReview] = useState('');
  const [loading, setLoading] = useState(false);
  // Store into local storage if the product was reviewed by the customer.
  const [productReviewed, setProductReviewed] = useStorageState('product-reviewed')

  async function handleSubmit() {
    // Simulate a server request
    setLoading(true);
    return new Promise((resolve) => {
      setTimeout(() => {
      // Send the review to the server
      console.log('Submitted:', productReview);
      setLoading(false);
      setProductReviewed(true);
      resolve();
    }, 750)});
  }

  // Hides the survey if the product has already been reviewed
  if (productReviewed.loading || productReviewed.data) {
    return null;
  }

  return (
    <Survey
      title="How do you like your purchase?"
      description="We would like to learn if you are enjoying your purchase."
      onSubmit={handleSubmit}
      loading={loading}
    >
      <ChoiceList
        name="product-review"
        value={productReview}
        onChange={setProductReview}
      >
        <BlockStack>
          <Choice id="5">Amazing! Very happy with it.</Choice>
          <Choice id="4">It's okay, I expected more.</Choice>
          <Choice id="3">Eh. There are better options out there.</Choice>
          <Choice id="2">I regret the purchase.</Choice>
        </BlockStack>
      </ChoiceList>
    </Survey>
  );
}

これで、アンケートのUIに必要な3つのコンポーネントが作成されていることを確認できました!ここまでで「targetの設定」と「UIの構築」を含むCheckout UI extensionを構築できました。

次回の後編では、構築したextensionを開発サーバーでプレビューするところから始め、Shopifyへのデプロイ・ストアへのインストールと動作確認までを行います。