54
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-12-12

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
赤線で隠された部分に表示されるテスト用の公開鍵(pk_testで始まる文字列)と、秘密鍵(sk_testで始まる文字列)はこれから使うので保管しておいてください。
本番で使うAPIキーを取得するにはまた別途申請が必要ですが、ここでは割愛します。

Customerオブジェクトの作成

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

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オブジェクトを作成します。ここでは、ユーザーデータと同時に作成するようにします。

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発行用のエンドポイントを作ります。

config/routes.rb
get 'orders/create_ephemeral_key'

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

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をあらかじめインストールしておきます。

podfile
pod 'Alamofire'
pod 'Stripe'
$ pod install

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

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ステップで表示します。

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を閉じるようにしておきます。

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) {
        // 選ばれた決済手段の情報を保存する処理
    }

}

これでアプリ上でユーザーにカード情報を入力させることができるようになりました。
実際に使ってみましょう。ボタンを押すと、下のような画面と共に、今までユーザーが決済に使用したことのあるカードの一覧が表示されます。
スクリーンショット 2017-12-03 12.44.25.png

今まで使ったカード以外で決済を行いたい場合は、一番下のボタンを押してカードを追加できます。
スクリーンショット 2017-12-03 12.45.16.png

iOSから渡されるcardIdを用いてバックエンドで決済

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

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

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

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

終わりに

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

参考

https://qiita.com/100010/items/c09fb08dd555b32a2652
https://stripe.com/docs

54
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
54
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?