【Shopify.dev和訳】Apps/Checkout/Payment methods/Rename a method


この記事について

この記事は、Apps/Checkout/Payment methods/Rename a methodの記事を和訳したものです。

記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。

Shopify アプリのご紹介

Shopify アプリである、「商品ページ発売予告アプリ | リテリア Coming Soon」は、商品ページを買えない状態のまま、発売日時の予告をすることができるアプリです。Shopify で Coming Soon 機能を実現することができます。

https://apps.shopify.com/shopify-application-314?locale=ja&from=daniel

Shopify アプリである、「らくらく日本語フォント設定|リテリア Font Picker」は、ノーコードで日本語フォントを使用できるアプリです。日本語フォントを導入することでブランドを演出することができます。

https://apps.shopify.com/font-picker-1?locale=ja&from=daniel

決済方法の名称変更

このチュートリアルでは、Shopify Scripts を使って、マーチャントがチェックアウト時に顧客に提供する支払い方法の名前をカスタマイズできるようにする Payment Methods スクリプトを作成します。

学べる内容

このチュートリアルを終えると、以下のことができるようになります。

  • 最初の支払い方法の名前を変更するスクリプトの作成
  • スクリプトのロジックをテストする
  • スクリプトを Shopify プラットフォームにデプロイし、カスタムアプリと関連付ける
  • Shopify のストアでスクリプトを有効にする
  • パートナーダッシュボードでスクリプトの実行時間を確認する
  • コードを再利用できるように設定を追加することに慣れる

名前を変更した支払い方法を示すスクリーンショット

次の図は、ワークフローを完成させるための手順を示しています。

支払い方法を非表示にするための支払いスクリプトを構築する際の手順を示した図

要件

Step 1: 決済スクリプトを作成する

Shopify CLIを使って、以下のコマンドを実行します。

terminal
shopify script create --extension-point=payment_methods --name="Rename payment method"

決済スクリプトの更新

src/script.tsにある決済スクリプトの内容を、以下のコードで置き換えます。これは、PaymentMethods.RenameProposalの配列を返すrenamePaymentMethod関数を導入しています。この例では、最初の payment method の名前を Hello Payments に変更しています。

src/script.ts
import {PaymentMethods, Configuration, CheckoutDomain as Domain} from '@shopify/scripts-checkout-apis';

export function paymentMethodsHandler(
  input: PaymentMethods.Input,
  configuration: Configuration,
): PaymentMethods.Result {

  const sortResponse = new PaymentMethods.SortResponse([]);
  const filterResponse = new PaymentMethods.FilterResponse([]);
  const renameResponse = renamePaymentMethod(input.paymentMethods);

  return new PaymentMethods.Result(sortResponse, filterResponse, renameResponse);
}

function renamePaymentMethod(paymentMethods: Domain.PaymentMethod[]): PaymentMethods.RenameResponse {
  var renameProposals = new Array<PaymentMethods.RenameProposal>();

  if (paymentMethods.length > 0) {
    const renameProposal = PaymentMethods.RenameProposal.rename(paymentMethods[0], 'Hello Payments');
    renameProposals.push(renameProposal);
  }

  return new PaymentMethods.RenameResponse(renameProposals);
}

Step 2: 作成したスクリプトをテストする

スクリプトのロジックをテストするには、script.spec.ts にユニットテストを作成して、npm test コマンドで実行します。

Payment スクリプトの test/script.spec.ts の内容を以下のコードで置き換えて、いくつかのテストを追加します。createPurchaseProposal関数を使って、テストで使用する 1 つのラインアイテムを持つテストカートを作成します。最初の支払い方法の名前だけが Hello Payments に変更されていることをチェックするテストが 1 つあります。

test/script.spec.ts
import {
  CheckoutDomain as Domain,
  PaymentMethods,
  Currencies,
  Configuration,
  Money,
} from '@shopify/scripts-checkout-apis';
import {paymentMethodsHandler} from '../src/script';

