Shopify Fuctions APIを使用した自動割引Extension開発方法(廃止された割引APIの移行)

Shopify Fuctions APIを使用した自動割引Extension開発方法(廃止された割引APIの移行)

今回は、ShopifyのFuctions APIを使用した割引の拡張機能(Discount Function Extension)の作成について紹介します!

また、最近API自体が大幅に変わったこともあり、廃止された割引API から新しい割引API に移行する方法も併せてのご紹介となります。

前提

  • Shopifyアプリが構築されていること
    • Functions拡張機能はshopify app createで作成したアプリのディレクトリ構成内に組み込まれるため
    • 作成したFunction ExtensionをShopに適用するには、アプリを通じてインストールおよび設定する必要があるため

ディレクトリ構成的には以下のようになります。

my-shopify-app/
├── extensions/
│   └── my-discount-function/
│       ├── shopify.extension.toml
│       └── src/
│           └── function.ts
├── shopify.app.toml

必要な開発ツールと構成

構成要素

説明

Shopifyアプリ

Functions Extensionを含むメインコンテナをCLIで作成

Functions Extension

discounts タイプのExtensionを定義

Shopify CLI

開発・デプロイ・登録すべてに必須

Partnerアカウント

アプリの作成・開発に必要

開発用Shop

開発したアプリをテストするための環境

廃止された割引APIから新しい割引APIに移行する

https://shopify.dev/docs/apps/build/discounts/migrate-discount-api?extension=javascript&appType=graphql

結論から言うと、ドキュメントには移行と書かれていますが、ちょっと書き換えるような移行ではなく、Extension自体を作り直すと言うのが正しい解釈です。

私も、APIバージョンだけ上げて既存コードの型などを修正しようと試みましたが、大幅にレスポンスなども変わってしまっており、作り直すのが早いと言う回答に至りました。

Discount APIで割引のExtension作成〜デプロイ

ここからは実践です。

Extension Appを作成

my-shopify-app/の階層で以下コマンドを実行。

% shopify app generate extension --template discount --name my-discount-function

ストアの選択と言語選択が出てくるので、自分に合うものを選択してください。

これでExtensionが作られます。

my-shopify-app/
├── extensions/
│   └── my-discount-function/
├── shopify.app.toml
Extension Appのディレクトリ構成

デフォルト生成された割引のロジック見てみる

デフォルトで生成されたファイルの中に、

  • cart_lines_discounts_generate_run.ts
  • cart_delivery_options_discounts_generate_run.ts

すでに簡易的な割引機能が実装されています。

cart_lines_discounts_generate_run.tsでは、以下のような割引が実装されています。

  • DiscountClass.Order で注文全体から10%割引
  • DiscountClass.Product でカート内で最も高額な商品に20%割引

cart_delivery_options_discounts_generate_run.tsでは、以下のような割引が実装されています。

  • DiscountClass.Shippingで配送グループに対して 100% オフ(送料無料)割引を返す

割引ロジックを更新する(初回購入かつ5,000円以上の購入に限り500円OFFする機能を作ってみる)

では、せっかくなので別の割引機能を実装してみましょう。

仕様としては以下を想定します。

  • カートの合計が税込5,000円以上の購入で500円OFF
  • 初回購入のみに適応(shopifyのattribute属性で判定)

まずは、attribute属性などを取得できるようにGraphQLを更新する必要があります。

cart_lines_discounts_generate_run.graphql

attributeとcostが取得できるようにQueryを追加しました。

query CartInput {
  cart {
    attribute(key: "isFirstOrder") {
      key
      value
    }
    cost {
      totalAmount {
        amount
        currencyCode
      }
    }
    lines {
      id
      cost {
        subtotalAmount {
          amount
        }
      }
    }
  }
  discount {
    discountClasses
  }
}

GraphQLを更新したらスキーマの更新をする必要があります。

my-shopify-app/extension/my-discount-function/の階層で以下コマンドを実行してください。

% shopify app function schema

これで、attributeやcostが使えるようになります。

ロジックを以下のように修正します。

要所にコメントしていますので、わかりやすいかと。

cart_lines_discounts_generate_run.ts

import {
  CartLinesDiscountsGenerateRunResult,
  CurrencyCode,
  DiscountClass,
  OrderDiscountSelectionStrategy,
} from "../generated/api";

