5
2

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 3 years have passed since last update.

RailsアプリにStripeを用いたサブスクリプション決済を導入する(詳しく解説)

Last updated at Posted at 2021-03-16

#概要
僕がサブスクリプション決済を導入する際に参考にしたこちらの記事をもう少し詳しくしたような記事です。
https://qiita.com/tady/items/7617e62b2a5402ebd0fb
#事前準備
deviseというgemを使ったりして、Userモデルは作成しておいてください。僕はRails turorial第6版の完成品を使いました。

#注意
・この記事で扱うプランは一つです。複数のプランに関しては取り扱っておりません。
・無料プランすら扱っていません
#では始めましょう
まずこちらでstripeのアカウント登録を済ませてください。
https://dashboard.stripe.com/register

次にこちらでapi_keyをコピーできるようにしておいてください。
https://dashboard.stripe.com/account/apikeys

Railsアプリで
EDITOR="vi" rails credentials:edit(local環境の場合はrails→bin/railsと打ち込む)
と打ち込み、
app/config/credentials.yml.encを開いてください
形式はvimなので、基本的なvimの使い方は各自お調べください。(iを押すと入力できるようになりEscを押して:wqと入力すると保存ができます。移動は基本的にキーボードの矢印キーを使います。)

app/config/credentials.yml.enc
stripe_publishable_key: pk_test_xxxxxxxxxxxxxxx
stripe_api_key: sk_test_xxxxxxxxxxxxx

としてください。各々のキーは
Rails.application.credentials.stripe_publishable_key
Rails.application.credentials.stripe_api_key
で取り出せます。

次にRailsアプリとStripeを繋げるためにapp/config/initializers/stripe.rbというファイルを作成してください。

app/config/initializers/stripe.rb
Stripe.api_key = Rails.application.credentials.stripe_api_key

これで接続が完了しました。

次にhttps://dashboard.stripe.com/test/products
で商品と料金(プラン)を設定してください。

プランの種類が100種類とかでない限り、Railsからプランをつくるのはお勧めしません。公式ページで手動で作りましょう。

商品をクリックし、料金の欄を見ると作成したプランのAPI_IDが見つかるはずです。(price_xxxxってやつ)
どっかにメモしておいてください。あとで使います。

次はgemのインストールです。

gem 'stripe'

つづいて
bundle install
サーバーを起動している場合は再起動を忘れないようにしてください。

#ここからが大変です
ここから難易度があがるのでわかりにくくなったら
こちらの記事の図を見てくださいね
https://qiita.com/tady/items/7617e62b2a5402ebd0fb
どのユーザーがどのサブスクIDとつながっているかを確認するTeamモデルを作成します。

rails g model team user:references customer_id:string active_until:datetime stripe_subscription_id:string plan_id:string

(注意)customer_id, stripe_subscription_id, plan_idはstring型です。integer型ではありません。active_untilは次の決済日を表しています。 user:referenceは黒魔術のようなものなので気にしなくてOKです。どうしても気になる方は各自お調べください。team.rbは以下のもので上書きしてください。

app/models/team.rb
class Team < ApplicationRecord
  belongs_to :user
  validates_uniqueness_of :plan_id, scope: :user_id
  with_options presence: true do
    validates :user_id
    validates :customer_id
    validates :active_until
    validates :stripe_subscription_id
    validates :plan_id
  end
end

validates_uniqueness_of :plan_id, scope: :user_id
ではユーザー1人につき、1つしかSubscriptionを契約できないようにしています。つまり、二重決済が発生しないようにしています。

with_options presence: true doのブロックでは、四つのカラムすべてにバリデーションを書けています。(presence :true)
validates :user_id, presence: trueなどのバリデーションを書けてもいいのですが、まとめてかけたほうが早いため上記のようにしております。

(customer_idやstripe_subscription_idがあるからcustomerモデルとかstripe_subscriptionモデルとか必要なのかなと考えるかたがいらっしゃるのはわかりますが、使わないのでモデルは作らないが吉です。)

ルートを追加します

config/routes.rb
Rails.application.routes.draw do
  resources :teams, only: [:new, :create, :destroy]
end

コントローラーを作成します。

rails g controller teams

ビューを新しく作成します

app/views/teams/new.html
<%= form_tag teams_path do %>
    <article>
      <% if flash[:error].present? %>
        <div id="error_explanation">
          <p><%= flash[:error] %></p>
        </div>
      <% end %>

      <label class="amount">
        <span>料金: 500円</span>
      </label>
    </article>

    <script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
            data-key = "pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
            data-description="A month's subscription"
            data-amount="500"
            data-locale="ja"
            data-currency="JPY"
            data-label="購入する">
      
    </script>
<% end %>
<%= link_to "解約する",@team, method: :delete, data: { confirm: "解約しますがいいですか?"}%>

いろいろ突っ込みどころはあるでしょうがひとまず置いときましょう。後で解説します。ちなみにこの記事では契約も解約もこのnew.html.erbで完結させます。

#コントローラを作成
重要な部分なので強調しておきました。
コントローラは以下をコピペしてください。