function newPaymentMethod(name: string, cards: string[] = []): Domain.PaymentMethod {
  return new Domain.PaymentMethod(1, name, cards);
}

/**
 * この関数は、Domain.TestHelperのbuilder classを使用して、
 * チェックアウトなどの偽の入力オブジェクトを簡単に作成します。
 * この関数を編集したり、コピーを作成したりして、
 * 独自のカスタムチェックアウトオブジェクトを定義し、
 * それに対してテストを行います。
 */
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),
      )
    ])
    .build();
}

describe('paymentMethodsHandler', () => {
  it('renames first method only', () => {
    const purchaseProposal: Domain.PurchaseProposal = createPurchaseProposal();
    const paymentMethods = [
      newPaymentMethod('2'),
      newPaymentMethod('3', ['visa', 'mc', 'amex']),
      newPaymentMethod('1'),
      newPaymentMethod('4'),
    ];

    const result: PaymentMethods.Result = paymentMethodsHandler(
      new PaymentMethods.Input(purchaseProposal, paymentMethods),
      new Configuration([]),
    );

    const renameResponse = result.renameResponse!;
    expect(renameResponse.renameProposals[0].name).toBe('Hello Payments');
    expect(renameResponse.renameProposals.length).toBe(1);
  });
});

ターミナルで,テストスイート(テストセット)を実行します。

terminal
npm test

[Describe]: paymentMethodsHandler

 [Success]: ✔ renames first method only

    [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]: 6.157ms

✔ test/script.spec.ts Pass: 1 / 1 Todo: 0 Time: 6.157ms
  [Result]: ✔ PASS
   [Files]: 1 total
  [Groups]: 2 count, 2 pass
   [Tests]: 1 pass, 0 fail, 1 total
    [Time]: 2141.536ms


Step 3: スクリプトをデプロイする

スクリプトを Shopify プラットフォームにデプロイし、アプリに関連付けるには、以下のコマンドを実行します。

terminal
shopify script push

スクリプトをデプロイすると、Shopify プラットフォームにアップロードされ、アプリに登録されます。shopify script push --force コマンドを使用してスクリプトを再度デプロイすると、スクリプトの現在のバージョンが上書きされます。スクリプトがストアで有効になっている場合は、上書きされた後も有効な状態が維持されます。スクリプトに設定値が設定されていた場合は、新しいスクリプトでもその設定値が使用されます。

スクリプトを再デプロイする

次回、shopify script pushコマンドを実行すると、Shopify CLI は.envファイルを読み込んで、以下のことを行います。

  • .env に記載されているアプリに接続します。
  • UUID にマッチするスクリプトがないかアプリをチェックする

スクリプトを別のアプリにデプロイする

スクリプトを別のアプリにデプロイしたい場合は、.envファイルを削除して、shopify script pushを実行します。


Step 4: スクリプトの有効化

デフォルトでは、アプリがインストールされたストアでスクリプトが使用できるようになっていますが、有効になっていません。チェックアウトの動作を変更するには、スクリプトを有効にする必要があります。

  1. ストアの Shopify 管理画面で、設定をクリックします。
  2. 支払いをクリックします。
  3. 支払い方法のカスタマイズセクションで、カスタマイズの作成をクリックします。
  4. 有効にするスクリプトを選択し、ステータスをオンに更新します。
  5. Saveをクリックします。

Step 5: スクリプトの監視とデバッグ

Partner Dashboard のアプリのExtensionsセクションから、スクリプトを監視し、エラーを調査することができます。

  1. Partner Dashboard で、Appsをクリックします。

  2. アプリの名前をクリックします。

  3. 拡張機能をクリックします。

  4. Checkout をクリックし、スクリプト名をクリックします。

    スクリプトの実行時間とエラー情報が表示されます。実行結果が Partner Dashboard に表示されるまでには、最大で 2 分かかることがあります。

エラーの種類

