WebサイトでApple Payができるまで(実装編)

開発環境の構築を含めた準備はWebサイトでApplePayができるまで(準備編)

今回は主にApple PayをWebサイトで使うための実装について説明します。

Apple Pay on the Webの実装

Apple Pay JSをWebサイトに組み込むには以下のステップを実装する必要があります。

  1. Apple Pay JSが使えるかどうかを確認
  2. PaymentSheetに表示する情報を作成
  3. PaymentSheetを表示する
  4. Apple Developperに登録したWebサイト(Marchant)であることを認証
  5. ユーザの購入情報の入力と選択
  6. 購入

6については、採用している決済サービスによってはほぼ必要ないかもしれません。 今回私が担当したのは上記の1~5に相当するところなのでそこまでを簡単に解説します。

Apple Pay JSが使えるかどうかを確認

以下のようなコードでApple Pay JSが使える状態かを確認できます。

    if (window.ApplePaySession) {
       var merchantIdentifier = 'merchant.com.example';
       var promise = ApplePaySession.canMakePaymentsWithActiveCard(merchantIdentifier);
       promise.then(function (canMakePayments) {
         if (canMakePayments) {
           // ボタンを表示
         }
       });
    }

PaymentSheetに表示する情報を作成

Javascriptのオブジェクトに必要なプロパティを追加します。 最低限必要な情報は以下の通りです。

    var request = {
      countryCode: 'JP',
      currencyCode: 'JPY',
      supportedNetworks: ['masterCard'],
      merchantCapabilities: ['supports3DS'],
      total: { label: 'My Shop', amount: '100000' },
    }

金額の内訳を表示する

PaymentRequestに金額の内訳を表示するにはlineItemsプロパティにデータを追加します。

    var request = {
      ...(省略)...
      lineItems: [
                   { type: 'final', label: '商品価格', amount: '1000'},
                   { type: 'final', label: '送料', amount: '500'}
                 ]
    }

type には final, pendingの2種類があり、確定した価格はfinal、未確定な金額にはpendingを表示します。

例えば、ユーザが住所を設定するまで送料が確定しない場合などは送料をpendingにするなどに使えそうです。

配送方法を表示する

shippingMethodsプロパティに配送方法を追加します。

    var request = {
      ...(省略)...
      shippingMethods: [
        { label: '配送方法1', detail: '全国一律', amount: '1000' },
        { label: '配送方法3', detail: '関東', amount: '100' }
      ]
    }

住所の入力欄を出す

requiredShippingContactFieldsrequiredBillingContactFieldsプロパティに表示したい項目を渡すと表示することができます。

    var request = {
      ...(省略)...
      requiredBillingContactFields: [postalAddress], //postalAddress or name
      requiredShippingContactFields: [postalAddress, phone, email, name] //postalAddress, phone, email, or name
    }

住所の初期値を出す

shippingContactbillingContactプロパティに初期値を入れることができます。 私が実装時にハマったところとしてはaddressLinesは配列として渡さないと無視されることです。

    var request = {
      ...(省略)...
      billingContact: {
        addressLines: [桜丘町261,セルリアンタワー],
        locality: '渋谷区',
        postalCode: '150-8512',
        administrativeArea: '東京都',
        country: '日本',
        countryCode: 'JP'
      },
      shippingContact: {
        emailAddress: 'amacou@example.com',
        familyName: 'Amano',
        givenName: 'Yoshihiko',
        addressLines: [桜丘町261,セルリアンタワー],
        locality: '渋谷区',
        postalCode: '150-8512',
        administrativeArea: '東京都',
        country: '日本',
        countryCode: 'JP'
      }
    }

配送タイプ

配送タイプを選ぶことができます。 何も指定しない場合はshippingが設定されたことになります。

    var request = {
      ...(省略)...
      shippingType: [shipping, delivery, storePickup, servicePickup]
    }

PaymentSheetを表示する

下記のようにApplePaySessionインスタンスを作成し、beginメソッドをコールすることでPaymentSheetが表示できます。

     var request = {
       ...(省略)...
     };

     var session = new ApplePaySession(1, request); //1はAPIバージョン
     session.begin();

ただし、このbeginメソッドは、ボタンのクリックなどユーザのアクションコールバックでのみ可能となっています。 そのため、ボタンをクリックした後に通信処理を行い、通信終了後にPaymentSheetを表示しようとするとエラー発生して表示できないので注意が必要です。

ApplePaySessionについての詳細は公式ドキュメントを確認していただくといいかと思います。

Apple Developperに登録したWebサイト(Marchant)であることを認証してもらう

