31. RailsとStripe連携: サブスクリプション課金システムを構築する
はじめに
SaaS (Software as a Service) をはじめとする多くのWebサービスにおいて、サブスクリプション(月額・年額課金)モデルは主要な収益源となっています。この複雑な課金システムをゼロから構築するのは大変ですが、決済代行サービス Stripe を利用することで、安全かつ迅速に実装することが可能です。
本記事では、RailsアプリケーションにStripeを連携させ、基本的なサブスクリプション課金システムを構築する手順を解説します。
この記事で学べること
- Stripeの基本的な概念(Customer, Product, Price, Subscription)
stripe
gemを使ったRailsとStripe APIの連携方法- Stripe Checkoutを利用した安全な決済ページの生成
- Stripe Webhookによる課金イベントのハンドリング
1. Stripeの準備
1.1. Stripeアカウントの作成とAPIキーの取得
- Stripe公式サイトでアカウントを登録します。
- ダッシュボードにログインし、開発者メニューから「APIキー」ページに移動します。
- 「標準キー」の 公開可能キー (Publishable key) と シークレットキー (Secret key) をコピーしておきます。これらは後ほどRailsの設定で使用します。
1.2. 商品と価格の作成
Stripeダッシュボードで、ユーザーに提供するサブスクリプションプランを作成します。
- 「商品」メニューに移動し、「商品を追加」をクリックします。
- 商品情報(例: プレミアムプラン)を入力します。
- 料金体系で「継続」を選択し、価格(例: 1,500円/月)と請求期間を設定します。
- 作成後、価格詳細ページに表示される 価格ID (Price ID)(
price_...
から始まる文字列)をコピーしておきます。
2. RailsアプリケーションへのStripe導入
2.1. Gemのインストールと設定
Gemfile
に stripe
gemを追加します。
Gemfile
gem 'stripe'
bundle install
を実行します。
次に、APIキーを設定します。config/initializers/stripe.rb
を作成し、以下のように記述します。
config/initializers/stripe.rb
Stripe.api_key = Rails.application.credentials.dig(:stripe, :secret_key)
rails credentials:edit
を実行し、Stripeのシークレットキーを保存します。
config/credentials.yml.enc
stripe:
secret_key: sk_test_xxxxxxxxxxxx
publishable_key: pk_test_xxxxxxxxxxxx
# Webhook署名シークレットも後で追加
2.2. ユーザーモデルの準備
ユーザーがStripeの顧客情報と紐づくように、User
モデルに stripe_customer_id
カラムを追加します。
rails g migration AddStripeCustomerIdToUsers stripe_customer_id:string
rails db:migrate
3. サブスクリプション登録フローの実装
Stripe Checkoutを利用すると、Stripeがホストする安全な決済ページにリダイレクトさせるだけで、カード情報の入力をStripeに任せることができます(PCI DSSコンプライアンスに準拠しやすくなります)。
3.1. Checkoutセッションの作成
ユーザーが「登録」ボタンを押したときに、Stripeの決済ページへのリダイレクトURLを生成するコントローラのアクションを作成します。
app/controllers/subscriptions_controller.rb
class SubscriptionsController < ApplicationController
before_action :authenticate_user! # Deviseなどの認証を想定
def create
# ユーザーに対応するStripe顧客を作成または取得
customer = find_or_create_stripe_customer
# Stripe Checkoutセッションを作成
session = Stripe::Checkout::Session.create(
customer: customer.id,
payment_method_types: ['card'],
line_items: [{
price: 'price_xxxxxxxxxxxx', # Stripeで作成した価格ID
quantity: 1,
}],
mode: 'subscription',
success_url: subscription_success_url,
cancel_url: subscription_cancel_url
)
# Stripeの決済ページへリダイレクト
redirect_to session.url, allow_other_host: true, status: :see_other
end
def success
# 決済成功時の処理(Webhookで処理するのがより堅牢)
flash[:notice] = "サブスクリプションに登録しました!"
redirect_to root_path
end
def cancel
# 決済キャンセル時の処理
flash[:alert] = "サブスクリプション登録をキャンセルしました。"
redirect_to root_path
end
private
def find_or_create_stripe_customer
if current_user.stripe_customer_id?
Stripe::Customer.retrieve(current_user.stripe_customer_id)
else
customer = Stripe::Customer.create(email: current_user.email)
current_user.update!(stripe_customer_id: customer.id)
customer
end
end
end
3.2. ルーティングとビュー
config/routes.rb
にルーティングを追加します。
config/routes.rb
resource :subscription, only: [:create]
get 'subscription/success', to: 'subscriptions#success'
get 'subscription/cancel', to: 'subscriptions#cancel'
ビューに登録ボタンを設置します。
app/views/home/index.html.erb
<%= button_to "プレミアムプランに登録", subscription_path, method: :post %>
4. Webhookによるイベント処理
決済成功、失敗、更新、キャンセルなど、Stripe上で発生したイベントを確実にRailsアプリケーションに通知させるためにWebhookを利用します。
4.1. Webhookエンドポイントの作成
StripeからのPOSTリクエストを受け取るコントローラを作成します。
rails g controller Webhooks receive
app/controllers/webhooks_controller.rb
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token # CSRF保護を無効化
def receive
payload = request.body.read
sig_header = request.env['HTTP_STRIPE_SIGNATURE']
endpoint_secret = Rails.application.credentials.dig(:stripe, :webhook_secret)
event = nil
begin
event = Stripe::Webhook.construct_event(
payload, sig_header, endpoint_secret
)
rescue JSON::ParserError => e
render json: { error: 'Invalid payload' }, status: 400
return
rescue Stripe::SignatureVerificationError => e
render json: { error: 'Signature verification failed' }, status: 400
return
end
# イベントタイプに応じて処理を分岐
case event.type
when 'checkout.session.completed'
# 決済が完了したときの処理
session = event.data.object
user = User.find_by(stripe_customer_id: session.customer)
# ユーザーのステータスを更新するなどの処理
user.update(plan: 'premium')
when 'customer.subscription.deleted'
# サブスクリプションがキャンセルされたときの処理
subscription = event.data.object
user = User.find_by(stripe_customer_id: subscription.customer)
user.update(plan: 'free')
else
puts "Unhandled event type: #{event.type}"
end
render json: { status: :ok }
end
end
4.2. StripeでのWebhook設定
- 開発環境でWebhookをテストするために、
stripe-cli
をインストールして使います。bashstripe listen --forward-to localhost:3000/webhooks/receive
- 上記のコマンドを実行すると、Webhook署名シークレット(
whsec_...
)が表示されるので、これをcredentials.yml.enc
に保存します。 - 本番環境では、Stripeダッシュボードの「Webhook」設定ページで、
https://your-domain.com/webhooks/receive
のような公開URLをエンドポイントとして登録します。
まとめ
StripeとRailsを連携させることで、複雑なサブスクリプション課金システムを比較的容易に実装できます。
- Stripe Checkout: カード情報の入力をStripeに任せ、安全性を高める。
- Stripe Webhook: 課金に関するイベントを確実に捕捉し、アプリケーションの状態を正確に更新する。
本記事で紹介したのは基本的な流れですが、これを基に複数プランへの対応、クーポンの適用、解約・再開処理などを実装していくことで、本格的なSaaSを構築することが可能です。