[Swift] Webpay で iOS アプリからクレジットカード決済を試す(CardIOも使うよ)


iOS アプリの決済周りを調べてみたのでメモ。

# iOS アプリでお金を払うということ

iOS アプリからの支払いについては「Apple に3割もっていかれる」というぼんやりしたイメージしかなったのですが調べてみると、

## In-App Purchaseを使用して販売可能なもの

* コンテンツ
  
  雑誌、写真、アートワークなどのデジタルコンテンツまたはデジタルアセット
  
* アプリケーションの機能
 
 既に配信済みのアプリケーションでロックされている動作の解除や機能の拡張を行います。
 
* サービス

  音声の転送などの1回限りのサービス、またはデータのコレクションへのアクセスなどの継続するサービスに対して、ユーザに支払いを求めます。

## In-App Purchaseを使用して販売できないもの

* 実物の商品やサービス

アプリケーション内でユーザに実物の商品やサービスを購入してもらうには、
クレジットカードや支払いサービスなどの別の支払いメカニズムを使用してください。

参考：https://developer.apple.com/jp/documentation/StoreKitGuide.pdf

# EC など実物を売る場合は In-App Purchase は使えない

物販するアプリなどを作る場合は自前で支払いする仕組みを用意しないといけないとのこと。

そこで調べたのが [WebPay](https://webpay.jp/) というサービスでREST API でクレジットカードによる決済が簡単に導入できるらしい。

iOS 向けにも[ライブラリ](https://github.com/webpay/webpay-token-ios)も公開されています。

# Webpay による決済の流れ

おおまかには下記のような感じ。

1. ユーザにクレジットカード情報を入力してもらう
2. Webpay にその情報を送り、token を生成してもらう
3. token を使って決済(charge)を行う 

流れはこんな感じなのですが、iOS 向けの[サンプルコード](https://github.com/webpay/webpay-token-ios-sample-card.io)が古くて動かなかったので自分で作ってみた。

## Setup

Webpay にログインして公開鍵と非公開鍵を取得。公開鍵は Webpay ライブラリに渡します。非公開鍵は Webpay の REST API を呼び出す際に Basic 認証のユーザーにセットします。

```公開鍵の設定
WPYTokenizer.setPublicKey(WebpayClient.publicKey)
```

## Token の取得

Webpay にクレジットカード情報を渡し、その後アプリ側では代わりに Token を使います。クレジットカード情報を保持しなくていいので楽ですね。

クレジットカードの入力フォームが Webpay ライブラリに含まれており、 WPYPaymentViewController を呼び出すと下記のようなフォームが簡単に使うことができます。

![2015-12-06 22.47.54.png](https://qiita-image-store.s3.amazonaws.com/0/27210/ff5621dd-22f0-b4e9-daa7-869f3c9165c1.png "2015-12-06 22.47.54.png")

クレジットカードを入力し、「カードで支払う」ボタンをタップすると Token が生成され、結果が callback で渡されます。

```
        let paymentViewController = WPYPaymentViewController(priceTag: "¥350", card: card) { [unowned self] (viewController, token, error) -> Void in
            if let newError = error {
				// エラー処理
            } else {
                // ここで Token を受け取る

                // when transaction is complete
                viewController.setPayButtonComplete()
                viewController.dismissAfterDelay(2.0)
            }
        }

```

## カード決済

Token を使ってカード決済を行います。（Token は一度しか使えません。このやり方だと決済の度にクレジットカードを入力してもらわないといけません。実際に運用を見据えた場合は Token を使ってユーザーを生成する方法を使います。）

|キー|意味|
|:--|:--|
|currency|通貨|
|card|生成した Token|
|amount|金額|
|capture|実決済するかどうか|

決済 API は Alamofire を使って呼び出します。パラメータの capture がポイントで、公式ドキュメントによると、

> すぐに実売上にするか、仮売上として後で実売上化するかを指定します。falseの場合に与信のみが行われ、後で「仮売上の実売上化」をすることで実売上化できます。

とあります。

参考：https://webpay.jp/docs/api#charge_create

物販などで、
1. 購入された時点で仮売上
2. 商品を発送した時点で実売上(実際に決済を行う)
という使い方になるのではないかと思います。

いきなり実売上にすることもできるようなのでここでは capture = true で呼び出してみました。
非公開鍵を使って認証をしています。

```
    class func charge(amount: Int, token: String, handler: ((Bool) -> Void)) {
        let param: [String : AnyObject] = ["amount": amount,
            "currency" : "jpy",
            "capture" : true,
            "card" : token]
        Alamofire.request(.GET, "https://api.webpay.jp/v1/charges", parameters: param, encoding: .URL, headers: ["Authorization": "Bearer \(WebpayClient.secretKey)"])
            .responseJSON { response in
                print(response.request)  // original URL request
                print(response.response) // URL response
                print(response.data)     // server data
                print(response.result)   // result of response serialization
                
                handler(response.result.isSuccess)
        }
    }

```

## CardIO を使ってクレジットカード情報を読み取る

PayPal が出してるライブラリに [CardIO](https://github.com/card-io/card.io-iOS-SDK) というものがあり、クレジットカード情報を読み込んでくれます。

＃エンボス(凹凸)加工がないカードはうまく読み取れないかもしれません

使い方は簡単で、
1. CardIOPaymentViewController を呼び出す
2. クレジットカードをかざす
3. CardIOPaymentViewControllerDelegate で結果が渡される

となります。

#### CardIOPaymentViewController の呼び出し
```
    func onCameraTapped(sender: UIBarButtonItem) {
        let cardIOVC = CardIOPaymentViewController(paymentDelegate: self)
        cardIOVC.modalPresentationStyle = .FormSheet
        presentViewController(cardIOVC, animated: true, completion: nil)
    }

```

#### 結果の受け取り

```
    // MARK: CardIOPaymentViewControllerDelegate
    func userDidCancelPaymentViewController(paymentViewController: CardIOPaymentViewController!) {
        paymentViewController?.dismissViewControllerAnimated(true, completion: nil)
    }
    
    func userDidProvideCreditCardInfo(cardInfo: CardIOCreditCardInfo!, inPaymentViewController paymentViewController: CardIOPaymentViewController!) {
        if let info = cardInfo {
            // ここで情報を受け取る
            card.number = info.cardNumber
            card.expiryMonth = WPYMonth(rawValue: info.expiryMonth)!
            card.expiryYear = info.expiryYear
            card.cvc = info.cvv
            
            // 受け取った情報を WebPay のフォームに渡す
            form.removeFromSuperview()
            form = WPYCardFormView(frame: cardBaseView.bounds, card: card)
            form.delegate = self
            cardBaseView.addSubview(form)
        }
        paymentViewController?.dismissViewControllerAnimated(true, completion: nil)
    }

```

#### CardIO でのクレジットカード情報の読み取り

WebPay が公開しているデモビデオがありました、こんな感じでクレジットカードが読み取れます。

https://vimeo.com/95709998

# サンプルコード

今回説明したサンプルプロジェクト一式はこちらです。

サンプルコードは[こちら](https://github.com/takecian/webpay-cardio-swift)。

WebPay でとても簡単に決済が導入できそうです。