Stripe で Apple Pay を使う

[Stripe](https://stripe.com/jp) と [Apple Pay](https://www.apple.com/jp/apple-pay/) の連携について調べているので、その調査結果です。

# [Apple Pay in Apps - stripe.com](https://stripe.com/docs/apple-pay/apps)

Stripe で Apple Pay を使うときの概要。

## ApplePay に必要なもの

* merchant ID
* entitlements to your app

## 必須条件

* iOS8.1 以上
* a newer iOS device (iPhone 6 or 6+, iPad Air 2, or iPad mini 3)

`Stripe.deviceSupportsApplePay()` などのメソッドがあるのでそれで判断できる。 

## 手数料

* Stripe を使うので手数料がかかる(3.6%)
* IAP ではないので 30% は取られない

## Apple Merchant ID

http://developer.apple.com/account/ios/identifiers/merchant/merchantCreate.action
ここで Merchant ID が 取得できる。

### Apple Pay vs. In-App Purchases

Apple Pay は IAP の置き換えにはならない。
Apple Pay は物理商品を買うため、 IAP はデジタル商品を買うためなどの役割がある。

## Creating a new Apple Pay certificate

3ステップあって、

1. Stripe から CSR を入手
    * https://dashboard.stripe.com/account/apple_pay から取得
2. それを使って Apple の certificate を生成
    * http://developer.apple.com/account/ios/certificate/certificateCreate.action
    * ここで DL した certificate を stripe にアップロードするから、これは使わない
3. その certificate を stripe に登録

<img src="https://qiita-image-store.s3.amazonaws.com/0/5678/3b0a1156-2800-70d9-db9f-4f1d7c133e4a.png" width="70%" />


## Integrating with Xcode

Capabilities の Apple Pay を ON にして merchant id を stripe に渡す。


```swift
stripeConfig.appleMerchantIdentifier = "merchant id"
```

## 実行する

Stripe Starndard Integration のサンプルを動かすと Apple Pay が有効になってる、購入直前まで行けた(購入はサーバサイドなので検証にちょっと手間)。
しかし Standard だと Stripe 側に住所入力をした後に Apple Pay 支払いになるため、 Apple Pay に登録した住所を利用することができなかった。

そのため Custom Integration をやる必要がありそう。

# [Custom iOS Integration#apple-pay - Stripe](https://stripe.com/docs/mobile/ios/custom#apple-pay)

Stripe に縛られずに Apple Pay を使う。

## PKPaymentRequest

```swift
let merchantIdentifier = "merchant id"
let paymentRequest = Stripe.paymentRequest(withMerchantIdentifier: merchantIdentifier, country: "JP", currency: "JPY")
```

これで `PKPaymentRequest` を取得できる。
これは Apple が提供しているもので、Apple Pay でユーザに入力して欲しい設定などを追加できる。 [PKPaymentRequest \- PassKit \| Apple Developer Documentation](https://developer.apple.com/documentation/passkit/pkpaymentrequest)

[Apple Payプログラミングガイド：ペイメントリクエストの作成](https://developer.apple.com/jp/documentation/ApplePay_Guide/CreateRequest.html#//apple_ref/doc/uid/TP40014764-CH3-SW2) に詳しい説明がある。

## アイテムと合計を設定

```swift
paymentRequest.paymentSummaryItems = [
    PKPaymentSummaryItem(label: "Fancy Hat", amount: 500.00),
    PKPaymentSummaryItem(label: "iHats, Inc", amount: 1000.00),
    PKPaymentSummaryItem(label: "Total", amount: 1500.00),
]
```

合計金額は最後の行に設定すること。

## 入力必須項目の設定

```swift
paymentRequest.requiredShippingContactFields = [.postalAddress]
```

配送先住所も入力して欲しいので、 `.postalAddress` を設定。
なお、 `requiredShippingContactFields` は iOS11 からので、iOS10 以下は `requiredShippingAddressFields` を使う必要がある。

## Apple Pay の View を表示

```swift
let paymentAuthorizationViewController = PKPaymentAuthorizationViewController(paymentRequest: paymentRequest)!
paymentAuthorizationViewController.delegate = self
present(paymentAuthorizationViewController, animated: true)
```

これで画面下からニュッっと Apple Pay の支払い画面が出てくる。

## Payment Token を受け取る

[paymentAuthorizationViewController\(\_:didAuthorizePayment:handler:\) \- PKPaymentAuthorizationViewControllerDelegate \| Apple Developer Documentation](https://developer.apple.com/documentation/passkit/pkpaymentauthorizationviewcontrollerdelegate/2865759-paymentauthorizationviewcontroll) の Delegate を実装する。

ユーザが Apple Pay での支払いを行うと、 PKPayment オブジェクトを取得できる。  PKPayment にはユーザが入力した住所情報が含まれているため、このタイミングで Apple Pay で設定した住所を取得できる。
この PKPayment を Stripe SDK に渡してカードトークンを取得し、サーバサイドで決済を行う。

```swift
    func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {
        STPAPIClient.shared().createToken(with: payment) { (token: STPToken?, error: Error?) in
            guard let token = token else {
                print(error?.localizedDescription ?? "")
                return
            }

            print(token)
            // サーバにトークンを送信して支払いを行う。
            // 失敗した場合は completion(.failure)
            // 成功した場合は ↓
            completion(.success)
        }
    }
```

## 全体像

<img src="https://qiita-image-store.s3.amazonaws.com/0/5678/481b8238-c87f-ff80-654f-bbaace88094c.gif" width="30%" />

```swift
extension StripeSamplesTableViewController: PKPaymentAuthorizationViewControllerDelegate {
    private func applePay() {
        print(#function)
        let merchantIdentifier = "merchant id"
        let paymentRequest = Stripe.paymentRequest(withMerchantIdentifier: merchantIdentifier, country: "JP", currency: "JPY")

        // Configure the line items on the payment request
        paymentRequest.paymentSummaryItems = [
            PKPaymentSummaryItem(label: "Fancy Hat", amount: 500.00),
            PKPaymentSummaryItem(label: "iHats, Inc", amount: 1000.00),
            PKPaymentSummaryItem(label: "Total", amount: 1500.00),
        ]

        paymentRequest.merchantCapabilities = .capability3DS
        paymentRequest.requiredShippingContactFields = [.postalAddress]

        if Stripe.canSubmitPaymentRequest(paymentRequest) {
            // Setup payment authorization view controller
            let paymentAuthorizationViewController = PKPaymentAuthorizationViewController(paymentRequest: paymentRequest)!
            paymentAuthorizationViewController.delegate = self

            // Present payment authorization view controller
            present(paymentAuthorizationViewController, animated: true)
        }
        else {
            // There is a problem with your Apple Pay configuration
        }
    }

    func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {
        print(#function)
        print(payment.shippingContact?.postalAddress?.country ?? "")
        print(payment.shippingContact?.postalAddress?.city ?? "")

        STPAPIClient.shared().createToken(with: payment) { (token: STPToken?, error: Error?) in
            guard let token = token else {
                print(error?.localizedDescription ?? "")
                return
            }

            print(token)
            completion(.success)

//            submitTokenToBackend(token, completion: { (error: Error?) in
//                if let error = error {
//                    // Present error to user...
//
//                    // Notify payment authorization view controller
//                    completion(.failure)
//                }
//                else {
//                    // Save payment success
//                    paymentSucceeded = true
//
//                    // Notify payment authorization view controller
//                    completion(.success)
//                }
//            })
        }
    }

    func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
        print(#function)
        dismiss(animated: true, completion: nil)
//        dismiss(animated: true, completion: {
//            if (paymentSucceeded) {
//                // Show a receipt page...
//            }
//        })
    }
}
```

## 参考

* [Apple Payプログラミングガイド：Apple Payについて](https://developer.apple.com/jp/documentation/ApplePay_Guide/index.html#//apple_ref/doc/uid/TP40014764-CH1-SW1)
* [iOS Integration - Stripe](https://stripe.com/docs/mobile/ios)