[iOS 10] アプリを Apple Pay に対応させるために必要な実装について

2016.10.21

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは。モバイルアプリサービス部の平屋です。前回の記事に引き続き、Apple Pay についての情報を紹介していきます。

本記事ではアプリを Apple Pay に対応させる場合に必要な実装を解説していきます。

Apple Pay の概要や環境構築などについては以下の記事などをご覧ください。

検証環境

  • Xcode Version 8.0 (8A218a)
  • iPhone 6s, iOS 10.0.2

目次

[1] PassKit をインポート

Apple Pay まわりの処理では PassKit フレームワークのクラスを使用します。PassKit をインポートします。

import PassKit

[2] Apple Pay の利用可否をチェックする

まずは、Apple Pay を利用できる状態かどうかをチェックします。

Apple Pay 自体が使用できる状態かどうか

PKPaymentAuthorizationViewControllercanMakePayments() メソッドを使用すれば、Apple Pay 自体が使用できる状態かどうかをチェックできます。ハードウェアが対応していなかったり、機能制限がかかっていたりする場合、このメソッドは false を返します。

if PKPaymentAuthorizationViewController.canMakePayments() {
    // Apple Pay に対応している
} else {
    // Apple Pay に対応していない
}

クレジットカードが「Wallet」アプリに登録済みかどうか

Apple Pay を利用した決済では、標準の「Wallet」アプリに登録済みのクレジットカードを使用します。

PKPaymentAuthorizationViewControllercanMakePayments(usingNetworks:) メソッドを使用すれば、クレジットカードが登録済みかどうかをチェックできます。

// 利用可能な決済ネットワーク (MasterCard, Visa など) を取得
let networks = PKPaymentRequest.availableNetworks()

if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: networks) {
    // 利用可能な決済ネットワークが 1 つ以上ある場合 (クレジットカードが登録済みの場合)
} else {
    // 利用可能な決済ネットワークがない場合
}

[3] Apple Pay ボタンを表示する

Apple Pay ボタンは決済開始のトリガーを提供するボタンです。

ios-10-applepay-for-developers-3-01

実装的には PKPaymentButton を使用します。typestyle を指定して PKPaymentButton を作成します。

private func preparePayButton() {
    let paymentButton = PKPaymentButton(type: PKPaymentButtonType.plain,
                                        style: PKPaymentButtonStyle.black)
    paymentButton.addTarget(self,
                            action: #selector(ViewController.pay),
                            for: .touchUpInside)
    self.view.addSubview(paymentButton)
    self.prepareButtonConstraints(button: paymentButton)
}

クレジットカードが未登録の場合

クレジットカードが未登録の場合は typePKPaymentButtonType.setUp を指定して、カード登録を誘導する外観のボタンを表示できます。

ios-10-applepay-for-developers-3-02

private func prepareSetUpButton() {
    let setUpButton = PKPaymentButton(type: PKPaymentButtonType.setUp,
                                      style: PKPaymentButtonStyle.black)
    setUpButton.addTarget(self,
                          action: #selector(ViewController.setUp),
                          for: .touchUpInside)
    self.view.addSubview(setUpButton)
    self.prepareButtonConstraints(button: setUpButton)
}

typePKPaymentButtonType.setUp のボタンが押された時に PKPassLibraryopenPaymentSetup() メソッドを呼ぶようにします。

func setUp(sender: AnyObject) {
    PKPassLibrary().openPaymentSetup()
}

OS が提供するクレジットカード追加画面に遷移させることができます。

ios-10-applepay-for-developers-3-04

[4] ペイメントリクエストを作成する

ペイメントシートを表示する前に、PKPaymentRequest を作成し、決済まわりの設定や金額などの情報を格納します。

ここの実装はこちらの記事の手順 [1] に対応します。

func pay(sender: AnyObject) {
    // PKPaymentRequest作成
    let request = PKPaymentRequest()

    // マーチャント ID を指定
    request.merchantIdentifier = "merchant.com.example.myMerchant"

    // ISO 3166 country code (2文字) を指定
    request.countryCode = "JP";

    // ISO 4217 currency code を指定
    request.currencyCode = "JPY";

    // サポートする決済ネットワークを指定
    request.supportedNetworks = PKPaymentRequest.availableNetworks()

    // サポートするプロトコルを指定
    request.merchantCapabilities = .capability3DS // capability3DS は必須

    // 金額を指定
    request.paymentSummaryItems = [
        // 商品の金額、送料、割引額、合計の 3 つを表示する場合
        PKPaymentSummaryItem(label: "商品の金額", amount: NSDecimalNumber(string: "1000")),
        PKPaymentSummaryItem(label: "送料", amount: NSDecimalNumber(string: "100")),
        PKPaymentSummaryItem(label: "割引額", amount: NSDecimalNumber(string: "100")),
        PKPaymentSummaryItem(label: "MyMerchant", amount: NSDecimalNumber(string: "1000")) // 合計額
    ]

    // ...
}

請求先や配送先の情報が必要な場合は、requiredShippingAddressFieldsrequiredBillingAddressFields プロパティなどに値を指定します。

func pay(sender: AnyObject) {
    // ...

    // 請求先や配送先のフィールドのタイプを指定
    request.requiredShippingAddressFields = .postalAddress
    request.requiredBillingAddressFields = .postalAddress

    // 配送方法
    let shippingMethod = PKShippingMethod(label: "Japan Post", amount: NSDecimalNumber(string: "100"))
    shippingMethod.identifier = "Japan Post"
    shippingMethod.detail = "到着: 3-4 週後"
    request.shippingMethods = [shippingMethod]

    // ...
}

