「Payment Request API を利用したスムーズな決済処理」のコードラボへようこそ。このコードラボでは、既存の e コマース ウェブサイトに Payment Request API を導入する方法を学びます。それでは始めましょう。

必要なもの

以下の Git コマンドで E-Commerce ラボ レポジトリの "payment-request-api" ブランチのクローンを作成します。

$ git clone -b payment-request-api https://github.com/google-developer-training/pwa-ecommerce-demo.git

レポジトリには project フォルダや solution フォルダなどが含まれます。

クローンを作成したレポジトリに移動して、project フォルダを開きます。

$ cd pwa-ecommerce-demo/project

project ディレクトリ配下で、コマンドラインから npm install を実行します。

$ npm install

このコマンドによって node_modules ディレクトリが作成され、プロジェクトに必要なノード モジュールがすべてインストールされます。完了まで少し時間がかかります。

同じディレクトリで npm run serve を実行して、dist にサーバーを作成して実行します。このコマンドを実行し続けている限り、このコードラボで JavaScript ファイルに任意の変更を加えるたびに再ビルドが走ります。

$ npm run serve

ブラウザを起動して http://localhost:8080 を開き、初期状態のアプリの動作を確認してください。

現時点で、この e コマースアプリが動いているのがわかると思います。アプリ内ではカートに商品を追加したり、支払フォームに遷移したりできます。いろいろとアプリを操作して、ベースとなるウェブサイトの動きを確認しておいてください。

それでは、Payment Request API の実装を始めましょう。

Chrome 58 はまだ Payment Request API に対応していないので、コードをテストするには Chrome をインストールした Android 端末が必要です。「ローカル サーバーへのアクセス」というページの手順に従って、Android 端末でポート転送を設定してください。この設定により、自身の端末で e コマースアプリをホストできるようになります。

機能が利用可能か確認する

まずは Payment Request API の機能を検出する処理を追加しましょう。機能が利用可能な場合は、ユーザーの決済処理に使用します。

