支払い取引のライフサイクル

販売者が決済アプリを統合する方法と、Payment Request API で決済取引が機能する仕組みについて説明します。

Web Payments API は、ブラウザに初めて組み込まれた専用の決済機能です。ウェブ決済を使用すると、販売者と決済アプリの統合が簡単になり、顧客エクスペリエンスが効率化され、より安全になります。

ウェブ決済のメリットについて詳しくは、ウェブ決済で支払いアプリを強化するをご覧ください。

この記事では、販売者のウェブサイトでの支払い取引について説明します。また、支払いアプリの統合の仕組みについても説明します。

このプロセスには 6 つのステップが含まれます。

  1. 販売者が支払いトランザクションを開始します。
  2. 販売者が支払いボタンを表示します。
  3. お客様が支払いボタンを押します。

    BobPay(決済アプリ)ボタンが表示されたチーズショップのウェブサイトの図。

  4. ブラウザが支払いアプリを起動します。

    モダールで起動された BobPay アプリがあるチーズショップのウェブサイトの図。モーダルに、配送オプションと合計金額が表示されます。

  5. お客様が詳細(配送オプションや住所など)を変更した場合、販売者は変更を反映して取引の詳細を更新します。

    お客様が BobPay アプリモーダルで別の配送オプションを選択している様子を示す図。販売者が BobPay に表示されている合計金額を更新している 2 つ目の図。

  6. 購入者が購入を確認すると、販売者は支払いを検証し、取引を完了します。

    お客様が [

ステップ 1: 販売者が支払い取引を開始する

顧客が購入を決定すると、販売者は PaymentRequest オブジェクトを作成して支払いトランザクションを開始します。このオブジェクトには、トランザクションに関する重要な情報が含まれます。

  • 利用可能なお支払い方法と、取引を処理するためのそのデータ。
  • 合計金額(必須)や商品に関する情報などの詳細。
  • 販売者が配送先住所や配送オプションなどの配送情報をリクエストできるオプション。
  • 販売者は、請求先住所、支払者の氏名、メールアドレス、電話番号をリクエストすることもできます。
  • 販売者は、オプションの配送タイプshippingdeliverypickup)を PaymentRequest に含めることもできます。支払いアプリでは、これをヒントとして UI に正しいラベルを表示できます。
const request = new PaymentRequest([{
  supportedMethods: 'https://bobpay.xyz/pay',
  data: {
    transactionId: '****'
  }
}], {
  displayItems: [{
    label: 'Anvil L/S Crew Neck - Grey M x1',
    amount: { currency: 'USD', value: '22.15' }
  }],
  total: {
    label: 'Total due',
    amount: { currency: 'USD', value : '22.15' }
  }
}, {
  requestShipping: true,
  requestBillingAddress: true,
  requestPayerEmail: true,
  requestPayerPhone: true,
  requestPayerName: true,
  shippingType: 'delivery'
});
トランザクション ID を含める

一部の支払いハンドラでは、取引情報の一部として、事前に発行した取引 ID を販売者が提供することが求められる場合があります。一般的な統合では、販売者と支払いハンドラのサーバー間で通信が行われ、合計金額が予約されます。これにより、悪意のある顧客が価格を操作して、取引終了時に検証で販売者をだますることを防止できます。

販売者は、PaymentMethodData オブジェクトの data プロパティの一部として取引 ID を渡すことができます。

取引情報が提供されると、ブラウザは、お支払い方法 ID に基づいて PaymentRequest で指定された支払いアプリの検出プロセスを開始します。これにより、販売者がトランザクションを進めることができるとすぐに、ブラウザは起動する支払いアプリを決定できます。

検出プロセスの仕組みについて詳しくは、お支払い方法の設定をご覧ください。

ステップ 2: 販売者に支払いボタンを表示する

販売者は多くのお支払い方法に対応できますが、お客様が実際に使用できるお支払い方法の支払いボタンのみを表示する必要があります。使用できない支払いボタンを表示すると、ユーザー エクスペリエンスが低下します。販売者は、PaymentRequest オブジェクトで指定された支払い方法がお客様に適さないと予測できる場合は、代替ソリューションを提供するか、そのボタンをまったく表示しないことができます。

販売者は PaymentRequest インスタンスを使用して、顧客が決済アプリを使用できるかどうかを照会できます。

お客様がお支払いアプリをお持ちかどうかを確認します。

PaymentRequestcanMakePayment() メソッドは、お客様のデバイスで支払いアプリが利用可能な場合は true を返します。「利用可能」とは、お支払い方法に対応したお支払いアプリが検出され、プラットフォーム固有のお支払いアプリがインストールされているか、ウェブベースのお支払いアプリが登録可能であることを意味します。

const canMakePayment = await request.canMakePayment();
if (!canMakePayment) {
  // Fallback to other means of payment or hide the button.
}

ステップ 3: お客様が支払いボタンを押す

顧客が支払いボタンを押すと、販売者は PaymentRequest インスタンスの show() メソッドを呼び出し、すぐに支払い UI の起動がトリガーされます。

最終的な合計金額が動的に設定される場合(サーバーから取得される場合など)、販売者は合計金額が判明するまで支払い UI の起動を延期できます。

支払い UI のリリースの延期

最終的な合計価格が決定されるまで支払いを延期する UI のデモをご覧ください。

支払い UI を延期するには、販売者は Promise を show() メソッドに渡します。プロミスが解決し、トランザクションを開始する準備ができるまで、ブラウザには読み込みインジケーターが表示されます。

const getTotalAmount = async () => {
  // Fetch the total amount from the server, etc.
};