スクリプトのすべてのエラーは、Partner Dashboard では RunErrors と表示されます。以下のようなエラーが発生することがあります。

  • 実行タイムアウト - これらのエラーは、スクリプトの実行に時間がかかりすぎた場合に発生します。例えば、無限ループなど、スクリプトが無限に実行されるようなエラーが発生する場合があります。
    src/shopify_main.ts
    export function shopify_main(): void {
     while(true) {}
    }
    
  • ランタイムエラー - これらのエラーは、コンパイル時には特定できない一般的なエラーです。以下のようなエラーが含まれます。
    • スタックオーバーフロー
    • ゼロによる整数の割り算
    • unreachable (コードに追加したスローエラーを含む)

スクリプトに src/shopify_\__main.ts ファイルが含まれていない場合、スクリプトは失敗しますが、Partner ダッシュボードにはエラーは表示されません。

スクリプトのデバッグ

スクリプトにメッセージログを追加すると、エラー発生時のデバッグに役立ちます。

Console.logas-pect's log 関数を使って、スクリプトやテストからのメッセージをログに記録します。どちらも使用する前に、以下の注意点を確認してください。

  • AssemblyScript では、Console.logは引数として文字列しか受け付けません。
  • as-pect's log 関数は、複数のデータ型を受け入れ、その出力がより適切にフォーマットされているので、より使いやすいです。プッシュする前にスクリプトから削除すれば、ユニットテストやスクリプトの中で log を使用することができます。

破壊的な変更を避ける

新しいスクリプトをプッシュすると、アプリ上の現在のライブスクリプトが置き換えられます。新しいスクリプトの設定スキーマに下位互換性がない場合は、設定値も更新されることを確認してください。

構成に関連する一般的なランタイムエラーには、次のような問題があります。

  • Missing configuration values (構成値の欠落)
  • Type casting errors (タイプキャスティングエラー)
  • Renaming configuration keys (構成キーの名前変更)

スクリプトのダウンタイムを最小限に抑えるには、店舗に連絡してスタッフと協力し、カスタマイズを削除して再作成することが必要です。また、スクリプトにチェックを入れることで、エラーをより適切に処理することができます。たとえば、設定値が存在しない場合、スクリプトは代わりにデフォルト値を使用することができます。

スクリプトとアプリの削除

Partner Dashboard を使用して、アプリからスクリプトを削除することができます。スクリプトを削除する前に、すべてのストアでそのスクリプトのステータスがオフになっていることを確認してください。

スクリプトを追加したアプリは削除しないでください。


Step 6: configurations

コードの再利用性を高めるために、スクリプト内のハードコードされた変数を configuration で置き換えることができます。configuration では、マーチャントやスタッフが Shopify 管理画面で値やオプションを入力して、スクリプトをカスタマイズすることができます。スクリプトが実行されると、これらの値はキーと値のペアで入力としてスクリプトに渡されます。

スクリプトの schema プロパティは、スクリプトがユーザー設定可能なプロパティを持つかどうかを決定します。設定は単一の値または値のリストを受け付けることができます。

設定可能な値を追加

script.jsonには、設定可能な 2 つの値として、名前を変更するペイメントゲートウェイと新しいゲートウェイの名前を入れます。

script.json
{
  "version": "1",
  "title": "Rename payment method",
  "description": "Rename a payment method",
  "configurationUi": true,
  "configuration": {
    "type": "single",
    "schema": [
     {
        "key": "nameToMatch",
        "name": "Payment method name",
        "type": "single_line_text_field",
        "helpText": "The payment method to rename",
        "defaultValue": "Cash on Delivery (COD)"
      },
     {
        "key": "renameTo",
        "name": "Rename to",
        "type": "single_line_text_field",
        "helpText": "The new payment method name",
        "defaultValue": "Hello Payments"
      }
    ]
  }
}

設定値の取得

スクリプトでは、configuration.getメソッドを使って設定値を取得します。これは、renamePaymentMethod関数にconfigurationを渡して、設定値をキーごとに取得するというものです。これで、すべてのペイメントメソッドを繰り返し処理して、設定値と一致するペイメントメソッドの名前を変更することができます。

