iPhoneアプリ上のクレカ決済をStripeで実装する

iPhoneアプリ上で課金機能を実装するにあたり、決済サービスStripe (https://stripe.com/jp) を使用しました。日本語のドキュメントが少なかったので、実装の手順を紹介したいと思います。
# Stripeとは？
公式ホームページの説明文を引用します。
>Stripe はインターネットで商品取引を行うための最もパワフルで柔軟なツールです。Stripe の精密に設計された API と無類の機能は、定期支払いサービス、シェアリングエコノミー、Eコマース、クラウドファンディングプラットフォームなど、最高の製品を作り出すのに役立ちます。何千万社もの世界最先端企業が Stripe で素早く効率的に、事業を成長させています。

クレジットカード決済は、取り扱う情報の機密性が高く、導入を躊躇してしまいがちです。しかしStripeを使えば、機密性の高い情報を自DBに保存することなく、また顧客に新たに会員登録などをさせることなく、簡単にサービスにクレカ課金機能を実装することができます。 

# サービス概要
Stripeでは、Webサービスの利用者ごとにCustomerオブジェクトを作成します。このオブジェクトのユニークなIDさえ自サービスのデータベースに保管しておけば、あとはStripe側で決済処理やそれに必要なデータの保管をほとんど全て行ってくれます。

# 実装の流れ
1. StripeのAPIキーを取得
2. Customerオブジェクトの作成
3. バックエンドに、Ephemeral Keyを生成するエンドポイントを設定
4. iOS側でSTPPaymentMethodsViewControllerDelegateと、そのメソッドを設定
5. iOSから渡されるcardIdを用いてバックエンドで決済

今回は、Ruby on Railsで作られたバックエンドを持つiOSアプリを例にとって、この流れを解説します。

#Stripeに会員登録
まず、Stripe(https://stripe.com/jp)　
にアクセスし、アカウントを取得しましょう。その後、個人ページでページ左部にある「API」ボタンをクリックすると以下のような画面になります。
![スクリーンショット 2017-11-18 16.06.41.png](https://qiita-image-store.s3.amazonaws.com/0/171028/27f50c81-a724-90e5-b219-2a9a625cb66b.png)
赤線で隠された部分に表示されるテスト用の公開鍵(pk_testで始まる文字列)と、秘密鍵(sk_testで始まる文字列)はこれから使うので保管しておいてください。
本番で使うAPIキーを取得するにはまた別途申請が必要ですが、ここでは割愛します。

# Customerオブジェクトの作成
次に、決済に使うCustomerオブジェクトを作成します。
RailsでStripeを利用する準備として、まずは`gem 'stripe'`を`bundle install`します。
その後、`config/initializers/stripe.rb`を作成して、次のように書き込みます。

```ruby:config/initializers/stripe.rb
Rails.configuration.stripe = {
  :publishable_key => ENV['PUBLISHABLE_KEY'],
  :secret_key      => ENV['SECRET_KEY']
}

Stripe.api_key = Rails.configuration.stripe[:secret_key]
```
環境変数PUBLISHABLE_KEYとSECRET_KEYに先ほど取得したAPIキーを忘れずに入れておきましょう。

ここから、決済に使うCustomerオブジェクトを作成します。ここでは、ユーザーデータと同時に作成するようにします。

```ruby:app/controllers/users_controller.rb
require 'stripe'

def create
  user = User.new(user_params)
  customer = Stripe::Customer.create(
    email: user.email
  )
  user.customer_id = customer.id
  user.save
  render json: {"message" => "ユーザーを作成しました"}

rescue Stripe::CardError => e
  render json: {"message" => e.message}, status: :bad_request
end

def user_params
  params.require(:user).permit(:email, :name)
end
```

これから、Stripeによる決済のたびにこのcustomer_idを用いれば、Stripeがユーザーごとの支払い履歴を管理してくれます。
なお、今回はuserの作成時にCustomerオブジェクトを同時に作成しましたが、必要に応じてStripe::Cutomer.createを実行すれば、Customerオブジェクトはいつでも作成することができます。

# バックエンドにEphemeral Keyを生成するエンドポイントを作成
このあと、iOSアプリケーションがStripeと直接データをやり取りすることになりますが、その前にどのユーザーが決済を行うのかをバックエンドからiOSに伝える必要があります。このとき、Stripeでは、バックエンド側でCustomerオブジェクトを元にEphemeral Key を作成し、これをiOSアプリケーション側に伝えるという手法をとります。
今回は、orders_controller上にEphemeral Key発行用のエンドポイントを作ります。

```ruby:config/routes.rb
get 'orders/create_ephemeral_key'
```

ここで、リクエストに応じてユーザーに対応したEphemeral Keyを返すようにします。この時、リクエストパラメータとしてStripeのapiバージョンを受け取る必要があります。

```ruby:app/controllers/orders_controller.rb
  def create_ephemeral_key
    stripe_version = params[:api_version]
    user = User.find(params[:id]) 
    customer_id = user.customer_id
    key = Stripe::EphemeralKey.create(
      {customer: customer_id},
      {stripe_version: stripe_version}
    )
    render json: key.to_json
  end
```

また、あらかじめiOS側でEphemeral Keyを受け取るようなクラスを作っておきましょう。
これから使うpodをあらかじめインストールしておきます。

```ruby:podfile
pod 'Alamofire'
pod 'Stripe'
```

```console
$ pod install
```

これらを使って、Ephemeral Keyをバックエンドから受け取るクラスを作ります。このクラスはSTPEphemeralKeyProviderプロトコルを継承し、そのメソッドであるcreateCustomerKeyを実装している必要があります。

```swift:StripeKeyProvider.swift
import Foundation
import Stripe
import Alamofire

class StripeKeyProvider: NSObject, STPEphemeralKeyProvider{
    func createCustomerKey(withAPIVersion apiVersion: String, completion: @escaping STPJSONResponseCompletionBlock) {
        let userId = UserDefaults.standard.string(forKey: "userId")
        let params: [String: String] = ["api_version":apiVersion, "user_id": userId]        
        let url = "先ほどのエンドポイントにアクセスするURL"
        Alamofire.request(url, method: .post, parameters: params)
        .validate(statusCode: 200..<300)
        .responseJSON { responseJSON in
            switch responseJSON.result {
            case .success(let json):
                completion(json as? [String: AnyObject], nil)
            case .failure(let error):
                completion(nil, error)
            }
        }
    }   
}

```
これで、iOSとバックエンドの間でデータをやり取りする準備が整いました。

# iOS側でSTPPaymentMethodsViewControllerDelegateと、そのメソッドを設定
いよいよ、iOS側で決済情報の入力画面を実装します。ここでは、StripeのクラスSTPPaymentMethodsViewControllerがほとんど全ての処理を行ってくれます。よって、やらなくてはいけないことは、
・STPPaymentMethodsViewControllerを呼び出す
・STPPaymentMethodsViewControllerが閉じたら、その結果を受け取る
この二つだけです。
まずは、STPPaymentMethodsViewControllerを呼び出すメソッドを作りましょう。iOS側では、STPCustomerContextクラスを使ってユーザー情報を保持します。そこで、今回は、
・先ほどのStripeKeyProviderクラスを使ってSTPCustomerContextのインスタンスを生成
・これを元にSTPPaymentMethodsViewControllerを生成
・Navigationコントローラーに埋め込んで表示
という3ステップで表示します。

```swift:PurchaseViewController.swift
    func handlePaymentMethods() {
        let customerContext = STPCustomerContext(keyProvider: StripeKeyProvider())
        
        let paymentMethodsViewController = STPPaymentMethodsViewController(configuration: STPPaymentConfiguration.shared(), theme: STPTheme.default(), customerContext: customerContext, delegate: self)
        
        let navigationController = UINavigationController(rootViewController: paymentMethodsViewController)
        present(navigationController, animated: true)
    }
```
あとは適当なタイミングでこのメソッドを呼び出すだけです。今回は適当なボタンがタップされた時に表示するようにします。
また、ユーザーの操作の結果を受け取るために、このボタンがあるViewControllerにSTPPaymentMethodsViewControllerDelegateを継承させ、デリゲートメソッドを定義しておきます。とりあえず、何か操作があったらSTPPaymentMethodsViewControllerを閉じるようにしておきます。

```swift:PurchaseViewController.swift
class PurchaseViewController:UIViewController, STPPaymentMethodsViewControllerDelegate{
    @IBAction func purchase(_ sender: UIButton) {
        handlePaymentMethods()
    }
    func handlePaymentMethods() {
        //略
    }

    func paymentMethodsViewController(_ paymentMethodsViewController: STPPaymentMethodsViewController, didFailToLoadWithError error: Error) {
        dismiss(animated: true)
        // なんらかのエラーがおきた時の処理
    }
    
    func paymentMethodsViewControllerDidCancel(_ paymentMethodsViewController: STPPaymentMethodsViewController) {
        dismiss(animated: true)
        //'Cancel'が押された時の処理
    }
    
    func paymentMethodsViewControllerDidFinish(_ 
        dismiss(animated: true)
        //なんらかの決済手段が選ばれた時の処理
    }
    
    func paymentMethodsViewController(_ paymentMethodsViewController: STPPaymentMethodsViewController, didSelect paymentMethod: STPPaymentMethod) {
        // 選ばれた決済手段の情報を保存する処理
    }

}
```
これでアプリ上でユーザーにカード情報を入力させることができるようになりました。
実際に使ってみましょう。ボタンを押すと、下のような画面と共に、今までユーザーが決済に使用したことのあるカードの一覧が表示されます。
<img width="517" alt="スクリーンショット 2017-12-03 12.44.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/171028/830c89f1-2a6c-ffc8-6d5c-8c4877f27060.png">

今まで使ったカード以外で決済を行いたい場合は、一番下のボタンを押してカードを追加できます。
<img width="517" alt="スクリーンショット 2017-12-03 12.45.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/171028/f68dc818-a368-1698-c7ef-1bd00139ced2.png">

# iOSから渡されるcardIdを用いてバックエンドで決済
いよいよユーザーが入力した情報を使って決済を行いましょう。
先ほど定義したデリゲートメソッドの引数を使って、選択されたカードを表す文字列であるcardIdを取得できます。

```swift:PurchaseViewController.swift
func paymentMethodsViewController(_ paymentMethodsViewController: STPPaymentMethodsViewController, didSelect paymentMethod: STPPaymentMethod) {
        if let selectedCard = paymentMethod as? STPCard {
            let cardId = selectedCard.cardId
        }
    }
```

あとはこのcardIdと、アプリで管理しているuserIdを同時にバックエンドに送りましょう。引き落としたい金額を指定するだけで簡単に決済が完了します。

```ruby:app/controllers/orders_controller.rb
  def create
    card_id = params[:cardId]
    user = User.find(params[:userId]) 
    customer_id = user.customer_id
    Stripe::Charge.create(
        customer: customer_id,
        amount: #引き落とす金額,
        source: cardId,
        currency: 'jpy'
     )
  rescue Stripe::CardError => e
    render json: {"message" => e.message}, status: :bad_request
    return
  end
```
完了した決済については、Stripeコンソール上で情報を確認できます。顧客毎、カード毎の情報の確認、及び返金処理なども実現できます。
![FireShot Capture 16 - [テスト] Payment ch_1BNbFzJVkV2HICPBjrW3d_ - https___dashboard.stripe.com_test_.png](https://qiita-image-store.s3.amazonaws.com/0/171028/f3af1373-4039-0b62-54ff-3fcfccb6c113.png)

# 終わりに
カード情報をDBに保存することなく、簡単にiOSアプリ上で決済処理を実現できました。他にも様々な実装方法があるので、是非公式ドキュメントをチェックしてみてください。


# 参考
https://qiita.com/100010/items/c09fb08dd555b32a2652
https://stripe.com/docs