【Shopify.dev和訳】Apps/Checkout/Shipping methods/Hide a method
この記事について
この記事は、Apps/Checkout/Shipping methods/Hide a shipping methodの記事を和訳したものです。
記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。
Shopify アプリのご紹介
Shopify アプリである、「商品ページ発売予告アプリ | リテリア Coming Soon」は、商品ページを買えない状態のまま、発売日時の予告をすることができるアプリです。Shopify で Coming Soon 機能を実現することができます。
Shopify アプリである、「らくらく日本語フォント設定|リテリア Font Picker」は、ノーコードで日本語フォントを使用できるアプリです。日本語フォントを導入することでブランドを演出することができます。
配送方法を非表示にする | Hide a shipping method
このチュートリアルでは、Shopify スクリプトを使用して、マーチャントが顧客タグに基づいてチェックアウト時に顧客に提供された配送方法を非表示にできる配送方法スクリプトを作成します。
ここで学べる事 | What you'll learn
このチュートリアルを終了すると、次のことが完了します。
- 最初の配送方法を非表示にするスクリプトを作成しました
- スクリプトのロジックをテストしました
- スクリプトを Shopify プラットフォームにデプロイし、カスタムアプリに関連付けました
- Shopify ストアでスクリプトを有効にしました
- パートナーダッシュボードでスクリプトの実行時間を表示しました
- コードを再利用可能にするための構成の追加に精通している
次の図は、ワークフローを完了するために従う手順を示しています。
必要条件 | Requirements
- ShopifyCLIをインストールした。
- カスタムアプリを作成した。Shopify スクリプトは現在、カスタムアプリのみをサポートしています。
- ストアにカスタムアプリをインストールした。
ステップ 1:スクリプトを作成する
Shopify CLIを使用して、次のコマンドを実行します。
shopify script create --extension-point=shipping_methods --name="Hide shipping method"
src /script.ts
の配送スクリプトの内容を以下のコードに置き換えます。このスクリプトは、ShippingMethods.FilterResponse
を返すfilterShippingMethod
関数を導入しています。この場合、最初の配送方法を非表示にしています。
import {ShippingMethods, Configuration, CheckoutDomain as Domain} from '@shopify/scripts-checkout-apis';
export function shippingMethodsHandler(
input: ShippingMethods.Input,
configuration: Configuration,
): ShippingMethods.Result {
const sortResponse = new ShippingMethods.SortResponse([]);
const filterResponse = filterShippingMethod(input.shippingMethods);
const renameResponse = new ShippingMethods.RenameResponse([]);
return new ShippingMethods.Result(sortResponse, filterResponse, renameResponse);
}
function filterShippingMethod(shippingMethods: Domain.ShippingMethod[]): ShippingMethods.FilterResponse {
var hiddenMethods = new Array<Domain.ShippingMethod>();
if (shippingMethods.length > 0) {
hiddenMethods.push(shippingMethods[0]);
}
return new ShippingMethods.FilterResponse(hiddenMethods);
}
ステップ 2:スクリプトをテストする
スクリプトのロジックをテストするには、script.spec.ts
で単体テストを作成し、npm test
コマンドを使用してそれらを実行します。
配送スクリプトtest/script.spec.ts
の内容を以下のコードに置き換えて、いくつかのテストを追加します。createPurchaseProposal
関数は、テストで使用される単一のラインアイテムを含むテストカートを作成するために使用されます。最初の配送方法であるCheap Option
のみが非表示になっていることを確認するテストがあります。
import {
CheckoutDomain as Domain,
ShippingMethods,
Currencies,
Configuration,
Money,
} from '@shopify/scripts-checkout-apis';
import {shippingMethodsHandler} from '../src/script';
/**
* この関数は、Domainのbuilderクラスを使用します。TestHelperは
* Checkoutなどの仮の入力オブジェクトを簡単に作成できるようにしてくれます。
* この関数を編集するか、コピーを作成して、テストする独自のカスタムチェック
* アウトオブジェクトを定義します。
*/
function createPurchaseProposal(): Domain.PurchaseProposal {
return new Domain.TestHelper.PurchaseProposalBuilder()
.setLines([
Domain.TestHelper.PurchaseProposalBuilder.line(
new Domain.TestHelper.VariantBuilder()
.withProduct(new Domain.TestHelper.ProductBuilder().titled('Red Delicious').addTag('fruits').buildWithId(1))
.buildWithId(1),
1,
Money.fromAmount(1, Currencies.CAD),
),
Domain.TestHelper.PurchaseProposalBuilder.line(
new Domain.TestHelper.VariantBuilder()
.withProduct(new Domain.TestHelper.ProductBuilder().titled('Florida').addTag('fruits').buildWithId(2))
.buildWithId(2),
1,
Money.fromAmount(1, Currencies.CAD),
),
])
.build();
}
describe('run', () => {
it('hides first option', () => {
const purchaseProposal: Domain.PurchaseProposal = createPurchaseProposal();
let cheapShippingMethod = new Domain.TestHelper.ShippingMethodBuilder()
.withTitle('Cheap Option')
.withAmount(Money.fromAmount(1, Currencies.CAD))
.buildWithId(1);
let expensiveShippingMethod = new Domain.TestHelper.ShippingMethodBuilder()
.withTitle('Expensive Option')
.withAmount(Money.fromAmount(2, Currencies.CAD))
.buildWithId(2);
const shippingMethods = [cheapShippingMethod, expensiveShippingMethod];
const result: ShippingMethods.Result = shippingMethodsHandler(
new ShippingMethods.Input(purchaseProposal, shippingMethods),
Configuration.fromMap(new Map<string, string>()),
);
const filterResponse = result.filterResponse!;
expect(filterResponse.hiddenMethods[0].title).toBe('Cheap Option');
expect(filterResponse.hiddenMethods.length).toBe(1);
});
});
ターミナルでテストを実行します。
npm test
[Describe]: run
[Success]: ✔ hides first option
[File]: test/script.spec.ts
[Groups]: 2 pass, 2 total
[Result]: ✔ PASS
[Snapshot]: 0 total, 0 added, 0 removed, 0 different
[Summary]: 1 pass, 0 fail, 1 total
[Time]: 4.056ms
✔ test/script.spec.ts Pass: 1 / 1 Todo: 0 Time: 4.056ms
[Result]: ✔ PASS
[Files]: 1 total
[Groups]: 2 count, 2 pass
[Tests]: 1 pass, 0 fail, 1 total
[Time]: 1888.133ms
ステップ 3:スクリプトをデプロイする
スクリプトを Shopify プラットフォームにデプロイしてアプリに関連付けるには、次のコマンドを実行します。
shopify script push
スクリプトをデプロイすると、Shopify プラットフォームにアップロードされ、アプリに登録されます。shopify script push --force
コマンドを使用してスクリプトを再度デプロイすると、スクリプトの現在のライブバージョンが上書きされます。スクリプトがストアで有効になっている場合、上書きされた後も有効のままになります。スクリプトに構成値が設定されている場合、新しいスクリプトは引き続きそれらの値を使用します。
スクリプトを再デプロイします
次回shopify script push
コマンドを実行すると、Shopify CLI は.env
ファイルを読み取り、次のことを行います。
-
.env
にリストされているアプリに接続します - UUID に一致するスクリプトがないかアプリをチェックします
スクリプトを別のアプリにデプロイする
スクリプトを別のアプリにデプロイする場合は、.env
ファイルを削除して、shopify script push
を実行します。
ステップ 4:スクリプトを有効にする
デフォルトでは、スクリプトはアプリがインストールされているストアで使用できますが、有効になっていません。チェックアウトの動作を変更するには、スクリプトを有効にする必要があります。
- ストアの Shopify admin で、設定をクリックします。
- 配送をクリックします。
- レートカスタマイズセクションでは、クリックしてカスタマイズを作成します。
- スクリプトを選択して有効にし、ステータスをオンに更新します。
- 保存をクリックします。
ステップ 5:スクリプトを監視およびデバッグする
パートナーダッシュボードのアプリの拡張機能セクションから、スクリプトを監視してエラーを調査できます。
- パートナーダッシュボードで、アプリをクリックします。
- アプリの名前をクリックします。
- 拡張機能をクリックします。
-
チェックアウトをクリックしてから、スクリプトの名前をクリックします。
スクリプトの実行時間とエラー情報が表示されます。実行結果がパートナーダッシュボードに表示されるまで、最大 2 分かかる場合があります。
エラーの種類
すべてのスクリプトエラーは、パートナーダッシュボードでRunErrorsとしてラベル付けされます。次のタイプのエラーが発生する可能性があります。
- 実行タイムアウト - これらのエラーは、スクリプトの実行に時間がかかりすぎる場合に発生します。たとえば、無限ループなど、スクリプトが無期限に実行される原因となるエラーが発生する場合があります。
export function shopify_main(): void {
while(true) {}
}
-
Runtime errors - これらのエラーは、コンパイル時に識別できない一般的なエラーです。これには、次のエラーが含まれます。
- スタックオーバーフロー
- 整数を 0 で割り算
- 到達不能、コードに追加した投げられたエラーを含んでいる
スクリプトをデバックする
スクリプトにメッセージログを追加して、エラーが発生したときにスクリプトをデバッグするのに役立てることができます。
Console.log
とas-pect’s log関数を使用して、スクリプトとテストからのメッセージをログに記録します。いずれかを使用する前に、次の考慮事項を確認してください。
- AssemblyScript では、
Console.log
は引数として文字列のみを受け入れます。 -
as-pect’s log関数は、複数のデータ型を受け入れ、出力の形式が適切であるため、使いやすくなっています。プッシュする前にスクリプトからログを削除している限り、ユニットテストやスクリプトで
log
を使用することができます。
変更をブレークしないでください
新しいスクリプトをプッシュすると、アプリの現在のライブスクリプトが置き換えられます。新しいスクリプトの構成スキーマに下位互換性がない場合は、構成値も更新されていることを確認してください。
構成に関連する一般的なランタイムエラーには、次の問題が含まれます。
- 構成値がない
- 型のキャストエラー
- 構成きいーの名前の変更
ストアに連絡し、スタッフと協力してカスタマイズを削除して再作成することで、スクリプトのダウンタイムを最小限に抑えることができます。スクリプトにチェックを含めて、エラーをより適切に処理することもできます。たとえば、構成値が存在しない場合、スクリプトは代わりにデフォルト値を使用できます。
スクリプトとアプリを削除する
パートナーダッシュボードを使用して、アプリからスクリプトを削除できます。スクリプトを削除する前に、すべてのストアでスクリプトのステータスがオフになっていることを確認してください。
スクリプトを追加するアプリは削除しないでください。
ステップ 6:構成を追加する
コードを再利用可能にするために、スクリプト内のハードコードされた変数を構成に置き換えることができます。構成により、マーチャントとスタッフは、Shopify admin に値とオプションを入力することでスクリプトをカスタマイズできます。スクリプトが実行されると、これらの値はキーと値のペアの入力としてスクリプトに渡されます。
クリプトのschema
プロパティは、スクリプトにユーザー設定可能なプロパティがあるかどうかを決定します。構成は、単一の値または値のリストを受け入れることができます。
構成可能な値を追加する
script.json
に、非表示にする顧客タグとメソッド名を含めます。
{
"version": "1",
"title": "Hide shipping method",
"description": "Hide's a shipping method based on customer tag",
"configurationUi": true,
"configuration": {
"type": "single",
"schema": [
{
"key": "shippingMethodName",
"name": "Shipping method to hide",
"defaultValue": "Unknown",
"type": "single_line_text_field"
},
{
"key": "customerTag",
"name": "Customer tag",
"helpText": "Customers with this tag will hide the specified shipping method.",
"defaultValue": "Unknown",
"type": "single_line_text_field"
}
]
}
}
構成値を取得する
スクリプトで、configuration.get
メソッドを使用して構成値を取得します。これを実現するには、filterShippingMethod
関数にconfiguration
を渡し、構成された値をキーで取得します。これで、すべての配送方法を繰り返し処理し、customer タグが一致した場合に、構成された値に一致する配送方法を非表示にできます。
import {ShippingMethods, Configuration, CheckoutDomain as Domain} from '@shopify/scripts-checkout-apis';
export function shippingMethodsHandler(
input: ShippingMethods.Input,
configuration: Configuration,
): ShippingMethods.Result {
const sortResponse = new ShippingMethods.SortResponse([]);
const filterResponse = filterShippingMethod(input.purchaseProposal, input.shippingMethods, configuration);
const renameResponse = new ShippingMethods.RenameResponse([]);
return new ShippingMethods.Result(sortResponse, filterResponse, renameResponse);
}
function filterShippingMethod(
purchaseProposal: Domain.PurchaseProposal,
shippingMethods: Domain.ShippingMethod[],
configuration: Configuration
): ShippingMethods.FilterResponse {
var hiddenMethods = new Array<Domain.ShippingMethod>();
for (let i=0; i<shippingMethods.length; i++) {
const shippingMethod = shippingMethods[i];
if (shouldHide(purchaseProposal, shippingMethod, configuration)) {
hiddenMethods.push(shippingMethod);
}
}
return new ShippingMethods.FilterResponse(hiddenMethods);
}
function shouldHide(
purchaseProposal: Domain.PurchaseProposal,
shippingMethod: Domain.ShippingMethod,
configuration: Configuration,
): bool {
const customer = purchaseProposal.buyerIdentity.customer
const customerTags = customer ? customer.tags : [];
const nameMatches = nameConfiguration(configuration) == shippingMethod.title;
const hasTag = customerTags.indexOf(tagConfiguration(configuration)) > -1;
return nameMatches && hasTag;
}
function nameConfiguration(configuration: Configuration): string {
const defaultNameFilter = 'Unknown';
return configuration.exists('shippingMethodName') ? configuration.get('shippingMethodName')! : defaultNameFilter;
}
function tagConfiguration(configuration: Configuration): string {
const defaultTagFilter = 'Unknown';
return configuration.exists('customerTag') ? configuration.get('customerTag')! : defaultTagFilter;
}
構成をテストする
以下のコードtest/script.spec.ts
を次の場所にコピーして、テストを更新します。
import {
CheckoutDomain as Domain,
ShippingMethods,
Currencies,
Configuration,
Money,
} from '@shopify/scripts-checkout-apis';
import {shippingMethodsHandler} from '../src/script';
/**
* この関数は、Domainのbuilderクラスを使用します。TestHelperは
* Checkoutなどの仮の入力オブジェクトを簡単に作成できるようにしてくれます。
* この関数を編集するか、コピーを作成して、テストする独自のカスタムチェック
* アウトオブジェクトを定義します。
*/
function createPurchaseProposal(): Domain.PurchaseProposal {
return new Domain.TestHelper.PurchaseProposalBuilder()
.setLines([
Domain.TestHelper.PurchaseProposalBuilder.line(
new Domain.TestHelper.VariantBuilder()
.withProduct(new Domain.TestHelper.ProductBuilder().titled('Red Delicious').addTag('fruits').buildWithId(1))
.buildWithId(1),
1,
Money.fromAmount(1, Currencies.CAD),
),
Domain.TestHelper.PurchaseProposalBuilder.line(
new Domain.TestHelper.VariantBuilder()
.withProduct(new Domain.TestHelper.ProductBuilder().titled('Florida').addTag('fruits').buildWithId(2))
.buildWithId(2),
1,
Money.fromAmount(1, Currencies.CAD),
),
])
.setCustomer(
new Domain.TestHelper.CustomerBuilder()
.addTag("B2B")
.buildWithId(4)
)
.build();
}
describe('run', () => {
it('does nothing when not configured', () => {
const purchaseProposal: Domain.PurchaseProposal = createPurchaseProposal();
let cheapShippingMethod = new Domain.TestHelper.ShippingMethodBuilder()
.withTitle('Cheap Option')
.withAmount(Money.fromAmount(1, Currencies.CAD))
.buildWithId(1);
let expensiveShippingMethod = new Domain.TestHelper.ShippingMethodBuilder()
.withTitle('Expensive Option')
.withAmount(Money.fromAmount(2, Currencies.CAD))
.buildWithId(2);
const shippingMethods = [cheapShippingMethod, expensiveShippingMethod];
const result: ShippingMethods.Result = shippingMethodsHandler(
new ShippingMethods.Input(purchaseProposal, shippingMethods),
Configuration.fromMap(new Map<string, string>()),
);
const filterResponse = result.filterResponse!;
expect(filterResponse.hiddenMethods.length).toBe(0);
});
it('hides payment method when configured', () => {
const purchaseProposal: Domain.PurchaseProposal = createPurchaseProposal();
let cheapShippingMethod = new Domain.TestHelper.ShippingMethodBuilder()
.withTitle('Cheap Option')
.withAmount(Money.fromAmount(1, Currencies.CAD))
.buildWithId(1);
let expensiveShippingMethod = new Domain.TestHelper.ShippingMethodBuilder()
.withTitle('Expensive Option')
.withAmount(Money.fromAmount(2, Currencies.CAD))
.buildWithId(2);
const shippingMethods = [cheapShippingMethod, expensiveShippingMethod];
var configurations = new Map<string, string>();
configurations.set('shippingMethodName', 'Cheap Option');
configurations.set('customerTag', 'B2B');
const result: ShippingMethods.Result = shippingMethodsHandler(
new ShippingMethods.Input(purchaseProposal, shippingMethods),
Configuration.fromMap(configurations),
);
const filterResponse = result.filterResponse!;
expect(filterResponse.hiddenMethods.length).toBe(1);
expect(filterResponse.hiddenMethods[0].title).toBe('Cheap Option');
});
});
ターミナルでテストを実行をします。
npm test
[Describe]: run
[Success]: ✔ does nothing when not configured
[Success]: ✔ hides payment method when configured
[File]: test/script.spec.ts
[Groups]: 2 pass, 2 total
[Result]: ✔ PASS
[Snapshot]: 0 total, 0 added, 0 removed, 0 different
[Summary]: 2 pass, 0 fail, 2 total
[Time]: 7.415ms
✔ test/script.spec.ts Pass: 2 / 2 Todo: 0 Time: 7.415ms
[Result]: ✔ PASS
[Files]: 1 total
[Groups]: 2 count, 2 pass
[Tests]: 2 pass, 0 fail, 2 total
[Time]: 1859.302ms
スクリプトを再デプロイします
push
コマンドを使用して、更新されたコードを Shopify にデプロイします。今回は、--force
オプションを使用して、既存のスクリプトを上書きする必要があります。
shopify script push --force
配送設定ページで、スクリプトの 2 つの新しい構成値を入力し、チェックアウトでテストできるようになりました。現在、チェックアウト時に変更が有効になるまでに最大 5 分かかる場合があります。
次のステップ
- Shopify がスクリプトの UI をレンダリングするのに役立つconfiguration schemaを作成します。
- スクリプト内の配送方法名をLocalizeし、複数の言語と文化で顧客にサービスを提供するストアで利用できるようにします。
- Shopify スクリプトのshipping methods API reference documentationをお読みください。
Shopify アプリのご紹介
Shopify アプリである、「商品ページ発売予告アプリ | リテリア Coming Soon」は、商品ページを買えない状態のまま、発売日時の予告をすることができるアプリです。Shopify で Coming Soon 機能を実現することができます。
Shopify アプリである、「らくらく日本語フォント設定|リテリア Font Picker」は、ノーコードで日本語フォントを使用できるアプリです。日本語フォントを導入することでブランドを演出することができます。
Discussion