送料無料や割引クーポン、ポイント還元……。顧客に対して購入の心理的ハードルを下げるディスカウント施策には、いくつか選択肢があります。中でも取り入れやすい「送料無料」は、導入しているストアも少なくはないと思います。
ただ「○○円以上購入で送料無料」といった条件付きの送料無料を他のディスカウントと併用する場合、注意が必要です。なぜなら、Shopifyのデフォルトの仕組みでは、送料の計算がディスカウント適用後に行われるため、本来送料無料の条件を満たしていても、割引によって条件から外れてしまうことがあるからです。
例)3,000円以上購入で送料無料となるストアのケース(3,000円未満の場合は送料1,000円がかかる設定)
- カートに入れた商品の小計が3,000円以上の場合、送料無料
- 会計時に500円引きのクーポンを適用
- 合計金額が3,000円以下となり、送料無料適用外に
- 送料1,000円が復活し、総支払金額が割引適用前より高額化
このディスカウントの逆転現象は、顧客のチェックアウト直前の離脱に繋がり、ストアにとっても購入機会を逃してしまう大きな問題です。
この問題を解決するために、Checkout Extensibilityのひとつである、「Shopify Functions」に着目しました。Shopify Functionsを用いることで、ディスカウント適用後も送料無料を維持するカスタムアプリを実装することが可能です。本記事では、そのカスタムアプリの実装手順について解説します。
実装の概要
今回の実装のポイントは、送料の判定をディスカウント適用前に行うことです。
Shopify FunctionsのDelivery Customization APIには、「商品種類ごとの小計(cart->lines->cost->subtotalAmount)」というプロパティがあります。これは、ディスカウント適用前の商品種類ごとの小計を表しています。この値を使って送料無料の判定を行うことで、追加のディスカウントに影響されることなく、元々の条件に基づいた判定が可能になります。
実装のロジックは以下の通りです。
- 【Shopify配送設定】「通常配送(送料発生)」と「送料無料」を用意する
※「●●円以上/︎▲kg以上の場合」といった条件設定は行わない。 - 【カスタムアプリ】Delivery Customization APIを使ってカート内の各商品の小計(割引前)を取得し、その合計を計算する。
- 【カスタムアプリ】合計が閾値(例:3,000円)以上の場合は「通常配送」の選択肢を非表示に、それ以外の場合は「送料無料」の選択肢を非表示にする。
このロジックにより、ディスカウント適用前の商品小計に基づいて送料無料の判定が行われ、条件を満たす場合は送料無料の配送オプションのみが表示されるようになります。一方、条件を満たさない場合は通常の配送オプションのみ表示されます。
では、実際にこのカスタムアプリを開発・設定する手順をご紹介します。
※以下の開発は2024年6月時点での情報によるものです。
カスタムアプリの作成
実装の前提
今回の実装は、以下で進めました。
- Shopify CLI……version: 3.60.1
- テーマ……Dawn 13.0.0
- 開発言語……React
- API……Delivery Customization API
- OS……macOS 13.4.1
プロジェクトの新規作成
アプリを作成し、アプリのプロジェクト名と開発の方針を決めます。
$ shopify app init
Welcome. Let's get started by naming your app project. You can change it later.
? Your project name?
✔ delivery-custom-test-app
? Get started building your app:
✔ Start with Remix (recommended)
? For your Remix template, which language do you want?
✔ JavaScript
Functionsの選択
今回はDelivery Customization APIを選択していきます。
$ cd delivery-custom-test-app
$ shopify app generate extension --template delivery_customization --name delivery-customization
Before proceeding, your project needs to be associated with an app.
? Create this project as a new app on Shopify?
✔ Yes, create it as a new app
? App name:
✔ delivery-custom-test-app
? What would you like to work in?
✔ JavaScript
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
Building GraphQL types ...
(node:5574) [DEP0040] DeprecationWarning: The punycode module is deprecated. Please use a userland al
ソースコードの編集
テンプレートで作成されたソースコード内に実際の処理を記載していきます。
extensions/delivery-customization/src/run.graphql
では今回利用する値(商品種類ごとの小計、配送方法の名称)を取得するGraphQLを記述します。
query RunInput {
cart {
lines {
cost {
subtotalAmount {
amount
}
}
}
deliveryGroups {
id
deliveryOptions {
handle
title
}
}
}
}
記述が終わったら、関数に対する入力クエリに基づいて、GraphQLタイプを作成します。
$ cd extensions/delivery-customization
$ shopify app function typegen
(node:5774) [DEP0040] DeprecationWarning: The punycode module is deprecated. Please use a userland alternative instead.
(Use node --trace-deprecation ... to show where the warning was created)
╭─ success ────────────────────────────────────────────────────────────────────╮
│ │
│ GraphQL types generated successfully. │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
? Have Shopify automatically update your app's URL in order to create a preview experience?
✔ Yes, automatically update
✔ Created extension delivery-customization.
› Press d │ toggle development store preview: ✔ on
› Press g │ open GraphiQL (Admin API) in your browser
› Press p │ preview in your browser
› Press q │ quit
次に実際に顧客が購入を行う際の判定処理を記述します。
extensions/delivery-customization/src/run.js
に元々記載のコードをコメントアウトし、以下のコードに置き換えてください。
// @ts-check
/**
* @typedef {import("../generated/api").RunInput} RunInput
* @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
* @typedef {import("../generated/api").Operation} Operation
*/
/**
* @type {FunctionRunResult}
*/
/*const NO_CHANGES = {
operations: [],
};*/
/**
* @param {RunInput} input
* @returns {FunctionRunResult}
*/
/*export function run(input) {
const configuration = JSON.parse(
input?.deliveryCustomization?.metafield?.value ?? "{}"
);
return NO_CHANGES;
};*/
export function run(input) {
const subtotal_amounts = input?.cart?.lines?.map(line => parseFloat(line?.cost?.subtotalAmount?.amount));
const subtotal_amount = subtotal_amounts.reduce((acc, current) => acc + current, 0);
console.log("lines->subtotalAmount:", subtotal_amount);
let hideShippingMethods = [];
let hide_option_title = "通常配送";
if(subtotal_amount >= 3000) {
hide_option_title = "送料無料";
}
hideShippingMethods = input?.cart?.deliveryGroups?.flatMap((group) => group?.deliveryOptions)
.filter(option => option.title !== hide_option_title)
.map(
(option) =>
({
hide: {
deliveryOptionHandle: option.handle,
},
})
);
return {
operations: hideShippingMethods,
};
}
アプリのインストールと確認
開発ストアへインストール
開発アプリをインストールしましょう。
また、「Shopify GraphiQL App」をインストールして開き、API Version「2023-07」を選択。
以下のjsonを入力して実行します。
query {
shopifyFunctions(first: 25) {
nodes {
app {
title
}
apiType
title
id
}
}
}
実行後、結果内に記載されている開発したアプリの”id”をコピーしておき、jsonに入力。
※ここでは”id”=functionId
と、機能説明=title
を入れています。
mutation {
deliveryCustomizationCreate(deliveryCustomization: {
functionId: "◆◆◆◆"
title: "商品割引前の合計価格が3000円以上なら送料無料"
enabled: true
}) {
deliveryCustomization {
id
}
userErrors {
message
}
}
}
実行すると機能がストアに追加されます。設定>配送と配達>配達のカスタマイズで確認可能です。
デプロイ
デプロイを行います。
$ shopify app deploy
? Release a new version of delivery-custom-test-app?
✔ Yes, release this new version
Releasing a new app version as part of delivery-custom-test-app
ストアで確認
次にストアでの動きを見てみましょう。
3,000円以上で送料無料のストア上で、3,000円以上の商品をカートに追加し、チェックアウトへ進みます。
ここに、1,000円のディスカウントを適用。会計金額は2,300円になります。
従来であれば総計が3,000円を下回るため送料も計上されてしまうところですが、実装したアプリの影響によって、ディスカウント適用後も送料無料とすることができました!
終わりに
柔軟なストア運営を実現するShopifyですが、デフォルトで敷かれているルールによっては、顧客に損が出てしまうケースがあります。今回はそんなトラップに、開発面からアプローチしてみました。
「ディスカウント適用後も元々の商品の価格で送料無料を判定したい」というのは、小さな部分かもしれません。しかし、少しでも購入体験が下がらない工夫をすることで、ストアにとっても恒久的な価値向上と利益をもたらすことにつながるのではないでしょうか。