Edited at
VASILYDay 12

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

More than 1 year has passed since last update.

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