Stripe決済第三弾です。
今回は前々回実装したサブスクと、前回実装した買い切りを組み合わせて複数のプランをユーザーが選択できるようにしてみます。
下準備は以下の記事を参考にしてみてください。
決済を実装する
今回用意するプラン
- スタンダードプラン
- ¥100/月
- ¥800/買い切り
- プレミアムプラン
- ¥1,000/月
- ¥8,000/買い切り
料金を作成する
こちら で料金を作成します。
前回までの記事を参考に商品と料金を作成します。
商品カタログページを見て、以下のようになっていればOKです。
プランのマスターデータを作る
各料金の price_~~~
を保存する場所がないので、作成していきます。
DBに保存でもいいのですが、今回は4つだけなので ActiveHash を使います。
Gemをインストールします。
Gemfile
gem "active_hash"
PlanとPlanPriceを作成します。
app/models/plan.rb
class Plan < ActiveHash::Base include ActiveHash::Associations has_many :plan_prices self.data = [ { id: 1, code: 'free', name: "無料会員"}, { id: 2, code: 'standard', name: "スタンダード会員"}, { id: 3, code: 'premium', name: "プレミアム会員"} ] end
app/models/plan_price.rb
class PlanPrice < ActiveHash::Base include ActiveHash::Associations belongs_to :plan self.data = [ { id: 1, plan_id: 2, price_id: "price_~~~~", mode: "subscription" }, # サブスクのスタンダードプラン { id: 2, plan_id: 2, price_id: "price_~~~~", mode: "payment" }, # 買い切りのスタンダードプラン { id: 3, plan_id: 3, price_id: "price_~~~~", mode: "subscription" }, # サブスクのプレミアムプラン { id: 4, plan_id: 3, price_id: "price_~~~~", mode: "payment" }, # 買い切りのプレミアムプラン ] end
usersテーブルにカラムを追加します。
$ bundle exec rails g migration plan_id_to_users
db/20240912000000_plan_id_to_users.rb
class PlanIdToUsers < ActiveRecord::Migration[7.1] def change add_column :users, :plan_id, :integer, default: 1 end end
※ 前回まで使用していた premium
カラムは不要です。
app/models/user.rb
以下を追記
extend ActiveHash::Associations::ActiveRecordExtensions belongs_to :plan
プレミアムプランへの動線を作る
ルーティングを追加します。
config/routes.rb
resources :plans, only: [:index]
コントローラーを作成します。
app/controllers/plans_controller.rb
class PlansController < ApplicationController def index; end end
viewsを作成します。
app/viewa/plans/index.html.erb
<%= button_to checkouts_path(plan_price_id: 1), data: { turbo: false } do %> スタンダードプラン(¥100/月)に申し込む <% end %> <%= button_to checkouts_path(plan_price_id: 2), data: { turbo: false } do %> スタンダードプラン(¥800/買い切り)に申し込む <% end %> <%= button_to checkouts_path(plan_price_id: 3), data: { turbo: false } do %> プレミアムプラン(¥1,000/月)に申し込む <% end %> <%= button_to checkouts_path(plan_price_id: 4), data: { turbo: false } do %> プレミアムプラン(¥8,000/買い切り)に申し込む <% end %>
全然DRYになってないですが、4つなのでよしとします。
Stripeで決済する
ルーティングを追加します。
config/routes.rb
resources :checkouts, only: [:create]
コントローラーを作成します。
app/controllers/checkouts_controller.rb
class CheckoutsController < ApplicationController def create create_customer if current_user.customer_id.blank? plan_price = PlanPrice.find_by(id: params[:plan_price_id]) session = Stripe::Checkout::Session.create( customer: current_user.customer_id, mode: plan_price.mode, payment_method_types: ["card"], line_items: [ { quantity: 1, price: plan_price.price_id } ], success_url: plans_url, cancel_url: plans_url ) redirect_to session.url, allow_other_host: true end private def create_customer customer = Stripe::Customer.create({ name: current_user.name, email: current_user.email, }) current_user.update(customer_id: customer.id) end end
ここまでで決済ができるようになっています。
それぞれのボタンを押してみると、それぞれの価格でStripeのカード情報入力ページへ遷移すると思います。
ここで使用できるテストカードは こちら を参考にしてください。
Webhookを使用できるようにする
Stripe CLIをインストールします。
ローカル環境でWebhookを使用するためにStripe CLIをインストールします。
$ brew install stripe/stripe-cli/stripe
CLIにログインします。
$ stripe login
ローカル環境にイベントを送信します。
stripe listen --forward-to localhost:3000/webhooks
「Your webhook signing secret is whsec_~~」 と表示されるかと思います。
ここに表示されている、 whsec_~~~
はWebhook用のsecretなのでコピーしましょう。
環境変数を追記します。
$ rails credentials:edit -e development
エディタが開いたら以下を参考にWebhook用のsecretを設定します。
stripe: publishable_key: pk_test_~~~~ secret_key: sk_test_~~~~ endpoint_secret: whsec_~~~~
Stripeで決済した情報を保存する
Stripeで決済した情報を保存できるようにWebhookを実装します。
ルーティングを追加します。
config/routes.rb
resources :webhooks, only: [:create]
コントローラーを作成します。
app/controllers/webhooks_controller.rb
class WebhooksController < ApplicationController skip_before_action :verify_authenticity_token skip_before_action :signed_in_user def create payload = request.body.read sig_header = request.env['HTTP_STRIPE_SIGNATURE'] endpoint_secret = Rails.application.credentials.dig(:stripe, :endpoint_secret) event = nil begin event = Stripe::Webhook.construct_event( payload, sig_header, endpoint_secret ) rescue JSON::ParserError => e # Invalid payload p e status 400 return rescue Stripe::SignatureVerificationError => e # Invalid signature p e status 400 return end case event.type when 'checkout.session.completed' session = event.data.object user = User.find_by(customer_id: session.customer) return unless user ApplicationRecord.transaction do line_item = Stripe::Checkout::Session.list_line_items(session.id).data.first plan = PlanPrice.find_by(price_id: line_item.price.id).plan Payment.create!( user_id: user.id, subscription_id: session.subscription, product_id: line_item.price.product, price_id: line_item.price.id, customer_id: session.customer, price: line_item.price.unit_amount, plan_id: plan.id ) user.update!(plan_id: plan.id) rescue => e p e end end end end
決済情報を保存したあとは、ユーザーがどのプランの会員になったか判別して保存します。
終わり
これで実装完了です。
サブスクのプランと買い切りのプラン、料金の違うプランなどを並べることができました。
選択肢が増えるとユーザーに使ってもらいやすくなりそうですね!