app/controllers/teams_controller.rb
class TeamsController < ApplicationController
 
  
  def new
    @user = current_user
    @team = Team.find_by(user_id: @user.id)
  end
  
  def create
    @team = Team.new(user_id: current_user.id)
    customer = Stripe::Customer.create({
      source: params[:stripeToken]
    })
    subscription = Stripe::Subscription.create({
      customer: customer.id,
      plan: "price_xxxxxxxxxxxxxxxxxxxxxxx"
    })
    @team.plan_id = "price_xxxxxxxxxxxxxxxxxxxxxxx"
    @team.customer_id = customer.id
    @team.stripe_subscription_id = subscription.id
    @team.active_until = Time.zone.at(subscription.current_period_end)
    if @team.save
      flash[:success] = "成功しました"
      redirect_to root_url
    else
      render 'new'
    end
  end
  
  def destroy
    @team = Team.find_by(user_id: current_user.id)
    deleting_stripe_subscription = Stripe::Subscription.retrieve(@team.stripe_subscription_id)
    if current_user.unsubscribe
      deleting_stripe_subscription.delete
      flash[:notice] = "解約に成功しました"
      redirect_to root_url
    else
      render 'new'
    end
  end
end

二回目で申し訳ないですがCustomerモデルとかSubscriptionモデルとか作成する必要はありません。Stripeが勝手に全部やってくれます(なんて便利なんだ!)

ちょっとわかりにくいStripe::Subscription.retrieve(@team.stripe_subscription_id)の説明だけしておくと、retrieveはStripe側のモデルにおけるfindだと思えば十分です。
current_user.unsubscribeはUser.rbを見ればわかります。

User.find(1)とするとidが1のユーザーが見つかるように、
Stripe::Subscription.retrieve(@team.stripe_subscription_id)とすれば
IDが合致するサブスクリプション契約をStripe側が勝手に見つけてくれます。

続いてuser.rbに行きましょう

app/models/user.rb
class User < ApplicationRecord
  has_one :team
  
  def unsubscribe
    team = Team.find_by(user_id: self.id)
    team.destroy
  end
  
end

これでコントローラが何をしていたかわかりましたね!!

#Viewの解説
さっき飛ばしたViewの説明をします。

app/views/teams/new.html
<%= form_tag teams_path do %>
    <article>
      <% if flash[:error].present? %>
        <div id="error_explanation">
          <p><%= flash[:error] %></p>
        </div>
      <% end %>

      <label class="amount">
        <span>料金: 500円</span>
      </label>
    </article>

    <script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
            data-key = "pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
            data-description="A month's subscription"
            data-amount="500"
            data-locale="ja"
            data-currency="JPY"
            data-label="購入する">
     </script>
<% end %>
<%= link_to "解約する",@team, method: :delete, data: { confirm: "解約しますがいいですか?"}%>
<% if @team %>
  <p>次回の決済日は<%= @team.active_untilstrftime("%Y年%m月%d日 %H:%M:%S").to_s %>です。</p>
  <p>登録日が月末付近の場合ご注意ください</p>
<% end %>

(注意!、<%= @team.active_until %>はなにも設定しないと、グリニッジ標準時間が表示されます。日本時間にしたい場合は"Rails 日本時間"でググるとすぐ出てきます。)

はstripe側が用意してくれたformでこれだけで決済が完結します。
data-keyには公開可能keyを直接入れるとうまくいきます。(グローバル変数にpublish_keyを格納して、scriptタグに突っ込んでもうまくいかず、公開可能鍵だからむき出しでいいか。と、ほったらかしにしている状態です。どなたか解決方法をご享受いただければ幸いです。)

実際に/teams/newにいきボタンを押して
・自分のメアド
・test用VISAカードの番号(4242424242424242)
・有効期限(未来ならいつでも。12/99とか)
・セキュリティコード(好きな3桁の数字)
を入力してみましょう。root_urlにちゃんとredirectされるはずです(root_urlのredirect先のページを作成するのも忘れないでくださいね。)。

stripeのダッシュボードから商品→プランへといってみましょう。反映されているはずです。
consoleからの確認もできます。rails console

team = Team.first
team.stripe_subscription_id
>sub_xxxxxxxx

となっていれば成功です。(stripe_subscription_idはstring型になっていますね///)
解約もしてみましょう。こちらもうまくいくはずです。
再び、rails console

team = Team.first
>nil

となれば成功です。

#before_acctionを追加

app/helpers/sessions_helper.rb
  #(current_userがある前提)
  def subscribed?
    if current_user.nil?
      return false
    else
      team = Team.find_by(user_id: current_user.id)
      !team.nil?
    end
  end
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include SessionsHelper
   
    def subscribed_user
      unless subscribed?
        flash[:danger] = "Please subscribe"
        redirect_to new_team_path
      end
    end
end

これでいろいろなコントローラで
before_action :subscribed_user, only: [:new, :create, :edit, :update, :destroy]
とすることでお金を払っていないユーザーさんの利用を制限できるようになりました。

#Special Thanks
https://qiita.com/tady/items/7617e62b2a5402ebd0fb
こちらの記事の筆者である@tadyさん。
本当に助かりました!!

#あとがき
ここまで読んでいただき、ありがとうございました。日本の経済が発展することを願って。

5
2
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
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?