export function cartLinesDiscountsGenerateRun(
  input
): CartLinesDiscountsGenerateRunResult {
  // カート内の商品行が0件なら処理を中断
  // Shopifyの仕様上、空のカートでは割引処理をしないため
  if (!input.cart.lines.length) {
    throw new Error("No cart lines found");
  }

  // Discount Functionが許可されている割引タイプが入っているか確認
  const hasOrderDiscountClass = input.discount.discountClasses.includes(
    DiscountClass.Order
  );

  // 対象の割引クラスがなければreturn
  if (!hasOrderDiscountClass) {
    return { operations: [] };
  }

  // 初回割引対象かの属性確認
  const attribute = input.cart.attribute;
  const isFirstOrder =
    attribute?.key === "isFirstOrder" && attribute?.value === "true";

  if (!isFirstOrder) {
    return { operations: [] };
  }

  // 初回割引対象の5,000円以上(税込)の購入か確認
  // hr商品では商品価格に税が含まれている運用なのでsubtotalAmountを使用する
  const totalAmount = input.cart.cost.totalAmount;
  if (
    totalAmount.currencyCode !== CurrencyCode.Jpy ||
    totalAmount.amount < 5000
  ) {
    return { operations: [] };
  }

  // 割引操作を格納する配列を初期化
  // Shopify Functionsでは、割引の定義(operations)を配列で返す必要があるので、複数の割引を順にpushできるように
  const operations: CartLinesDiscountsGenerateRunResult["operations"] = [];

  operations.push({
    // 注文全体に対する割引
    orderDiscountsAdd: {
      // 割引候補
      candidates: [
        {
          message: "初回5,000円以上の購入で500円OFF", // 管理画面などで表示される説明文
          targets: [
            {
              // 注文のどの部分に割引を適用するか?ここでは「全体(orderSubtotal)」
              orderSubtotal: {
                excludedCartLineIds: [],
              },
            },
          ],
          value: {
            // 割引種類
            fixedAmount: {
              amount: "500",
            },
          },
        },
      ],
      selectionStrategy: OrderDiscountSelectionStrategy.First, // 割引候補のうちどれを採用するかの戦略(First = 最初に適用可能なもの)
    },
  });

  return {
    operations,
  };
}

ディスカウントExtensionをデプロイする

実装が完了したら、ExtensionをShopifyにデプロイします。

my-shopify-app/extension/my-discount-function/の階層で以下コマンドを実行してください。

% shopify app deploy

デプロイ完了すると、ターミナルのログにShopify CLIから提供されたリンクURLが出る(successの緑枠の中)ので、クリックして開きます。

Shopify CLIのデプロイ

Shopify GraphiQL Appのインストール(初回デプロイ時のみ)

アプリをデプロイしただけでは、実際にまだディスカウントが作成されていません。

GraphiQLインターフェースを使用して、前のステップでデプロイした関数を基に、商品、注文、配送の割引を作成します。

具体的には、関数のIDをクエリし、それに基づいて適切なGraphQLミューテーションで割引を作成します。

Shopify GraphiQL Appを使うので、インストールしておきましょう。

https://shopify-graphiql-app.shopifycloud.com/login

また、インストール時にwrite_discounts権限が必要なので付与してください。

その他、作成した割引ロジックで必要な権限があれば付与しましょう。

Shopify GraphiQL Appから割引機能のIDを見つける(初回デプロイ時のみ)

まずは、クエリでアプリを探して、IDをコピーします。

query {
  shopifyFunctions(first: 100) {
    nodes {
      app {
        title
      }
      apiType
      title
      id
    }
  }
}
Shopify GraphiQL App
{
  "app": {
		"title": "my-shopify-app"
  },
	"apiType": "order_discounts",
	"title": "my-discount-function",
	"id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxx"
},

Shopify GraphiQL Appから割引を作成する(初回デプロイ時のみ)

次に、先ほど取得したIDをfunctionIdに代入して使って割引を作成します。

mutation {
  discountAutomaticAppCreate(
    automaticAppDiscount: {
      title: "初回5,000円以上の購入で500円OFF"
      functionId: "xxxxxxx-xxxx-xxxx-xxxx-xxxxxx"
      discountClasses: [ORDER]
      startsAt: "2025-01-01T00:00:00"
    }
  ) {
    automaticAppDiscount {
      discountId
    }
    userErrors {
      field
      message
    }
  }
}

実際にディスカウントを動作確認する

実行が完了したら、Shopify管理画面メニューのディスカウントから割引が有効になっていることを確認します。

Shopify ディスカウント

これで実際にストアでチェックアウトを見ると割引が適応されるようになります。

チェックアウトで割引確認

まとめ

本記事では、Shopify Functions API を活用した自動割引機能(Discount Function Extension) の開発方法、廃止された割引APIからの移行を含めて詳しく解説しました。

今後のShopify開発に参考になれば幸いです。

Anycloudではプロダクト開発の支援を行っています

プロダクト開発をお考えの方はぜひAnycloudにご相談ください。

まずは相談する

記事を書いた人

Matsuura

エンジニア

Matsuura

Twitter

Anycloudでエンジニアしてます!主にFlutterやWebフロントをやっていて、最近はバックエンドやインフラに挑戦・苦戦中。