まずは以下の手順でStripeのアカウントを取得してください。
mrradiology.hatenablog.jp
ダッシュボードで「公開可能キー」と「シークレットキー」をコピーします。
記述追加 GemFile(88行目)
gem 'stripe', '~> 3.0.0' gem 'rails-assets-card', source: 'https://rails-assets.org'
GemFile
source 'https://rubygems.org' git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") "https://github.com/#{repo_name}.git" end # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.0.7', '>= 5.0.7.1' # Use postgresql as the database for Active Record gem 'pg', '>= 0.18', '< 2.0' # Use Puma as the app server gem 'puma', '~> 3.0' # Use SCSS for stylesheets gem 'sass-rails', '~> 5.0' # Use Uglifier as compressor for JavaScript assets gem 'uglifier', '>= 1.3.0' # Use CoffeeScript for .coffee assets and views gem 'coffee-rails', '~> 4.2' # See https://github.com/rails/execjs#readme for more supported runtimes # gem 'therubyracer', platforms: :ruby # Use jquery as the JavaScript library gem 'jquery-rails' # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks gem 'turbolinks', '~> 5' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder gem 'jbuilder', '~> 2.5' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 3.0' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platform: :mri end group :development do # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. gem 'web-console', '>= 3.3.0' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] #bootstrap gem 'bootstrap-sass', '~> 3.4.1' #デバイス gem 'devise', '~>4.2' #Gravatar gem 'gravtastic' #フラッシュメッセージ gem 'toastr-rails', '~> 1.0' #日本語化 gem 'rails-i18n' #画像アップロード gem 'paperclip', '~> 5.1.0' #googleマップ gem 'geocoder', '~> 1.4' #アマゾンS3 gem 'aws-sdk', '~> 2.8' #日付ピッカー gem 'jquery-ui-rails', '~> 5.0' # 検索 gem 'ransack', '~> 1.7' # 電話番号認証 gem 'twilio-ruby', '~> 4.11.1' # フルカレンダー gem 'fullcalendar-rails', '~> 3.4.0' gem 'momentjs-rails', '~> 2.17.1' # Stripe決済 gem 'stripe', '~> 3.0.0' gem 'rails-assets-card', source: 'https://rails-assets.org'
コマンド
bundle
記述追加 app\assets\javascripts\application.js
22行目に「//= require card」の記述追加
// This is a manifest file that'll be compiled into application.js, which will include all the files // listed below. // // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. // // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // compiled file. JavaScript code in this file should be added after the last require_* statement. // // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // about supported directives. // //= require jquery //= require bootstrap-sprockets //= require jquery_ujs //= require jquery-ui/datepicker //= require jquery-ui/slider //= require toastr //= require moment //= require fullcalendar //= require gravtastic //= require card //= require turbolinks //= require_tree .
「config\initializers」フォルダに「stripe.rb」ファイルを新規作成して下さい。
config\initializers\stripe.rb(新規作成したファイル)
ご自分の公開可能キーとシークレットキーを入れて下さい。
Rails.configuration.stripe = { :publishable_key => 'pk_test_51GzsbVIgf2I7nZrombX4thnRVhCJd0uheuUUCqqr5xUzsAlruqJ9dzZORtseP6AK6dgKwkvI73xrArQmLuUTrgFI00QaRjEZYt', :secret_key => 'sk_test_51GzsbVIgf2I7nZroSjoq3KzrjPAxCGNpoeTYprcYVHISJaERKGgPS1AKwlzIkrwEa4KlWc3IHzlX8pAYnsU9cgEe00nXwSuUX7' } Stripe.api_key = Rails.configuration.stripe[:secret_key]
コマンド
rails g migration AddStripeIdToUser stripe_id
コマンド マイグレーション
rails db:migrate
記述追加 app\controllers\users_controller.rb(44行目)
def payment end def add_card if current_user.stripe_id.blank? customer = Stripe::Customer.create( email: current_user.email ) current_user.stripe_id = customer.id current_user.save # Stripeにカード情報を追加 customer.sources.create(source: params[:stripeToken]) else customer = Stripe::Customer.retrieve(current_user.stripe_id) customer.source = params[:stripeToken] customer.save end flash[:notice] = "カード情報が保存されました" redirect_to payment_method_path rescue Stripe::CardError => e flash[:alert] = e.message redirect_to payment_method_path end
app\controllers\users_controller.rb
class UsersController < ApplicationController def show @user = User.find(params[:id]) @rooms = @user.rooms # ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示 @guest_reviews = Review.where(type: "GuestReview", host_id: @user.id) # ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示 @host_reviews = Review.where(type: "HostReview", guest_id: @user.id) end def update_phone_number current_user.update_attributes(user_params) # 日本の電話国番号+81を先頭につける japan_number = user_params[:phone_number].sub(/\A./,'+81') current_user.update_attributes(phone_number: japan_number) current_user.generate_pin current_user.send_pin redirect_to edit_user_registration_path, notice: "保存しました。" rescue Exception => e redirect_to edit_user_registration_path, alert: "#{e.message}" end def verify_phone_number current_user.verify_pin(params[:user][:pin]) if current_user.phone_verified flash[:notice] = "電話番号が確認されました。" else flash[:alert] = "電話番号を確認できません。" end redirect_to edit_user_registration_path rescue Exception => e redirect_to edit_user_registration_path, alert: "#{e.message}" end def payment end def add_card if current_user.stripe_id.blank? customer = Stripe::Customer.create( email: current_user.email ) current_user.stripe_id = customer.id current_user.save # Stripeにカード情報を追加 customer.sources.create(source: params[:stripeToken]) else customer = Stripe::Customer.retrieve(current_user.stripe_id) customer.source = params[:stripeToken] customer.save end flash[:notice] = "カード情報が保存されました" redirect_to payment_method_path rescue Stripe::CardError => e flash[:alert] = e.message redirect_to payment_method_path end private def user_params params.require(:user).permit(:phone_number, :pin) end end
記述追加 config\routes.rb(12行目と14行目)
get '/payment_method' => "users#payment" post '/add_card' => "users#add_card"
config\routes.rb
Rails.application.routes.draw do #ルートをpages#homeに設定 root 'pages#home' get 'pages/home' get '/your_trips' => 'reservations#your_trips' get '/your_reservations' => 'reservations#your_reservations' get 'search' => 'pages#search' get 'dashboard' => 'dashboards#index' get '/host_calendar' => "calendars#host" get '/payment_method' => "users#payment" post '/add_card' => "users#add_card" resources :users, only: [:show] do member do post '/verify_phone_number' => 'users#verify_phone_number' patch '/update_phone_number' => 'users#update_phone_number' end end resources :rooms, except: [:edit] do member do get 'listing' get 'pricing' get 'description' get 'photo_upload' get 'amenities' get 'location' get 'preload' get 'preview' end resources :photos, only: [:create, :destroy] resources :reservations, only: [:create] resources :calendars end resources :guest_reviews, only: [:create, :destroy] resources :host_reviews, only: [:create, :destroy] resources :reservations, only: [:approve, :decline] do member do post '/approve' => "reservations#approve" post '/decline' => "reservations#decline" end end devise_for :users, path: '', path_names: {sign_in: 'login', sign_out: 'logout', edit: 'profile', sign_up: 'registration'}, controllers: {registrations: 'registrations'} # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end
「app\views\users\」フォルダに「payment.html.erb」ファイルを新規作成してください。
app\views\users\payment.html.erb(新規作成したファイル)
<div class="row"> <div class="col-md-3"> <ul class="sidebar-list"> <li class="sidebar-item"><%= link_to "クレジットカード情報の追加", payment_method_path, class: "sidebar-link active" %></li> </ul> </div> <div class="col-md-9"> <div class="panel panel-default"> <div class="panel-heading">お客様のクレジットカード情報 <% if current_user.stripe_id? %> (クレジットカード登録済みです) <% else %> (まだクレジットカードが登録されていません) <% end %> </div> <div class="panel-body"> <div class="container"> <%= form_tag("/add_card", method: "post", id: "add-card") do %> <label> <span>氏名</span> <input name="cardholder-name" class="field" placeholder="MINPAKU TAROU" /> </label> <label> <span>カード番号</span> <div id="card-element" class="field"></div> </label> <div class="outcome"> <div class="error" role="alert"></div> </div> <% if current_user.stripe_id %> <button type="submit" class="btn btn-normal btn-block" style="padding-top: 0;">カードの更新</button> <% else %> <button type="submit" class="btn btn-normal btn-block" style="padding-top: 0;">カードを追加する</button> <% end %> <% end %> </div> </div> </div> </div> </div> <script src="https://js.stripe.com/v3/"></script> <script> $(function() { var stripe = Stripe('<%= Rails.configuration.stripe[:publishable_key] %>'); var elements = stripe.elements(); var card = elements.create('card', { hidePostalCode: true, style: { base: { iconColor: '#F99A52', color: '#32315E', lineHeight: '48px', fontWeight: 400, fontFamily: '"Helvetica Neue", "Helvetica", sans-serif', fontSize: '15px', '::placeholder': { color: '#CFD7DF', } }, } }); card.mount('#card-element'); function setOutcome(result) { var errorElement = document.querySelector('.error'); errorElement.classList.remove('visible'); if (result.token) { var form = $('#add-card'); form.append($('<input type="hidden" name="stripeToken">').val(result.token.id)); form.get(0).submit(); } else if (result.error) { errorElement.textContent = result.error.message; errorElement.classList.add('visible'); } } card.on('change', function(event) { setOutcome(event); }); $('#add-card').on('submit', function(e) { e.preventDefault(); var extraDetails = { name: $('input[name=cardholder-name]').value }; stripe.createToken(card, extraDetails).then(setOutcome); }); }); </script>
記述更新 app\controllers\reservations_controller.rb
5行目の「create()」メソッドに記述追加、67行目にプライベートメソッド「charge()」を追加しています。
55行目を「charge(@reservation.room, @reservation)」の記述に書き換えています。
コードをコピーしてファイルを置き換えて下さい。
class ReservationsController < ApplicationController before_action :authenticate_user! before_action :set_reservation, only: [:approve, :decline] def create room = Room.find(params[:room_id]) if current_user == room.user flash[:alert] = "オーナーが予約することはできません。" elsif current_user.stripe_id.blank? flash[:alert] = "予約する前にクレジットカードを登録する必要があります。" return redirect_to payment_method_path else start_date = Date.parse(reservation_params[:start_date]) end_date = Date.parse(reservation_params[:end_date]) days = (end_date - start_date).to_i if days == 0 flash[:alert] = "宿泊日数が1泊以上でなければ予約することはできません。" else @reservation = current_user.reservations.build(reservation_params) @reservation.room = room @reservation.price = room.price @reservation.total = room.price * days #@reservation.save if @reservation.Waiting! if room.Request? flash[:notice] = "予約承認申請を送信しました。予約が承認されるまでしばらくお待ち下さい。" else charge(room, @reservation) end else flash[:alert] = "ご予約できません!" end end end redirect_to room end # 宿泊者用予約確認 def your_trips @trips = current_user.reservations.order(start_date: :asc) end #ホスト用予約確認 def your_reservations @rooms = current_user.rooms end def approve charge(@reservation.room, @reservation) redirect_to your_reservations_path end def decline @reservation.Declined! redirect_to your_reservations_path end private def charge(room, reservation) if !reservation.user.stripe_id.blank? customer = Stripe::Customer.retrieve(reservation.user.stripe_id) charge = Stripe::Charge.create( :customer => customer.id, :amount => reservation.total, :description => room.listing_name, :currency => "JPY" ) if charge reservation.Approved! flash[:notice] = "お支払い手続きが完了し、ご予約されました。お越しをお待ちしております!" else reservation.Declined! flash[:notice] = "お支払い手続きができません。予約ができませんでした。" end end rescue Stripe::CardError => e reservation.declined! flash[:alert] = e.message end def set_reservation @reservation = Reservation.find(params[:id]) end def reservation_params params.require(:reservation).permit(:start_date, :end_date) end end
ブラウザ確認
http://localhost:3000/payment_method
まずクレジットカードを登録しないと予約ができないようにしています。
テストカードは番号「4242 4242 4242 4242」を使用して下さい。
他の入力は何でも大丈夫です。
カードを登録するとユーザテーブルの「stripe_id」にストライプのIDが入ります。
実際に予約するとストライプでテスト決済が確認できます。
ストライプのダッシュボードページで「支払い」を選択し、テスト決済を確認します。