スクリプトの configuration.get メソッドのキーが、構成スキーマの key の値と一致していることを確認してください。構成がリストの場合は、ConfigurationList クラスを使用して構成を操作することもできます。

src/script.ts
import {PaymentMethods, Configuration, CheckoutDomain as Domain} from '@shopify/scripts-checkout-apis';

export function paymentMethodsHandler(
  input: PaymentMethods.Input,
  configuration: Configuration,
): PaymentMethods.Result {

  const sortResponse = new PaymentMethods.SortResponse([]);
  const filterResponse = new PaymentMethods.FilterResponse([]);
  const renameResponse = renamePaymentMethod(input.paymentMethods, configuration);

  return new PaymentMethods.Result(sortResponse, filterResponse, renameResponse);
}

function renamePaymentMethod(paymentMethods: Domain.PaymentMethod[], configuration: Configuration): PaymentMethods.RenameResponse {
  var renameProposals = new Array<PaymentMethods.RenameProposal>();

  for (let i=0; i<paymentMethods.length; i++) {
    if (paymentMethods[i].name == nameMatchConfiguration(configuration)) {
      const renameProposal = PaymentMethods.RenameProposal.rename(paymentMethods[i], renameConfiguration(configuration));
      renameProposals.push(renameProposal);
    }
  }

  return new PaymentMethods.RenameResponse(renameProposals);
}

function nameMatchConfiguration(configuration: Configuration): string {
  const defaultNameFilter = 'Unknown';
  return configuration.exists('nameToMatch') ? configuration.get('nameToMatch')! : defaultNameFilter;
}

function renameConfiguration(configuration: Configuration): string {
  const defaultNameFilter = 'Unknown';
  return configuration.exists('renameTo') ? configuration.get('renameTo')! : defaultNameFilter;
}

設定内容のテスト

以下のコードを test/script.spec.ts にコピーして、テストを更新します。

test/script.spec.ts
import {
  CheckoutDomain as Domain,
  PaymentMethods,
  Currencies,
  Configuration,
  Money,
} from '@shopify/scripts-checkout-apis';
import {paymentMethodsHandler} from '../src/script';

function newPaymentMethod(name: string, cards: string[] = []): Domain.PaymentMethod {
  return new Domain.PaymentMethod(1, name, cards);
}

/**
 * この関数は、Domain.TestHelperのbuilderクラスを使用して、
 * チェックアウトなどの偽の入力オブジェクトを簡単に作成します。
 * この関数を編集したり、コピーを作成したりして、
 * 独自のカスタムチェックアウトオブジェクトを定義し、
 * それに対してテストを行います。
 */
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),
      ),
    ])
    .build();
}

describe('paymentMethodsHandler', () => {
  it('does nothing when not configured', () => {
    const purchaseProposal: Domain.PurchaseProposal = createPurchaseProposal();
    const paymentMethods = [
      newPaymentMethod('2'),
      newPaymentMethod('3', ['visa', 'mc', 'amex']),
      newPaymentMethod('1'),
      newPaymentMethod('4'),
    ];

    const result: PaymentMethods.Result = paymentMethodsHandler(
      new PaymentMethods.Input(purchaseProposal, paymentMethods),
      new Configuration([]),
    );

    expect(result.renameResponse!.renameProposals.length).toBe(0);
  });

  it('renames payment method when configured', () => {
    const purchaseProposal: Domain.PurchaseProposal = createPurchaseProposal();
    const paymentMethods = [
      newPaymentMethod('2'),
      newPaymentMethod('3', ['visa', 'mc', 'amex']),
      newPaymentMethod('1'),
      newPaymentMethod('4'),
    ];

    var configurations = new Map<string, string>();
    configurations.set('nameToMatch', '2');
    const renameTo = 'Hello Payments'
    configurations.set('renameTo', renameTo);

    const result: PaymentMethods.Result = paymentMethodsHandler(
      new PaymentMethods.Input(purchaseProposal, paymentMethods),
      Configuration.fromMap(configurations),
    );

    const renameResponse = result.renameResponse!;
    expect(renameResponse.renameProposals[0].name).toBe('Hello Payments');
    expect(renameResponse.renameProposals.length).toBe(1);
  });
});