PaymentSheetが表示されると、onvalidatemerchantイベントが発生し、MarchantがAppleに登録されたものかをどうかを認証してもらう必要があります。

クライアントサイドの実装例

onvalidatemerchantコールバックでvalidationURLが返却されるので、それをサーバサイドに送って、validationを行い、結果をsession.completeMerchantValidation(data)としてコールします。

    var request = {
      ...(省略)...
    };
    var session = new ApplePaySession(1, request); //1はAPIバージョン
    session.onvalidatemerchant = function(event) {
      $.ajax({
        type: "post",
        url: '/ON_VALIDATE_MERCHANT_URL', #サーバサイドのエンドポイント
        data:JSON.stringify({
          validationURL: event.validationURL
        }),
        contentType: 'application/json',
        dataType: "json",
        success: function(data) {
          session.completeMerchantValidation(data);
        },
      })
    };
    session.begin();

サーバサイドの実装例

クライアントサイドから渡されたvalidationURLに対して、Appleに登録した情報をcertファイルを使って認証してもいます。

Merchant Identity CertificateはMerchant Identity Certificateを登録を行っていれば、すでにKeychain.appに登録されていますので、右クリックからp12形式のファイルに書き出します。 さらに以下のコマンドを使って書き出したp12形式の証明書をcertファイルに変換するします。 ( この際、ターミナルソフトによってはパスワード入力が上手くいかない事がありましたので、標準のターミナルで行うことをおすすめします )

    openssl pkcs12 -in 書き出した証明書.p12 -out 変換後のファイル.cert -nodes -clcerts

変換したcertファイルを使って、validationURLにリクエストを送ります。

    uri = URI.parse(params[:validationURL])
    client_cert ||= File.read(PATH_TO_CERT_FILE)
    http = Net::HTTP.new(uri.host, 443)
    http.use_ssl = true
    http.cert = OpenSSL::X509::Certificate.new(client_cert)
    http.key = OpenSSL::PKey::RSA.new(client_cert)
    http.ssl_version = :TLSv1_2
    res = http.post(uri.path, '{"merchantIdentifier": "merchant.com.example", "domainName": "example.com", "displayName": "My Shop"}')
    render json: res.body

ユーザの購入情報の入力と選択

PaymentSheetは入力できる自由度があまり高くなく、 ユーザが選択できることは以下の6つだけです。

また、ユーザが行った操作は以下のようにイベントを受け取ることができます。

    var request = {
      ...(省略)...
    };
    var session = new ApplePaySession(1, request);
    session.oncancel = function(event) {...(省略)...} //購入をキャンセルした
    session.onpaymentmethodselected = function(event) { //支払い方法を選択した
      ...(省略)...
      //session.completePaymentMethodSelection(total, lineItems)をコールする
      //totalとlineItemsを更新できる
    }
    session.onshippingcontactselected = function(event) { //配送先を選択した
      ...(省略)...
      //session.completeShippingMethodSelection(status, shippingMethods, total, lineItems)をコールする
      //statusとしてSTATUS_SUCCESSか、STATUS_INVALID_SHIPPING_POSTAL_ADDRESSを設定できる
      //shippingMethods, total, lineItemsを更新できる
    }
    session.onshippingmethodselected = function(event) { //配送方法を選択した
      ...(省略)...
      //session.completeShippingContactSelection(total, lineItems)をコールする
      //totalとlineItemsを更新できる
    }
    session.onpaymentauthorized = function(event) { //ユーザが購入ボタンを押してTouchIDでの認証が完了した
      ...(省略)...
      //session.completePayment(status)をコールする
      //statusとしては以下のいずれかを設定
      // STATUS_SUCCESS
      // STATUS_FAILURE
      // STATUS_INVALID_BILLING_POSTAL_ADDRESS
      // STATUS_INVALID_SHIPPING_POSTAL_ADDRESS
      // STATUS_INVALID_SHIPPING_CONTACT
      // STATUS_PIN_REQUIRED
      // STATUS_PIN_INCORRECT
      // STATUS_PIN_LOCKOUT
    }

上記のように、入力された住所によって、配送方法の選択肢を変更する、住所に誤りがあることを示すなどもコールバックによって可能となっています。

その他注意が必要なこと

住所は購入が確定するまでは一部分しか取得できないようになっていました。 また、購入が完了した、購入がキャンセルされた、購入が途中で終了されたというステータスを見逃しやすいので、購入が完了した際にはそれがわかる画面に遷移するなど、ユーザを混乱させないことも必要でした。

まとめ

以上簡単ではありましたが、Apple Pay JSをWebサイトで導入する際の手順を紹介しました。