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

今回は、ShopifyのFuctions APIを使用した割引の拡張機能(Discount Function Extension)の作成について紹介します!
また、最近API自体が大幅に変わったこともあり、廃止された割引API から新しい割引API に移行する方法も併せてのご紹介となります。
目次
- 前提
- 必要な開発ツールと構成
- 廃止された割引APIから新しい割引APIに移行する
- Discount APIで割引のExtension作成〜デプロイ
- Extension Appを作成
- デフォルト生成された割引のロジック見てみる
- 割引ロジックを更新する(初回購入かつ5,000円以上の購入に限り500円OFFする機能を作ってみる)
- ディスカウントExtensionをデプロイする
- Shopify GraphiQL Appのインストール(初回デプロイ時のみ)
- Shopify GraphiQL Appから割引機能のIDを見つける(初回デプロイ時のみ)
- Shopify GraphiQL Appから割引を作成する(初回デプロイ時のみ)
- 実際にディスカウントを動作確認する
- まとめ
前提
- Shopifyアプリが構築されていること
- Functions拡張機能は
shopify app create
で作成したアプリのディレクトリ構成内に組み込まれるため - 作成したFunction ExtensionをShopに適用するには、アプリを通じてインストールおよび設定する必要があるため
- Functions拡張機能は
ディレクトリ構成的には以下のようになります。
my-shopify-app/
├── extensions/
│ └── my-discount-function/
│ ├── shopify.extension.toml
│ └── src/
│ └── function.ts
├── shopify.app.toml
必要な開発ツールと構成
構成要素 | 説明 |
---|---|
Shopifyアプリ | Functions Extensionを含むメインコンテナをCLIで作成 |
Functions Extension |
|
Shopify CLI | 開発・デプロイ・登録すべてに必須 |
Partnerアカウント | アプリの作成・開発に必要 |
開発用Shop | 開発したアプリをテストするための環境 |
廃止された割引APIから新しい割引APIに移行する
結論から言うと、ドキュメントには移行と書かれていますが、ちょっと書き換えるような移行ではなく、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

デフォルト生成された割引のロジック見てみる
デフォルトで生成されたファイルの中に、
- 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 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
}
}
}
.png?w=1428&fit=max&fm=webp)
{
"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 Functions API を活用した自動割引機能(Discount Function Extension) の開発方法、廃止された割引APIからの移行を含めて詳しく解説しました。
今後のShopify開発に参考になれば幸いです。