WebにApplePayを導入したので、その開発・実装手順を紹介します。
ApplePay on the Webの最新情報や詳しい実装方法などは公式ドキュメントを参照してください。
Apple Pay on the Web | Apple Developer Documentation
目次
前提条件
Apple Pay on the Webのテストできる条件は以下の通りです。
- Apple Pay実装に必要なものが用意されていること
- merchant IDに登録した認証済みドメインのみテスト可能
- 決算する店舗側にはSSLの導入が必須(TLS1.2)
- 以下のiOS・macOSバージョンであること(最新情報は公式ドキュメントを参考にしてください)
- Apple Pay JS API : OS 10より最新、macOS 10.12より最新
- Payment Request API : iOS 11.3 10より最新、macOS 10.1210より最新
Choosing an API for Implementing Apple Pay on Your Website | Apple Developer Documentation
Apple Pay on the Web実装に必要なもの
merchant ID
Apple Payに対して身元を証明する一意の識別子。
Apple Developerにログインし、Merchant IDを登録します。
Merchant Identity Certificate
Appleとのセッションを確立するには、マーチャントID証明書が必要です。
このマーチャントID証明書はサーバにアップロードします。
作成方法
Apple Developerで「Apple Pay Merchant Identity Certificate」を作成し、ダウンロードします。その後、キーチェーンアクセスからP12ファイルを作成します。
以下コマンドより、証明書と秘密鍵ファイルをpem形式で抽出します。この2ファイルと秘密鍵ファイルで設定したパスワードは後に使用します。
1 | $ openssl pkcs12 -in ApplePayMerchantIdentity.p12 -out ApplePay.crt.pem -clcerts -nokeys |
1 | $ openssl pkcs12 -in ApplePayMerchantIdentity.p12 -out ApplePay.key.pem -nocerts |
以下のエラーが出たらRSAの公開鍵を作成し、秘密鍵作成で設定したパスワードを削除します。
1 | unable to load client key: -8178 (SEC_ERROR_BAD_KEY) |
1 | $ openssl rsa -in ApplePay.key.pem -out ApplePayRsa.key.pem |
curl - SSL certificate generated with OpenSSL not working on NSS - Stack Overflow
Sandbox環境
決済カードで取引をテストするには、ApplePayサンドボックス環境を使用します。
テストする実機
実機でテストする場合は、Sandboxアカウントに変更する必要があります。
デバッグ
今回はiPhone端末をテスト実機としたので、Macと接続してデバッグを行いました。
iPhone端末で、「設定」→「Safari」→「詳細」→「Webインスペクタ」を有効にします。
その後、iPhoneとMacをUSBケーブルで繋ぎ、MacのSafariから「開発」を選択肢、接続したiPhone端末を選択します。これでデバッグ準備完了です。
フロント側(HTML、CSS、JS)を変更する場合、「設定」→「Safari」→「履歴とWebサイトデータを消去」を選択しないとキャッシュが残ってしまうので、少々めんどくさいです。
証明書をサーバにアップロード
先ほど作成した、
- ApplePay.crt.pem
- ApplePay.key.pem
をサーバにアップロードします。public配下に配置するのは簡単にダウンロードされてしまうので避けましょう。
例えばルートディレクトリが/var/www/htmlの場合は、/var/www配下にapplepay_includesのようなディレクトリを作成し、その配下に格納しましょう。
1 2 3 | /var/www/applepay_includes ・ApplePay.crt.pem ・ApplePay.key.pem |
ApplePayに対応しているかどうか判定
まずApplePayに対応しているかを以下の処理で判定します。
Apple Payに対応してるSafariには、window.ApplePaySessionオブジェクトが存在しています。
そのため、if (window.ApplePaySession) でtrueの場合はApplePayに対応してるSafariであると判定できます。
ApplePaySession.canMakePaymentsWithActiveCard()メソッドで、ユーザのデバイスがApplePayをサポートしている及びユーザーのWalletでアクティブなカードを持っているかどうかを判定します。
trueの場合ApplePayのボタンを表示します。
1 2 3 4 5 6 7 8 9 10 11 | if (window.ApplePaySession) { // merchantIdentifierには使用するマーチャントIDを定義 var merchantIdentifier = 'example.com.store'; var promise = ApplePaySession.canMakePaymentsWithActiveCard(merchantIdentifier); promise.then(function (canMakePayments) { if (canMakePayments) { // Apple Payボタンを表示 $("#applepay_button").show(); } }); } |
PaymentRequestの作成
商品情報
paymenRequestに商品情報を追加します。
countryCode
2文字の国コードを指定します。日本はJP。
currencyCode
3文字の通貨コードを指定します。日本円はJPY。
supportedNetworks
マーチャントがサポートしている支払方法です。
特定のApple Payバージョンで導入されたネットワークを使用するには、ApplePaySessionのバージョンを次のように必要なバージョン番号に設定します。
詳しくは公式ドキュメントを参照してください。
merchantCapabilities
マーチャントがサポートしている決済機能です。
- merchantCapabilitiesでサポートされているのは以下の通りです。
- supports3DS – 必須。この値は必ず指定する必要があります。
- supportsCredit – オプション。クレジットカードに分類されている取引のみ許可します。
- supportsDebit – オプション。デビットカードに分類されている取引のみ許可します。
- supportsEMV – China Union Pay取引をサポートしている場合にのみ、指定する必要があります。
詳しくは公式ドキュメントを参照してください。
total
商品名と合計金額を記載します。実装の際は変数を入れるなどして置き換えてください。
1 2 3 4 5 6 7 | var paymenRequest = { countryCode: 'JP', currencyCode: 'JPY', supportedNetworks: ['visa', 'jcb', 'masterCard'], merchantCapabilities: ['supports3DS',], total: { label: 'りんご', amount: '150' } }; |
ApplePay Session確立【フロントエンド】
ApplePaySession
ApplePaySessionは、サポートされているApple Payのバージョンと商品情報を引数にし、インスタンスを作成します。サポートバージョンは公式ドキュメントを参照してください。
session.begin()が呼び出されると、支払シートが表示されセッション確立処理が開始されます。
session.onvalidatemerchant()は、支払いシートが表示されたときに呼び出され、有効なマーチャントであるかをサーバに呼び出し検証します。validationURLはマーチャントを認証するApplePayサーバです。
サーバの呼び出しは performValidation()で行っています。 認証リクエストはサーバから実行する必要があります。
サーバは認証リクエストを使用してApple Payサーバにセッションを要求します。セッション結果をMerchantSessionで受け取り、session.completeMerchantValidationに渡します。
認証が成功すると支払いシートが有効になり、セッションの確立が完了します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | var session = new ApplePaySession(2, paymenRequest ); // Merchant Validation session.onvalidatemerchant = function (event) { console.log(event); var promise = performValidation(event.validationURL); promise.then(function (merchantSession) { session.completeMerchantValidation(merchantSession); }); } function performValidation(valURL) { return new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest(); xhr.onload = function() { console.log(this.responseText); var data = JSON.parse(this.responseText); console.log(data); resolve(data); }; xhr.onerror = reject; xhr.open('POST', '/path/to/applepay/serverside', true); xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr.send('validationUrl='+valURL); }); } session.begin(); |
ApplePay Session確立【サーバサイド】
ApplePayのマーチャントID証明書やパスワードなどを設定ファイルに記載します。
developやstaging、本番環境などで設定を変更する必要がある場合は、それぞれの環境で設定ファイルを用意しておきましょう。
・config/applepay_conf.php
1 2 3 4 5 6 7 8 9 10 11 | <?php return [ 'certificate_key' => '/path/to/ApplePay.key.pem', 'certificate_path' => '/path/to/ApplePay.crt.pem', 'certificate_key_pass' => 'password', 'merchantidentifier' => 'merchantidentifier', 'currency_code' => 'JPY', 'country_code' => 'JP', 'display_name' => 'display_name', ]; |
認証処理はサーバサイドで行います。サーバサイドはPHPを使用(フレームワークはFuelPHP)。
設定ファイルを読み込み、認証処理に必要なリクエストをサーバに送ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <?php class Controller_Applepay { public function post_applepay() { \Config::load('applepay_conf', true); $validation_url = $_POST['validationUrl']; if( "https" == parse_url($validation_url, PHP_URL_SCHEME) && substr( parse_url($validation_url, PHP_URL_HOST), -10 ) == ".apple.com" ) { $ch = curl_init(); $post_data = '{"merchantIdentifier":"' . \Config::get('applepay.merchantidentifier') . '", "domainName":"' . $_SERVER["HTTP_HOST"].'", "displayName":"' . \Config::get('applepay.display_name') . '"}'; curl_setopt($ch, CURLOPT_URL, $validation_url); curl_setopt($ch, CURLOPT_SSLCERT, \Config::get('applepay.certificate_path')); curl_setopt($ch, CURLOPT_SSLKEY, \Config::get('applepay.certificate_key')); curl_setopt($ch, CURLOPT_SSLKEYPASSWD, \Config::get('applepay.certificate_key_pass')); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); if(curl_exec($ch) === false) { echo '{"curlError":"' . curl_error($ch) . '"}'; } } } } |
ApplePay決済処理
session.onpaymentauthorized()は、ユーザがタッチIDやパスコードなどによってApplePayの支払が承認されたときに呼び出されます。
ApplePayの支払いは、30秒のタイムアウト前にsession.completePayment()を呼び出して応答することで完了します。
30秒以内に支払いを完了できなかった場合、「支払いを完了できませんでした」とエラーメッセージが表示され、支払いが完了せずに終了します。
決済代行サービスを利用している場合は、この段階でトークンが取得できるため、ユーザ情報や商品情報などをあわせてサーバに送ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | session.onpaymentauthorized = function (event) { console.log('starting session.onpaymentauthorized'); var promise = sendPaymentToken(event.payment.token); promise.then(function (success) { var status; if (success){ status = ApplePaySession.STATUS_SUCCESS; console.log('Apple Pay Payment SUCCESS '); } else { status = ApplePaySession.STATUS_FAILURE; } session.completePayment(status); }); } function sendPaymentToken(paymentToken) { return new Promise(function(resolve, reject) { resultApplepaySettle = createTransaction(paymentToken.paymentData); if (resultApplepaySettle == true) { resolve(true); } else { reject; } }); } function createTransaction(dataObj) { console.log('starting createTransaction'); let objJsonStr = JSON.stringify(dataObj); let objJsonB64 = window.btoa(objJsonStr); $.ajax({ url: "/path/to/applepay/serverside/third-party/payment/provider/", data: { label: label, total_price: total_price, apple_token: objJsonB64 }, method: 'POST', timeout: 5000 }).done(function(data){ console.log('createTransaction Success'); }).fail(function(){ console.log('createTransaction Error'); }) return true; } |
以上、ApplePay on the Webを実装する方法をまとめました。最新情報や詳しい実装方法などは公式ドキュメントを参照してください。
Apple Pay on the Web | Apple Developer Documentation
ちょっとハマったところ
Apple Payのセッション確立時にApple側から以下のエラーメッセージを受け取るも原因分からず苦戦。
1 2 3 4 | { "statusMessage": "Error processing request", "statusCode": "500" } |
curl_exec($ch)には1(true)が返ってきているので成功しているのではと思ったのですが、フロント側で受け取る際は失敗。
もう少し具体的な原因を教えてくれたら助かるのですが…。Apple側に送るリクエストを全て見直したところ、原因は単純に証明書が間違っていました。