[5] ペイメントシートを表示させる

ios-10-applepay-for-developers-3-03

ペイメントシートのビューは PKPaymentAuthorizationViewController が管理します。PKPaymentRequest から PKPaymentAuthorizationViewController を作成し、ペイメントシートを表示させます。

ここの実装はこちらの記事の手順 [2] に対応します。

func pay(sender: AnyObject) {
    // ...

    // PKPaymentRequest から PKPaymentAuthorizationViewController を作成し、表示
    let viewController = PKPaymentAuthorizationViewController(paymentRequest: request)
    viewController.delegate = self
    self.present(viewController,
                 animated: true,
                 completion: nil)
}

[6] ペイメントシートまわりのイベントをハンドリングする

PKPaymentAuthorizationViewController を表示する ViewController を PKPaymentAuthorizationViewControllerDelegate に適合させると、ペイメントシートまわりのイベントをハンドリングできるようになります。

実装必須のメソッドについて

実装が必須のメソッドは paymentAuthorizationViewController(_:didAuthorizePayment:completion:)paymentAuthorizationViewControllerDidFinish(_:) の 2 つです。

(1) paymentAuthorizationViewController(_:didAuthorizePayment:completion:)

このメソッドはユーザーがペイメントリクエストを承認した時に呼ばれます。

引数 payment から以下の情報を取得できます。ペイメントトークンのフォーマットはこちらのページに載っています。

  • 暗号化済みのペイメントトークン
  • 請求先情報 (PKPaymentRequest 作成時に requiredBillingAddressFields プロパティに値を指定した場合)
  • 配送先情報 (PKPaymentRequest 作成時に requiredShippingAddressFields プロパティに値を指定した場合)
  • 配送方法 (PKPaymentRequest 作成時に shippingMethods プロパティに値を指定した場合)

アプリのバックエンド側に上記の情報を送り、決済処理を進めます。

決済処理が正常終了したタイミングで引数に .success を指定して completion を呼びます。

ここの実装はこちらの記事の手順 [5] に対応します。

// ユーザーがペイメントリクエストを承認した時に呼ばれる
func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController,
                                        didAuthorizePayment payment: PKPayment,
                                        completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {
    // 暗号化済みのペイメントトークン
    // payment.token

    // ペイメントリクエストの requiredBillingAddressFields に値を指定した場合、値が入る
    // payment.billingContact
    // ペイメントリクエストの requiredShippingAddressFields に値を指定した場合、値が入る
    // payment.shippingContact
    // ペイメントリクエストの shippingMethods に値を指定した場合、値が入る
    // payment.shippingMethod

    // アプリのバックエンド側に上記の情報を送り、決済処理を進める
    // ...

    // 決済処理が正常終了した場合は、`completion` に `.success` つっこむ
    completion(.success)
}

(2) paymentAuthorizationViewControllerDidFinish(_:)

このメソッドは決済の全ての処理が終わった時、または決済がキャンセルされた時に呼ばれます。PKPaymentAuthorizationViewController を閉じる処理を実行します。

func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
    self.dismiss(animated: true,
                 completion: nil)
}

実装任意のメソッドについて

実装が任意のメソッドは 4 つです。

(1) paymentAuthorizationViewControllerWillAuthorizePayment(_:)

このメソッドは、ユーザーによるペイメントリクエスト承認が完了する直前に呼ばれます。

func paymentAuthorizationViewControllerWillAuthorizePayment(_ controller: PKPaymentAuthorizationViewController) {
    // ...
}

(2) paymentAuthorizationViewController(_:didSelect:completion:)、第二引数の型 : PKPaymentMethod

このメソッドは支払い方法が変更された時に呼ばれます。

例えば、特定の支払い方法のときに割引をする場合はこのメソッド内で合計金額などを更新します。

func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController,
                                        didSelect paymentMethod: PKPaymentMethod,
                                        completion: @escaping ([PKPaymentSummaryItem]) -> Void) {
    // ...
}

(3) paymentAuthorizationViewController(_:didSelect:completion:)、第二引数の型 : PKShippingMethod

このメソッドは配送方法が変更された時に呼ばれます。

例えば、配送方法によって、配送料が変わる場合は、このメソッド内で配送料などを更新します。

func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController,
                                        didSelect shippingMethod: PKShippingMethod,
                                        completion: @escaping (PKPaymentAuthorizationStatus, [PKPaymentSummaryItem]) -> Void) {
    // ...
}

(4) paymentAuthorizationViewController(_:didSelectShippingContact:completion:)

このメソッドは配送先が変更された時に呼ばれます。

例えば、配送先によって配送方法や配送料が変わる場合は、このメソッド内で配送方法や配送料を更新します。

func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController,
                                        didSelectShippingContact contact: PKContact,
                                        completion: @escaping (PKPaymentAuthorizationStatus, [PKShippingMethod], [PKPaymentSummaryItem]) -> Void) {
    // ...
}

さいごに

本記事ではアプリを Apple Pay に対応させる場合に必要な実装について解説しました。アプリ内での支払いに Apple Pay を採用すれば、決済のフロント部分の実装工数を小さくできそうだと思いました。

2016/10/21 現在、日本ではまだサービスインしていませんが、今後広く普及していくことを期待しています!

参考資料

WWDC Documents

API Reference

その他