Payment Request APIに独自の支払い方法を追加する

UI開発者 加藤

ここ数年でキャッシュレス決済がまたたく間に浸透しました。今ではさまざまなオンライン決済サービスがあり、お得なキャンペーンなどによって複数のサービスを使い分けている方もいらっしゃるでしょう。しかし、それぞれのサービスでユーザーインターフェースが違うことでユーザーの学習コストは上がる一方です。

ユーザーインターフェースのばらつきはアプリに限った話ではなく、Webサイトでも長い間議論されている話題だと思います。Web上にはたくさんのECサイトが存在し、当然サイトごとに固有の決済フォームが存在しています。私たちユーザーはサイトごとにアカウントを作り、住所を入力し、クレジットカードの情報を入力する必要があります。使い勝手はサイトごとに違いますし、暗号化されているとはいえ大事な個人情報をいろいろな場所に分散させざるを得ない状況です。

そこで活用をおすすめしたいのがPayment Request APIです。Payment Request APIを使用すれば、どのサイトでも同じ決済インターフェースを提供することができ、住所や支払い方法はブラウザに保存された情報をそのまま流用できます。Google ChromeとMicrosoft Edgeで表示されるインターフェースをキャプチャしました。

Chromeではwindow.confirm()メソッドを実行したときに表示されるダイアログと似ているようなイメージで、ダイアログ内で自分の注文内容の詳細を確認することができます。Edgeの場合はMicrosoft Payというダイアログが表示され、支払いを行うにはMicrosoftアカウントによるログインが必須となっています。その他のブラウザに関しては、Firefoxは未対応、SafariはApple Payでの支払いのみ対応している状況です。

ダイアログ内に表示できる標準のオプションは現状以下の5つです。表示する内容はPaymentRequestコンストラクタに渡す引数で制御します。

  • 買い物かごの詳細
  • 届け先住所
  • 配送方法
  • 支払い方法
  • 連絡先情報

基本的な使い方はMDNのUsing the Payment Request APIにまとめられているため、そちらを参照ください。

独自の支払い方法を追加する

Google Pay API PaymentRequest チュートリアルから見て取れるように、支払い方法にはクレジットカードだけでなくその他のオンライン決済サービスを選択することもできます。ここでは「MLC Pay」というアプリを架空のページ「https://mlc-pay.com/」にホスティングし、この決済方法を架空のECサイト「https://mlc-shop.com」で使う想定で考えてみます。検証はGoogle Chromeで行いました。

MLC Payを選択できるようにするには3つのファイルを用意する必要があります。順を追って説明します。

payment-method-manifest.json

payment-method-manifestには、決済機能を持つアプリケーションのmanifest.jsonへのパスや、この決済機能を使用するサードパーティアプリケーションのドメインを記載します。default_applicationssupported_originsは必須のフィールドです。supported_originsには仕様上「*」を指定することもできます。

{
  "default_applications": ["https://mlc-pay.com/manifest.json"],
  "supported_origins": ["https://mlc-shop.com"]
}

このファイルはアプリケーションのルートディレクトリ(今回の例ではhttps://mlc-pay.com/payment-method-manifest.json)に配置します。

manifest.json

payment-method-manifest.jsonのdefault_applicationsにて指定したmanifest.jsonにはダイアログ上に表示する支払い方法の名前やアイコンを設定します。また、次に説明するService Workerへのパスも指定します。

{
  "name": "MLC Pay",
  "short_name": "MLC Pay",
  "icons": [{
    "src": "icon.png",
    "sizes": "48x48",
    "type": "image/png"
  }],
  "serviceworker": {
    "src": "sw.js"
  }
}

Service Worker

Payment Request APIのshow()メソッドが実行されると「manifest.json」に指定されているService Workerに向けてpaymentRequestが発行されます。このときService WorkerはPayment Handlerとしてふるまい、PaymentRequestEventを検知した際にPaymentResponseオブジェクトで応答する必要があります。

self.addEventListener('paymentrequest', e => {
    e.respondWith(new Promise(async function(resolve, reject) {
        resolve({
            methodName: "mlc pay",
            details: {}
        });
    });
});

ちなみにPaymentRequestEventオブジェクトはopenWindowというメソッドを持っており、このメソッドを使用するとお支払いボタン押下後にダイアログ上で別のページを表示することもできます。例えば、支払時にPINコードを別途入力させたい場合は、PINコードを入力させる画面を表示しその中で決済処理を行うことができます。以下はService Workerのサンプルです。

self.addEventListener('paymentrequest', e => {
    e.respondWith(new Promise(async function(resolve, reject) {
        // 追加で表示した画面からの連絡を待つリスナー関数
        self.addEventListener("message", listener = function(e) {
            self.removeEventListener("message", listener);

            resolve(e.data);
        });

        try {
            // 追加で表示したい画面を開く
            const win = await e.openWindow("/pin.html");
            // 開いた画面に対して、決済情報を通知する
            win.postMessage(e.data);
        } catch (err) {
            reject(err);
        }
    }));
});

pin.htmlではService Workerのmessageイベントで情報を受け取ることができます。本来はその中で決済処理を行いますが、今回は割愛いたします。

navigator.serviceWorker.addEventListener('message', function(messageEvent) {
    const form = document.getElementById("form");

    form.addEventListener('submit', function () {

        // --- 決済処理などを行う ---

        const paymentResponse = {
            methodName: "mlc pay",
            details: {}
        };

        // Service Workerに結果を返却する
        messageEvent.source.postMessage(paymentResponse);
        // この画面を閉じる
        window.close();
    });
});

ここまでで最低限の準備は終わりです。あとは「https://mlc-shop.com」側で以下のようにPayment Request APIを実行します。

const supportedInstruments = [
    {supportedMethods: 'https://mlc-pay.com/'},
];
const details = {
    // 商品や配送情報のデータ
};
const request = new PaymentRequest(supportedInstruments, details);

let result;
try {
    // resultには、PaymentResponseが代入されます。
    result = await request.show();
    // 決済が完了したらPaymentRequestのダイアログを閉じます。
    result.complete('success');
} catch (e) {
    if (result) {
        result.complete('fail');
    }
    console.log(e.message);
}

これで独自の決済方法をPayment Request APIでも使えるようになりました。実行結果は以下のようになります。お支払いボタンを押下すると、PINコードを入力する画面がスライドインし、確定するボタンを押下するとダイアログ全体が閉じられます。

Payment Request APIはあくまでユーザーインターフェースを提供するだけであり、裏側で決済処理を行うシステムは自前で用意する必要があります。ECサイトなど、既に決済システムを持っている方は導入を検討されてみてはいかがでしょうか。