LoginSignup
4
5

More than 5 years have passed since last update.

Payment Request API:統合ガイドを写経してみた

Posted at

はじめに

先月メルカリさん主催のイベントに参加し、実はその時に初めて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を使う必要はなさそうですが、顧客情報・支払情報を持っていない新規サービスには有用だなと思いました。

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

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5