学生向けプログラミング入門 | 無料

学生向けにプログラミングを無料で解説。Java、C++、Ruby、PHP、データベース、Ruby on Rails, Python, Django

【民泊5.1】【Windows】Stripe決済

まずは以下の手順で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」を使用して下さい。
他の入力は何でも大丈夫です。

テストカード「4242 4242 4242 4242」
テストカード「4242 4242 4242 4242」


カードを登録するとユーザテーブルの「stripe_id」にストライプのIDが入ります。
ストライプID
ストライプID


実際に予約するとストライプでテスト決済が確認できます。
ストライプのダッシュボードページで「支払い」を選択し、テスト決済を確認します。
テスト決済を確認
テスト決済を確認