はじめに

先月メルカリさん主催のイベントに参加し、実はその時に初めてPaymentRequestAPIなるものの存在を知りました。触ってみたいなと思いつつも結局はまだ触らず終いでした。

そんな中、昨日このようなツイートを拝見しました。

いいきっかけだと思って触ってみることにしました。

PaymentRequestAPIとは

先ほどのツイートをされている方が書いた記事になります。ここに凝縮されています。
ウェブでの新しいお金の払い方 - Web Payments と Payment Request API について: Tender Surrender

Payment Request API は、ウェブでの支払いを標準化しようという一連の仕様である Web Payments を構成する要素のひとつであり、中心を成すものです。Web Payments を構成する要素には下記のようなものがあります。
・Payment Request API
・Payment Handler API
・Payment Method Identifiers
・Basic Card Payment

→Payment Request APIというのはそれ単体で決済完了までできるものではなく、決済の仕組みの中の構成要素の一つなんですね。

Payment Request API を使ってできることは、基本的にフォームの置き換えになります。それ自体は支払い処理を行わない、という点に注意してください。つまり、この API を使ってできるのは支払情報を取得するところまでであり、その先の支払い処理は別途開発者が行わなければなりません。

→あくまでも入力フォームの置き換えで、決済処理自体は自分たちでやってね、とのこと。
専用の入力フォームの表示と支払情報の取得までがこのAPIの役割みたいです。

デモサイト

https://polykart-credential-payment.appspot.com/
先ほどの記事内にリンクがあったので貼っておきます。

(実際に商品を購入することはできません。また、クレジットカードなどの情報がサーバーに渡されることはありません)。

とのことです。

統合ガイドの写経

Payment Request API: 統合ガイド
いろいろ書いてありますが、一番下にまとめのコードがあります。このscriptを

タグの最後に書きました。
function onBuyClicked(event) {
  if (!window.PaymentRequest) {
    return;
  }
  // Payment Request API is available.
  // Stop the default anchor redirect.
  event.preventDefault();

  var supportedInstruments = [{
    supportedMethods: [
      'visa', 'mastercard', 'amex', 'discover', 'maestro',
      'diners', 'jcb', 'unionpay', 'bitcoin'
    ]
  }];

  var details = {
    displayItems: [{
      label: 'Original donation amount',
      amount: { currency: 'USD', value: '65.00' }
    }, {
      label: 'Friends and family discount',
      amount: { currency: 'USD', value: '-10.00' }
    }],
    total: {
      label: 'Total due',
      amount: { currency: 'USD', value : '55.00' }
    }
  };

  var options = {
    requestShipping: true,
    requestPayerEmail: true,
    requestPayerPhone: true,
    requestPayerName: true
  };

  // Initialization
  var request = new PaymentRequest(supportedInstruments, details, options);

  // When user selects a shipping address
  request.addEventListener('shippingaddresschange', e => {
    e.updateWith(((details, addr) => {
      var shippingOption = {
        id: '',
        label: '',
        amount: { currency: 'USD', value: '0.00' },
        selected: true
      };
      // Shipping to US is supported
      if (addr.country === 'US') {
        shippingOption.id = 'us';
        shippingOption.label = 'Standard shipping in US';
        shippingOption.amount.value = '0.00';
        details.total.amount.value = '55.00';
      // Shipping to JP is supported
      } else if (addr.country === 'JP') {
        shippingOption.id = 'jp';
        shippingOption.label = 'International shipping';
        shippingOption.amount.value = '10.00';
        details.total.amount.value = '65.00';
      // Shipping to elsewhere is unsupported
      } else {
        // Empty array indicates rejection of the address
        details.shippingOptions = [];
        return Promise.resolve(details);
      }
      // Hardcode for simplicity
      if (details.displayItems.length === 2) {
        details.displayItems[2] = shippingOption;
      } else {
        details.displayItems.push(shippingOption);
      }
      details.shippingOptions = [shippingOption];

      return Promise.resolve(details);
    })(details, request.shippingAddress));
  });

  // When user selects a shipping option
  request.addEventListener('shippingoptionchange', e => {
    e.updateWith(((details) => {
      // There should be only one option. Do nothing.
      return Promise.resolve(details);
    })(details));
  });

  // Show UI then continue with user payment info
  request.show().then(result => {
    // POST the result to the server
    return fetch('/pay', {
      method: 'POST',
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(result.toJSON())
    }).then(res => {
      // Only if successful
      if (res.status === 200) {
        return res.json();
      } else {
        throw 'Failure';
      }
    }).then(response => {
      // You should have received a JSON object
      if (response.success == true) {
        return result.complete('success');
      } else {
        return result.complete('fail');
      }
    }).then(() => {
      console.log('Thank you!',
          result.shippingAddress.toJSON(),
          result.methodName,
          result.details.toJSON());
    }).catch(() => {
      return result.complete('fail');
    });
  }).catch(function(err) {
    console.error('Uh oh, something bad happened: ' + err.message);
  });
}

// Assuming an anchor is the target for the event listener.
document.querySelector('#start').addEventListener('click', onBuyClicked);

また、

Payment Request API shim の読み込み
この開発中の標準 API に随時対応する手間を省くために、この shim をコードの

セクションに追加することを強くお勧めします。

とのことなのでこれを<head>内に書きました。ちなみに書かなくても動きました。

<script src="https://storage.googleapis.com/prshim/v1/payment-shim.js">

結果

スクリーンショット 2018-03-28 12.45.25.png

ほんとクライアントサイドは簡単できちゃいました。

なお、PAY.JPとの連携をしている記事があったので参考に貼らせてもらいます。
PAY.JP で Google Chrome の Payment Request API を使って決済する

まとめ

このAPIを介してApplePayが使えるようになる、というのが最初のツイートの内容ですね。今でもすでにそれができるようになるJSがあるみたいですけど、もっとシームレスに簡単に連携できるということなのでしょうか。わかりません。

また、最初のリンクに記載がありますが、モバイルでの購入処理の取りやめはパソコンでのそれの2倍らしいです。やっぱりスマホは入力しづらいですもんね。アマゾンなんかは1クリックで買えちゃうのでほんと便利だなと思います。

アマゾンのような会員を抱えているサービスであれば別にわざわざこのAPIを使う必要はなさそうですが、顧客情報・支払情報を持っていない新規サービスには有用だなと思いました。

今後決済のあるサービスを作る場合は検討してみようと思います。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.