背景
Stripe決済を初めて実装する機会があり、参考記事があまりなく苦労したのでまとめてみました。
もし、Rails6でStripe決済を実装することになった駆け出しエンジニアがいたら是非参考にしてもらえると嬉しいです。
また、学習3~4ヶ月目のぺーぺーが書いた記事なので、理解が間違っている、リファクタリングなどあればなんなりと指摘していただけると幸いです。
Stripeには複数の決済手段があるが、今回は一番実装が簡単なCheckoutを実装しました。
ちなみにStripeの公式リファレンスではFWはSinatraなのでご注意ください。
Checkoutとは?: Stripeが用意してくれている決済画面に遷移して決済を行う手段である。
実装内容
- Stripe Checkout で決済機能を実装
- 決済完了時にユーザー情報を登録
- 支払完了後はWEBアプリに戻り、決済完了画面を表示
- 決済せずにStripe上で「戻る」をしたときに支払キャンセル画面を表示
Stripe側での設定
1. まずはStripeのサイトでアカウント作成
以下のリンクからアカウントを作成しましょう!
[Stripe公式サイト][0]
[0]:https://stripe.com/jp
2. ダッシュボード上の商品
タブをクリックし、+ テスト商品を追加
をクリック
3. 商品情報を入力
4. 以下の4つのキーを使用
商品ページ内
詳細のID: prod_◯◯◯◯◯◯
料金のAPI ID:price_◯◯◯◯◯◯◯◯◯◯
開発者ページ内APIキーの標準キー
公開可能キー: pk_test_◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯…◯◯◯
シークレットキー: sk_test_◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯…◯◯◯ *第三者に漏洩しないようにご注意ください
Rails側の手順
■前提
deviseや必要なデータベースは作成済み。
今回、必要なデータベースは以下の感じ。
ユーザーテーブル: name, email, password, customer_id, plan_id + デフォルト
プランテーブル: id, plan_name + デフォルト
もしテーブルの作り方がわからない場合は、簡単にまとめてあるので以下を御覧ください!
参考:[マイグレーションファイルの基本][5]
[5]:https://qiita.com/vit_udon_husqy/items/6142908cca27b2188f0d
実装
少し長いので、コントローラは機能2つに分けて解説します。
■Gemのインストール
gem 'stripe' # Stripeを実装するためのgem
gem 'dotenv-rails' # Stripeの重要な情報を記述するためのgem
bundle installして準備完了
■決済画面への遷移~決済完了ページ or 決済キャンセルページの実装
class PaymentsController < ApplicationController
#決済後の画面遷移
def new
@session = Stripe::Checkout::Session.create({
payment_method_types: ['card'],
line_items: [{
price: 'price_XXXXXXXXXXX', # 料金API-IDを記述
quantity: 1,
}],
mode: 'payment',
success_url: request.base_url + '/payments/after_payment_register?session_id={CHECKOUT_SESSION_ID}', # 決済成功後の遷移先
cancel_url: request.base_url + '/payments/payment_cancel', #決済キャンセルした際の遷移先
})
end
#決済前のキャンセルのアクション
def payment_cancel
end
end
Stripeでは、Railsに実装する際はStripe::Checkout::Session.◯◯◯
のような形でセッションオブジェクトを使用してデータを取得します。
参考:[Stripe API reference (Create)][1]
[1]:https://stripe.com/docs/api/checkout/sessions/create
参考:[Stripe Docs][7]
[7]:https://stripe.com/docs/checkout/integration-builder
<script src="https://js.stripe.com/v3"></script>
<script>
var stripe = Stripe('pk_test_XXXXXXXXXXXXXX'); // ()内には公開可能キーを記述
stripe.redirectToCheckout({
sessionId: '<%= @session.id %>'
}).then(function (result) {
});
</script>
上記ではnewアクションに対するビューの設定です。つまり、決済画面を作っています。
newアクションで作ったインスタンス変数@session(料金、数量等の情報)を決済ページに持っていってくれます。
今回のケースだと payments/new
というパスで決済ページにアクセスできます。
← 戻る
の箇所には元々はショップ名が入ります。
カーソルを持っていくと← 戻る
に変わり、クリックするとキャンセル扱いとなり画面遷移します。
ダミーカード番号 4242 4242 4242 4242
と
その他の情報を適当に入力して「支払う」ボタンを押すと決済完了画面に遷移するはずです!
■決済完了後、データベースにユーザー情報を登録
まず、Stripeの決済は色々な情報を保有しています。欲しいデータが何なのか、事前に整理しておきましょう!
どのようなデータが取れるのかは以下を参考にしてください。
参考:[Stripe API reference][2]
[2]:https://stripe.com/docs/api
今回、ユーザー情報としてStripeから引っ張りたかったのは以下の通り。
-顧客ID(customer_id)
-メールアドレス(email)
-商品ID(product_id)
class PaymentsController < ApplicationController
before_action :user_plan_judgement, except: [:new, :payment_cancel]
INITIAL_USER_NAME = "新規ユーザー"
STRIPE_PRODUCT_ID = "prod_XXXXXX" # 商品IDを記述
#(中略)
# 初期パスワードを乱数生成、初期ユーザー名を新規ユーザー、customer_id, email, plan_idをユーザーデータテーブルに登録
def after_payment_register
generated_initial_password = Devise.friendly_token.first(8)
stripe_user_data = Stripe::Checkout::Session.retrieve(params[:session_id])
@user = User.new(
customer_id: stripe_user_data.customer,
name: INITIAL_USER_NAME,
email: stripe_user_data.customer_details["email"],
password: generated_initial_password,
plan_id: user_plan_judgement
)
@user.save
send_notification_email
end
private
# 1.プランIDを取得
def user_plan_judgement
stripe_plan_data = Stripe::Checkout::Session.list_line_items(params[:session_id])
if stripe_plan_data[:data][0][:price]["product"] == STRIPE_PRODUCT_ID
plan_id = 1
end
end
end
ざっくりですが、以下のことを実行しています。
- ユーザーが決済したプランはなにかを判定
- 1.を含めたユーザー情報を登録
1. ユーザーが決済したプランはなにかを判定
Stripe::Checkout::Session.list_line_items
でStripeの商品IDと引っ張ってきたデータのproductデータが一致していれば
plan_idに1を代入するという処理にしました。
現状あまり意味はないのですが、
プランの料金が変わったときでも同一のプランであることとして管理するために設定しました。
参考:[Stripe API reference (line_items)][5]
[5]:https://stripe.com/docs/api/checkout/sessions/line_items
2. 1.を含めたユーザー情報を登録
Devise.friendly_token.first(8)
でランダムな8桁のパスワードを設定
Stripe::Checkout::Session.retrieve
でレトリーブしてきてくれます。
参考:[Stripe API reference (retrieve)][3]
[3]:https://stripe.com/docs/api/checkout/sessions/retrieve
かわいいレトリーバー犬はこのretrieveから来ているらしいです。
狩った獲物をとってくる(回収してくる)意味らしいです。 (今回のPJの恩師から教えてもらいました。)
参考:[語源由来辞典][4]
[4]:https://gogen-yurai.jp/retriever/
なので、欲しいデータをretrieve(回収)してきてもらうわけです。
retrieveしてもらった獲物(データ)を獲物箱(=変数)へ代入します。
あとは見慣れたUser.newとuser.saveでユーザーデータを保存してユーザー登録も完了!
実際に決済テストを行った後にRails console
のUser.all
コマンドで確認すると決済を行ったユーザーが登録されているはずです!
あとは決済後やキャンセル後のページをビューを整えて完成!
ビューで表示する内容はプロジェクトの指定のものか、テンプレート文を調べると良いと思います。
完成形
コントローラ
class PaymentsController < ApplicationController
before_action :user_plan_judgement, except: [:new, :payment_cancel]
INITIAL_USER_NAME = "新規ユーザー"
STRIPE_PRODUCT_ID = "prod_XXXXXX" # 商品IDを記述
def new
@session = Stripe::Checkout::Session.create({
payment_method_types: ['card'],
line_items: [{
price: 'price_XXXXXXXXXXX', # 料金API-IDを記述
quantity: 1,
}],
mode: 'payment',
success_url: request.base_url + '/payments/after_payment_register?session_id={CHECKOUT_SESSION_ID}',
cancel_url: request.base_url + '/payments/payment_cancel',
})
end
def payment_cancel
end
def after_payment_register
generated_initial_password = Devise.friendly_token.first(8)
stripe_user_data = Stripe::Checkout::Session.retrieve(params[:session_id])
@user = User.new(
customer_id: stripe_user_data.customer,
name: INITIAL_USER_NAME,
email: stripe_user_data.customer_details["email"],
password: generated_initial_password,
plan_id: user_plan_judgement
)
@user.save
send_notification_email
end
private
def user_plan_judgement
stripe_plan_data = Stripe::Checkout::Session.list_line_items(params[:session_id])
if stripe_plan_data[:data][0][:price]["product"] == STRIPE_PRODUCT_ID
plan_id = 1
end
end
end
ビュー
<script src="https://js.stripe.com/v3"></script>
<script>
var stripe = Stripe('pk_test_XXXXXXXXXXXXXX'); // ()内には公開可能キーを記述
stripe.redirectToCheckout({
sessionId: '<%= @session.id %>'
}).then(function (result) {
});
</script>
<p>決済してユーザー登録したぞ</p>
<p>決済後に表示されるページだよ</p>
<p>必要に応じてhtmlで文章記述</p>
<p>キャンセルされたときに表示されるページだよ</p>
その他(ルーティング、env)
(中略)
resources :payments, only: [:new]
get '/payments/after_payment_register', controller: 'payments', action: 'after_payment_register'
get '/payments/payment_cancel', controller: 'payments', action: 'payment_cancel'
(中略)
*ここはGithubにpushされないファイルなので、隠したい定数はここに書きます。
//以下はシークレットキー
STRIPE_TEST_SECRET_KEY = sk_test_XXXXXXXXXXXXXXXXXX
終わりに
長くなりましたが、ご覧いただきありがとうございました!
今回、初めてStripe決済を実装する機会をいただくことができ、自分で実装した中で理解したことをまとめてみました。
Stripe::Checkout::Session.◯◯◯
で自在にデータを取得できるのは本当に便利だなあと実感しました。
また、MVC、データの取得~保存、データベースの構築まで自分でやれたのでデータ関連の理解が深まったと感じます。
次回は本番環境の実装についてまとめられたらまとめてみようと思います。