try {
  const result = await request.show(getTotalAmount());
  // Process the result…
} catch(e) {
  handleError(e);
}

show() の引数として Promise が指定されていない場合、ブラウザは支払い UI をすぐに起動します。

ステップ 4: ブラウザで支払いアプリが起動される

ブラウザは、プラットフォーム固有のお支払いアプリまたはウェブベースのお支払いアプリを起動できます(詳しくは、Chrome が起動するお支払いアプリを決定する方法をご覧ください)。

支払いアプリの構築方法はほとんどデベロッパーに任されていますが、販売者との間で行われるイベントと、それらのイベントとともに渡されるデータの構造は標準化されています。

決済アプリは、起動するとステップ 1 で PaymentRequest オブジェクトに渡された取引情報を受け取ります。これには以下が含まれます。

  • お支払い方法のデータ
  • 合計金額
  • 支払い方法

支払いアプリは、取引情報を使用して UI にラベルを付けます。

ステップ 5: 購入者の操作に応じて販売者が取引の詳細を更新する方法

お客様は、支払いアプリで取引の詳細(お支払い方法や配送オプションなど)を変更できます。お客様が変更を加えている間、販売者は変更イベントを受信し、取引の詳細を更新します。

販売者が受け取るイベントには、次の 4 種類があります。

  • お支払い方法の変更イベント
  • 配送先住所の変更イベント
  • 配送オプションの変更イベント
  • 販売者検証イベント

お支払い方法の変更イベント

支払いアプリは複数のお支払い方法に対応できます。販売者は、お客様の選択に応じて特別な割引を提供できます。このユースケースに対応するには、お支払い方法の変更イベントで販売者に新しいお支払い方法を通知します。販売者は、割引を適用した合計金額を更新して、支払いアプリに返すことができます。

request.addEventListener('paymentmethodchange', e => {
  e.updateWith({
    // Add discount etc.
  });
});

配送先住所の変更イベント

支払いアプリは、必要に応じてお客様の配送先住所を提供できます。フォームに詳細を手動で入力する必要がなく、配送先住所を複数の販売者のウェブサイトではなく、お気に入りの支払いアプリに保存できるため、お客様にとって便利です。

取引の開始後にお客様が支払いアプリで配送先住所を更新すると、'shippingaddresschange' イベントが販売者に送信されます。このイベントは、販売者が新しい住所に基づいて送料を決定し、合計金額を更新して、支払いアプリに返す際に役立ちます。

request.addEventListener('shippingaddresschange', e => {
  e.updateWith({
    // Update the details
  });
});

販売者が更新された住所に発送できない場合は、支払いアプリに返された取引の詳細にエラー パラメータを追加して、エラー メッセージを提供できます。

配送オプションの変更イベント

販売者は、顧客に複数の配送オプションを提示でき、その選択肢を決済アプリに委任できます。これらの配送オプションは、顧客が選択できる価格とサービス名のリストとして表示されます。例:

  • 通常便 - 無料
  • エクスプレス配送 - 5 米ドル

お客様が支払いアプリで配送オプションを更新すると、'shippingoptionchange' イベントが販売者に送信されます。販売者は、送料を決定し、合計金額を更新して、決済アプリに返すことができます。

request.addEventListener('shippingoptionchange', e => {
  e.updateWith({
    // Update the details
  });
});

販売者は、お客様の配送先住所に基づいて配送オプションを動的に変更することもできます。これは、国内と海外のお客様に異なる配送オプションを提供する場合に便利です。

販売者検証イベント

セキュリティを強化するため、支払いアプリは支払いフローに進む前に販売者の検証を行うことができます。検証メカニズムの設計は支払いアプリに任されていますが、販売者検証イベントは、販売者が検証に使用できる URL を販売者に通知する役割を果たします。

request.addEventListener('merchantvalidation', e => {
  e.updateWith({
    // Use `e.validateURL` to validate
  });
});

ステップ 6: 販売者が支払いを検証し、取引を完了します

お客様が支払いを承認すると、show() メソッドは PaymentResponse に解決する Promise を返します。PaymentResponse オブジェクトには次の情報が含まれます。

  • 支払い結果の詳細
  • 配送先住所
  • 配送オプション
  • 連絡先情報

この時点では、トランザクションがまだ完了していないことを示す読み込みインジケーターがブラウザの UI に引き続き表示されることがあります。

支払いエラーやエラーが原因で支払いアプリが終了した場合、show() から返されたプロミスが拒否され、ブラウザは支払いトランザクションを終了します。

お支払いの処理と検証

PaymentResponsedetails は、支払いアプリから返される支払い認証情報オブジェクトです。販売者は、この認証情報を使用して支払いを処理または検証できます。この重要なプロセスの仕組みは、支払いハンドラによって異なります。

取引の完了または再試行

販売者は、取引が成功したかどうかを判断した後、次のいずれかを行います。

  • .complete() メソッドを呼び出してトランザクションを完了し、読み込みインジケーターを閉じます。
  • お客様に retry() メソッドを呼び出して、再試行してもらいます。
async function doPaymentRequest() {
  try {
    const request = new PaymentRequest(methodData, details, options);
    const response = await request.show();
    await validateResponse(response);
  } catch (err) {
    // AbortError, SecurityError
    console.error(err);
  }
}

async function validateResponse(response) {
  try {
    const errors = await checkAllValuesAreGood(response);
    if (errors.length) {
      await response.retry(errors);
      return validateResponse(response);
    }
    await response.complete("success");
  } catch (err) {
    // Something went wrong…
    await response.complete("fail");
  }
}
// Must be called as a result of a click
// or some explicit user action.
doPaymentRequest();

次のステップ