ターミナルで、テストスイートを実行します。

terminal
npm test

 [Success]: ✔ renames 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]: 5.018ms

✔ test/script.spec.ts Pass: 2 / 2 Todo: 0 Time: 5.018ms
  [Result]: ✔ PASS
   [Files]: 1 total
  [Groups]: 2 count, 2 pass
   [Tests]: 2 pass, 0 fail, 2 total
    [Time]: 1937.873ms

スクリプトの再デプロイ

更新したコードを push コマンドで Shopify にデプロイします。今回は、既存のスクリプトを上書きするために、--forceオプションを使用する必要があります。

terminal
shopify script push --force

Payment settingsページで、スクリプトに 2 つの新しい設定値を入力して、チェックアウトでテストすることができます。現在のところ、チェックアウトで変更が反映されるまでに、最大で 5 分かかることがあります。


お金と通貨に関するヒント

次のセクションでは、スクリプトで通貨と貨幣の値を使用する際のヒントを紹介します。

国際価格と多通貨はサポートされていません

Shopify スクリプトでは、国際価格や多通貨を使用することはできません。お金の値はすべてお客様の通貨(請求通貨とも呼ばれます)を使用します。Shopify は国際価格や多通貨をサポートしていないので、請求通貨はストアの通貨と同じになります。

設定を使ってお金の値を指定する

スクリプトに金額をハードコーディングしないでください。代わりに設定を使用して金額をスクリプトに提供します。設定値は文字列として保存されるため、以下の作業が必要となります。

  1. 文字列をサブユニットの値に変換します。
  2. 変換されたサブユニットの値でMoneyオブジェクトを作成します。通貨はストアの通貨と同じである必要があります。

import {MoneyI64 as Money, Currencies} from '@shopify/scripts-sdk-as'
...

const monetaryValue = Money.fromAmount(<f64>safeParseInt(configuration.get('amount')!), Currencies.CAD)
...

Money オブジェクトの操作順序

Money オブジェクトは、乗算、除算、加算などの一般的な二項演算をオーバーライドします。これらの二項演算は、Moneyオブジェクトと、float などの数値を用いて適用することもできます。この場合、演算の順序が重要になります。順番が重要なのは、二項演算が単なるMoneyクラスのメソッド呼び出しであり、パラメータとして数値を受け取るからです。その結果、Money * 4 は動作しますが、4 * Money は動作しません。

ParseInt の代わりに SafeParseInt を使用する。

AssemblyScript では、文字列を数値に解析する際にいくつかの特殊なケースがあります。例えば、I32.parseInt("bad")は 0 を返しますが、F64.parseInt("bad")は NaN を返します。F64 は AssemblyScript の中で最も柔軟な数値型なので、ぜひ使ってみてください。

parseInt の複雑さを避けるには、safeParseInt関数を使います。safeParseIntは、渡された文字列が数値でない場合に例外を投げます。safeParseIntScripts library で定義されているので、すべてのスクリプトで使用することができます。

次のステップ

  • Shopify がスクリプトの UI をレンダリングできるように、configuration schemaを作成します。
  • スクリプト内の支払い方法名をLocalizeして、複数の言語や文化で顧客にサービスを提供している店舗が利用できるようにします。
  • Shopify Scripts のpayment methods API reference documentationを読んでください。

Shopify アプリのご紹介

Shopify アプリである、「商品ページ発売予告アプリ | リテリア Coming Soon」は、商品ページを買えない状態のまま、発売日時の予告をすることができるアプリです。Shopify で Coming Soon 機能を実現することができます。

https://apps.shopify.com/shopify-application-314?locale=ja&from=daniel

Shopify アプリである、「らくらく日本語フォント設定|リテリア Font Picker」は、ノーコードで日本語フォントを使用できるアプリです。日本語フォントを導入することでブランドを演出することができます。

https://apps.shopify.com/font-picker-1?locale=ja&from=daniel