app/scripts/modules/app.js の "TODO PAY-3.1" を以下のコードで置き換え、PaymentRequest の機能検出処理を追加するために if (false) { というダミーの条件を削除します。

app.js

if (window.PaymentRequest) {

説明

機能の検出処理は、window.PaymentRequestundefined を返すかどうかを確認するだけのシンプルなものです。

PaymentRequest の作成

PaymentRequest コンストラクタを使用して Payment Request オブジェクトを作成します。

app/scripts/modules/payment-api.js の "TODO PAY-3.2" を以下のコードで置き換えて、PaymentRequest を初期化します。

payment-api.js

let request = new PaymentRequest(supportedInstruments, details, paymentOptions);

説明

このコンストラクタは次の 3 つのパラメータを取ります。

supportedInstruments:
1 つ目の引数は、サポートされる支払い方法に関する必須のデータセットです。このデータには、一般的なクレジットカードや Android Pay などの決済サービスが含まれます。このコードラボで扱うのは一般的なクレジットカードのみです。

details:
2 つ目の引数はトランザクションに関する必須情報です。合計額をユーザーに表示するための情報(ラベル、通貨、価額など)に加え、トランザクションにおける商品内訳も含まれる場合があります。

paymentOptions:
3 つ目の引数は配送などに関する任意のパラメータです。このパラメータにより、支払者の氏名や電話番号、メールアドレス、配送先情報などの追加情報をユーザーに要求できるようになります。

試してみる

これで、Payment Request API を使えるようになりました。サーバーを実行していない場合は npm run serve を実行して、Android 端末で動作を確認してください。

$ npm run serve

[Checkout] をクリックすると PaymentRequest UI が表示されます。

これで最初の一歩を踏み出すことができましたが、必要な作業はまだたくさんあります。引き続きがんばりましょう。

現時点では、ほとんどの引数が空の配列または空のオブジェクトです。では、支払い方法(supportedInstruments)に適切な値を設定しましょう。

scripts/modules/payment-api.js の "TODO PAY-4" とその下の JSON ブロックを以下の内容で置き換えます。

payment-api.js

    {
      supportedMethods: ['basic-card'],
      data: {
        supportedNetworks: ['visa', 'mastercard', 'amex',
          'jcb', 'diners', 'discover', 'mir', 'unionpay']
      }
    }

説明

PaymentRequest コンストラクタの第 1 引数は JSON オブジェクトで、これはサポートされる支払い方法のリストです。

supportedMethods は、サポートされる支払い方法の名称から成る配列です。サポートされる方法は、basic-card か決済アプリを表す URL のどちらかです。これらは決済方法の識別子の仕様で定義されています。

basic-card の場合、data 配下の supportedNetworks は、Payment Request API での使用が承認されているカードネットワーク識別子の仕様で定義されているサポート対象のクレジットカード ブランドのリストを取ります。これによってフィルターがかかり、Payment Request UI にはユーザーが利用可能なクレジットカードのみが表示されます。

次は、ユーザーが購入しようとしている商品の情報を表示しましょう。

details オブジェクトの定義

PaymentRequest コンストラクタの第 2 引数には、購入に関する詳細情報が含まれます。これは、表示する商品と合計額の情報から成るリストです。

この部分は app/scripts/modules/payment-api.js buildPaymentDetails() 関数内にすでに実装されています。よって、今回は特に作業は必要ありませんが、処理内容だけ確認しておきましょう。

payment-api.js

let details = {
  displayItems: displayItems,
  total: {
    label:'Total due',
    amount: {currency:'USD', value:String(total)}
  }
  // TODO PAY-7.2 - allow shipping options
};
return details;

説明

必須パラメータの total にはラベル、通貨、合計請求額が含まれます。

displayItems は任意のパラメータで、最終金額の計算方法を指定します。

この displayItems パラメータは、明細項目のリストではなく、小計、割引額、税、送料といった注文の主なコンポーネントの概要を示すことを目的としています。次のセクションでこれを定義します。

表示項目の定義

displayItems 変数は、カートに追加された商品に基づいて定義する必要があります。

app/scripts/modules/payment-api.js の "TODO PAY-5" を以下のコードで置き換えて、既存の let displayItems = []; を削除します。

payment-api.js

let displayItems = cart.cart.map(item => {
  return {
    label: `${item.sku}: ${item.quantity}x $${item.price}`,
    amount: {currency:'USD', value:String(item.total)}
  };
});

説明

以下のような Payment UI が表示されたら、"Order summary" を展開してください。

"Order summary" の行に displayItems の内容が表示されるはずです。ここでは、商品ごとに labelamount を指定しました。label は商品に関する情報を含む表示ラベルで、amount は商品の価格情報を構成するオブジェクトです。

PaymentRequest の実行に最低限必要なオプションは指定したので、ユーザーが決済処理を完了できるようにしましょう。

app/scripts/modules/payment-api.js の "TODO PAY-6" と既存の return request.show(); を以下のコードで置き換えます。

payment-api.js

    return request.show()
      .then(r => {
        // The UI will show a spinner to the user until
        // `request.complete()` is called.
        response = r;
        let data = r.toJSON();
        console.log(data);
        return data;
      })
      .then(data => {
        return sendToServer(data);
      })
      .then(() => {
        response.complete('success');
        return response;
      })
      .catch(e => {
        if (response) {
          console.error(e);
          response.complete('fail');
        } else if (e.code !== e.ABORT_ERR) {
          console.error(e);
          throw e;
        } else {
          return null;
        }
      });

PaymentRequest は、自身の show() メソッドを呼び出してインターフェースをアクティベートします。このメソッドは、ユーザーが購入の詳細を確認し、情報を追加または変更して、支払うことができるネイティブ UI を呼び出します。解決される Promisethen() メソッドとコールバック関数によって示されます)は、ユーザーが支払いリクエストを承認または拒否したときに返されます。

toJSON() を呼び出してレスポンス オブジェクトをシリアル化したら、それをサーバーに POST 送信して決済処理を行います。この部分は、使用している決済サービスや決済ゲートウェイによって異なります。

サーバーからレスポンスが返されたら、complete() を呼び出して success または fail を渡すことで、支払い処理が成功または失敗したことをユーザーに示します。

アプリの動作確認

おめでとうございます。これで基本的な Payment Request API の機能をアプリに実装することができました。サーバーを実行していない場合は npm run serve を実行して、Android 端末で動作を確認してください。

$ npm run serve

[Checkout] をクリックすると PaymentRequest UI が表示されます。

ここまで Payment Request API を組み込む方法を学んできましたが、配送項目については考慮していませんでした。ここからは、配送情報とオプション情報をユーザーから取得する方法について学びます。

配送オプションの定義

商品を配送するためにユーザーの住所を取得するには、PaymentRequest コンストラクタの 3 つ目のプロパティに requestShipping: true を追加します。

app/scripts/modules/payment-api.js の "TODO PAY-7.1" を以下のコードで置き換えます。

payment-api.js

requestShipping: true,

配送オプションのリストも表示する必要があります。

app/scripts/modules/payment-api.js の "TODO PAY-7.2" を以下のコードで置き換えます。

payment-api.js

,
shippingOptions: displayedShippingOptions

運良く SHIPPING_OPTIONSapp/scripts/modules/payment-api.js に事前定義されているので、これを解析したものを利用して displayShippingOptions オブジェクトを構成します。

app/scripts/modules/payment-api.js の "TODO PAY-7.3" を以下のコードで置き換えます。

payment-api.js

let displayedShippingOptions = [];
if (shippingOptions.length > 0) {
  let selectedOption = shippingOptions.find(option => {
    return option.id === shippingOptionId;
  });
  displayedShippingOptions = shippingOptions.map(option => {
    return {
      id: option.id,
      label: option.label,
      amount: {currency:'USD', value:String(option.price)},
      selected: option.id === shippingOptionId
    };
  });
  if (selectedOption) total += selectedOption.price;
}

説明

id は配送オプション項目を表す一意の識別子です。label はその項目の表示ラベル、amount は項目の価格情報を構成するオブジェクト、selected は項目の選択状態を表すブール値です。

お気づきのとおり、この変更によって Payment Request UI に "Shipping" というセクションが追加されました。ただし、発送先の住所を選択すると UI がフリーズしてタイムアウトします。これを回避するために、次のセクションで shippingaddresschange イベントを処理します。

ユーザーが指定した住所が配送対象国外で、商品を届けられない場合はどうしたらよいでしょうか。また、ユーザーが配送オプションを変更したときの課金方法はどうなるでしょう?こうした問題を解決するには、ユーザーが変更を行ったときや関連情報を更新したときにイベントを受信する必要があります。

shippingaddresschange イベント リスナーの追加

ユーザーが配送先の住所を変更したときに、shippingaddresschange イベントを受信するようにします。

app/scripts/modules/payment-api.js の "TODO PAY-8.1" を以下のコードで置き換えます。

payment-api.js

// When user selects a shipping address, add shipping options to match
request.addEventListener('shippingaddresschange', e => {
  e.updateWith((_ => {
    // Get the shipping options and select the least expensive
    shippingOptions = this.optionsForCountry(request.shippingAddress.country);
    selectedOption = shippingOptions[0].id;
    let details = this.buildPaymentDetails(cart, shippingOptions, selectedOption);
    return Promise.resolve(details);
  })());
});

説明

shippingaddresschange イベントを受信すると、request オブジェクトの shippingAddress の情報が更新されます。この情報を調べることで、次の内容を検証できます。

このコードでは配送国を調べて、米国内であれば無料配送または速達で、国外であれば国際配送で商品を送ります。app/scripts/modules/payment-api.js optionsForCountry() 関数を見て検証方法を確認してください。

なお、指定の住所に配送不可能な場合は shippingOptions に空の配列が渡されます。その場合は shippingOption.error を使用するとエラー メッセージを表示できます。

shippingoptionchange イベントリスナーの追加

ユーザーが配送オプションを変更したときに、shippingoptionchange イベントを受信するようにします。

app/scripts/modules/payment-api.js の "TODO PAY-8.2" を以下のコードで置き換えます。

payment-api.js

// When user selects a shipping option, update cost, etc. to match
request.addEventListener('shippingoptionchange', e => {
  e.updateWith((_ => {
    selectedOption = request.shippingOption;
    let details = this.buildPaymentDetails(cart, shippingOptions, selectedOption);
    return Promise.resolve(details);
  })());
});

説明

shippingoptionchange イベントを受信すると、request オブジェクトの shippingOption が更新されます。shippingOption は選択された配送オプションの id を表します。buildPaymentsDetails はこの id を受け取ると、その配送オプションに応じた料金を検索して表示項目を更新し、合計額に変更が生じたことをユーザーに伝えます。buildPaymentsDetails は、該当する配送オプションの selected 属性を true に変更して、ユーザーによってその項目が選択されていることも示します。app/scripts/modules/payment-api.js buildPaymentDetails() 関数で、この仕組みを確認してください。

配送先住所の他にも、メールアドレスや電話番号、氏名などの支払者情報を取得するためのオプションがあります。

app/scripts/modules/payment-api.js の "TODO PAY-9" を以下の支払いオプションで置き換えます。

payment-api.js

requestPayerEmail: true,
requestPayerPhone: true,
requestPayerName: true

説明

PaymentRequest コンストラクタの第 3 引数に requestPayerPhone: truerequestPayerEmail: truerequestPayerName: true を追加すると、それぞれ支払者の電話番号、メールアドレス、氏名を要求できます。

おめでどうございます。これで、配送オプションを含む Payment Request API の実装が完了しました。サーバーが停止していたら実行して、再度動作を確認してください。

$ npm run serve

ランダムに商品をカートに追加して支払いに進み、配送先住所と配送オプションを変更した上で、最後に決済をしてみてください。

e コマース アプリに支払い処理を組み込むことができました。完了

Payment Request API の詳細については、以下のリンク先をご覧ください。

リソース

仕様

デモ

Android Pay

Payment Request API に加えて Android Pay の有効化も検討している方は、Android Pay for the Web Codelab で具体的な方法をご確認ください。