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

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

Rails6.0 | 民泊サイトの構築(改良版)| 37 |Stripeによるクレジットカード決済の実装 | for MacOSX



| 36 |ページネーションの実装 <<  [ホーム] >> | 38 | Stripe Connectの実装


Stripe(ストライプ)を使ってでクレジット決済ができるようにします。


まずは以下の手順でStripeのアカウントを取得してください。
mrradiology.hatenablog.jp


ダッシュボードで「公開可能キー」と「シークレットキー」をコピーします。
公開可能キーとシークレットキー
公開可能キーとシークレットキー


記述追加 GemFile(89行目)

# stripe
gem 'stripe', '=4.18.1'



GemFile

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.6'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main'
gem 'rails', '~> 6.0.4', '>= 6.0.4.7'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# 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.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

# デバイス
gem 'devise'

# Rubyバージョンエラー修正
gem "net-http"

# 日本語化
gem 'rails-i18n'

# アマゾンS3
gem 'aws-sdk', '~> 3'

# アクションテキスト画像表示
gem "mini_magick"
gem 'image_processing', '~> 1.2'

#googleマップ
gem 'geocoder', '~> 1.4'

# facebook認証
gem 'omniauth', '= 1.9.0'
gem 'omniauth-facebook', '= 5.0.0'

# google認証
gem 'omniauth-google-oauth2'

# 検索
gem 'ransack', '~> 2.3'

# ページネーション
gem 'kaminari', '~> 1.2', '>= 1.2.1'
gem 'bootstrap5-kaminari-views', '~> 0.0.1'

# stripe
gem 'stripe', '=4.18.1'



コマンド
bundle update


ユーザテーブルにStripeのカラムを追加します。


コマンド
rails g migration AddStripeToUser stripe_last_4 stripe_id


コマンド マイグレーション適用
rails db:migrate


「config\initializers」フォルダに「stripe.rb」ファイルを新規作成してください。


config\initializers\stripe.rb(新規作成したファイル)
ご自分の公開可能キーとシークレットキーを入れて下さい

Rails.configuration.stripe = {

    #ご自分の公開可能キーとシークレットキーを入れて下さい
    :publishable_key => 'ご自分の公開可能キーを入れてください。',
    :secret_key => 'ご自分のシークレットキーを入れてください'

}
Stripe.api_key = Rails.configuration.stripe[:secret_key]

Stripe.api_version = '2020-03-02'



「app\views\users」フォルダに「payment.html.erb」ファイルを新規作成して下さい。


app\views\users\payment.html.erb(新規作成したファイル)

<style>
    .StripeElement {
    box-sizing: border-box;
    height: 40px;
    padding: 10px 12px;
    border: 1px solid transparent;
    border-radius: 4px;
    background-color: white;
    box-shadow: 0 1px 3px 0 #e6ebf1;
    -webkit-transition: box-shadow 150ms ease;
    transition: box-shadow 150ms ease;
    }
    .StripeElement--focus {
    box-shadow: 0 1px 3px 0 #cfd7df;
    }
    .StripeElement--invalid {
    border-color: #fa755a;
    }
    .StripeElement--webkit-autofill {
    background-color: #fefde5 !important;
    }
</style>
<div class="row" style="margin-left: 10rem;">
<div class="col-sm-8">
    <br/>
    <div class="card text-center">
    <% if current_user.stripe_id? %>
        <h4 class="card-header text-center bg-primary text-light">
        <span style="font-size: 1.3rem;"><i class="far fa-credit-card"></i> クレジットカード更新</span>
        <% if current_user.stripe_last_4 %>
              <span style="font-size: 1rem;"><%= "(登録カード: **** **** **** #{current_user.stripe_last_4})" %></span>
      <% end %> 
    <% else %>
    <h4 class="card-header text-center bg-primary text-light">
    <span style="font-size: 1.3rem;"><i class="far fa-credit-card"></i>クレジットカード登録</span>
    <% if current_user.stripe_last_4 %>
          <span style="font-size: 1rem;"><%= "(**** **** **** #{current_user.stripe_last_4})" %></span>
  <% end %> 

    <% end %>    
                
        </h4>
<div style="margin: 50px; text-align: left;">
            <br/>
            <br/>
            [対応カード]<br>

            </p>
            <br/>
                <%= form_with url: update_payment_path, local: true, id: "payment-form" do |f| %>
                        <div id="card-element">
                        <!-- ストライプ要素がここに挿入されます。 -->
                        </div>
                        <!-- フォームのエラーを表示するために使用されます。 -->
                        <div id="card-errors" role="alert"></div>
                        <br/>
                        <br/>
                        <%= f.submit "#{current_user.stripe_id ? "カードを更新する" : "カードを追加する"}", 
                                        class: "btn btn-outline-danger btn-block rounded-pill" %>
                <% end %>
                <br/>
               <br/>
               <span style="font-size: 0.8rem;">CVCとは、クレジットカードの裏面に記載されている暗証番号です。<br/>
               ほとんどのカード(Visa、MasterCardなど)ではカード裏面の署名欄に記載されています。<br/>
               記載番号の最後の3ケタがこれにあたります。<br/>
               American Express(AMEX)カードでは通常、カード前面の4ケタのコードとなります。</span><br/><br/>
               
</div>
    </div>
</div>
</div>
<script src="https://js.stripe.com/v3/"></script>
<script>
    // Stripeクライアントを作成します。
    var stripe = Stripe('<%= Rails.configuration.stripe[:publishable_key] %>');
    // Elementsのインスタンスを作成します。
    var elements = stripe.elements();
    // Elementの作成時に、カスタムスタイリングをオプションに渡すことができます。
    var style = {
        base: {
            color: '#32325d',
            fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
            fontSmoothing: 'antialiased',
            fontSize: '16px',
            '::placeholder': {
            color: '#aab7c4'
            }
        },
        invalid: {
            color: '#fa755a',
            iconColor: '#fa755a'
        }
    };
    // カード要素のインスタンスを作成します。
    var card = elements.create('card', {style: style});
    // カード要素のインスタンスを `card-element` <div>に追加します。
    card.mount('#card-element');
    // カード要素からのリアルタイム検証エラーを処理します。
    card.addEventListener('change', function(event) {
        var displayError = document.getElementById('card-errors');
        if (event.error) {
            displayError.textContent = event.error.message;
        } else {
            displayError.textContent = '';
        }
    });
    // フォームの送信を処理します。
    var form = document.getElementById('payment-form');
        form.addEventListener('submit', function(event) {
        event.preventDefault();
        stripe.createToken(card).then(function(result) {
            if (result.error) {
            // エラーが発生したかどうかをユーザーに通知します。
            var errorElement = document.getElementById('card-errors');
            errorElement.textContent = result.error.message;
            } else {
            // トークンをサーバーに送信します。
            stripeTokenHandler(result.token);
            }
        });
    });
    // トークンIDを使用してフォームを送信します。
    function stripeTokenHandler(token) {
        // トークンIDをフォームに挿入して、サーバーに送信されるようにします
        var form = document.getElementById('payment-form');
        var hiddenInput = document.createElement('input');
        hiddenInput.setAttribute('type', 'hidden');
        hiddenInput.setAttribute('name', 'stripeToken');
        hiddenInput.setAttribute('value', token.id);
        form.appendChild(hiddenInput);
        // フォームを送信する
        form.submit();
        }
</script>



ルートを設定します。


「config\routes.rb」ファイルに以下の記述を追加します。


1.記述追加 config\routes.rb(13行目)

get 'settings/payment', to: 'users#payment', as: 'settings_payment'



2.記述追加 config\routes.rb(16行目)

 post '/settings/payment', to: 'users#update_payment', as: "update_payment"



config\routes.rb

Rails.application.routes.draw do

  # ルートを app\views\pages\home.html.erb に設定
  root 'pages#home'

  get 'pages/home'
  get '/dashboard', to: 'users#dashboard'
  get '/users/:id', to: 'users#show', as: 'user'
  get '/your_trips' => 'reservations#your_trips'
  get '/your_reservations' => 'reservations#your_reservations'
  get 'search' => 'pages#search'
  get '/host_calendar' => "calendars#host"
  get 'settings/payment', to: 'users#payment', as: 'settings_payment'
  
  post '/users/edit', to: 'users#update'
  post '/settings/payment', to: 'users#update_payment', as: "update_payment"

  devise_for :users, 
  path: '', 
  path_names: {sign_up: 'register', sign_in: 'login', edit: 'profile', sign_out: 'logout'},
  controllers: {omniauth_callbacks: 'omniauth_callbacks', registrations: 'registrations'}

  resources :rooms, except: [:edit] do
    member do
      get 'listing'
      get 'pricing'
      get 'description'
      get 'photo_upload'
      get 'amenities'
      get 'location'
      get 'preload'
      get 'preview'
      delete :delete_photo
      post :upload_photo
    end
    resources :reservations, only: [:create]
  end

  resources :guest_reviews, only: [:create, :destroy]
  resources :host_reviews, only: [:create, :destroy]

  resources :reservations, only: [:approve] do
    member do
      post '/approve' => "reservations#approve"
    end
  end

  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end



「app\controllers\users_controller.rb」ファイルに以下の記述を追加します。


記述追加 app\controllers\users_controller.rb(33行目)

  def update_payment
    if !current_user.stripe_id
      customer = Stripe::Customer.create(
        email: current_user.email,
        source: params[:stripeToken]
      )
    else
      customer = Stripe::Customer.update(
        current_user.stripe_id,
        source: params[:stripeToken]
      )
    end

    if current_user.update(stripe_id: customer.id, stripe_last_4: customer.sources.data.first["last4"])
      flash[:notice] = "新しいカード情報が登録されました"
    else
      flash[:alert] = "無効なカードです"
    end
    redirect_to request.referrer
  rescue Stripe::CardError => e
    flash[:alert] = e.message
    redirect_to request.referrer
  end



app\controllers\users_controller.rb

class UsersController < ApplicationController

  before_action :authenticate_user!
  
  def dashboard
    @user = current_user
    @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 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
    @user = current_user
    if @user.update_attributes(current_user_params)
      flash[:notice] = "保存しました"
    else
      flash[:alert] = "更新できません"
    end
    redirect_to dashboard_path
  end

  def update_payment
    if !current_user.stripe_id
      customer = Stripe::Customer.create(
        email: current_user.email,
        source: params[:stripeToken]
      )
    else
      customer = Stripe::Customer.update(
        current_user.stripe_id,
        source: params[:stripeToken]
      )
    end
    if current_user.update(stripe_id: customer.id, stripe_last_4: customer.sources.data.first["last4"])
      flash[:notice] = "クレジットカードが登録されました"
    else
      flash[:alert] = "無効なカードです"
    end
    redirect_to request.referrer
  rescue Stripe::CardError => e
    flash[:alert] = e.message
    redirect_to request.referrer
  end

  private
  def current_user_params
    params.require(:user).permit(:about, :status, :avatar)
  end
  
end



ナビゲーションバーのリンクを追加します。


「app\views\shared\_navbar.html.erb」ファイルに以下の記述を追加します。


記述追加 app\views\shared\_navbar.html.erb(68行目)

<span class="dropdown-item"><i class="far fa-credit-card"></i><%= link_to 'クレジットカード登録', settings_payment_path, class: "btn btn-white", data: { turbolinks: false} %></span>



app\views\shared\_navbar.html.erb

<nav class="navbar navbar-expand-lg navbar-dark bg-info" style="z-index: 5;">
  <a class="navbar-brand" href="/"><h1 class="navh1">MinpakuBs</h1></a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>
  <div class="collapse navbar-collapse justify-content-end" id="navbarNavDropdown">
    <ul class="navbar-nav">
    <!-- もしログインしていなかったら-->
    <% if (!user_signed_in?) %>
      <li class="nav-item" style="margin-right: 20px; margin-bottom: 5px;">
        <%= link_to  "新規ユーザ登録", new_user_registration_path, class: "btn btn-light" %>
      </li>
      <li class="nav-item">
        <%= link_to  "ログイン", new_user_session_path, class: "btn btn-light", style: "margin-right: 80px;" %>
      </li>
    <!-- ログインしていたら -->
    <% else %>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

        <figure class="avatar <%= current_user.status ? "online" : "offline" %>"></figure>  
        <%= image_tag avatar_url(current_user), class: "bd-placeholder-img figure-img img-fluid rounded-pill", style: "width: 40px; height: 30px;" %>
 

        <%= current_user.full_name %>

        </a>
        <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
        <span class="dropdown-item"><i class="fas fa-user-edit"></i><%= link_to  "ユーザ登録情報編集", edit_user_registration_path, class: "btn btn-white" %></span>
          <hr/>
          <span class="dropdown-item"><i class="fas fa-sign-out-alt"></i><%= link_to  "ログアウト", destroy_user_session_path, method: :delete, class: "btn btn-white" %></span>
        </div>
      </li>
    <% end %>
    </ul>
  </div>
</nav>

<% if (user_signed_in?) %>

  <nav class="navbar navbar-expand-lg navbar-light bg-light" style="z-index: 4;">

    <div class="collapse navbar-collapse justify-content-end" id="navbarNavDropdown">
      <ul class="navbar-nav mr-auto">

        <li class="nav-item" style="margin-left: 100px; margin-bottom: 5px; margin-right: 80px;">
        <span style="margin-top:13px;"><i class="fas fa-tachometer-alt"></i></span><%= link_to 'ダッシュボード', dashboard_path, class: "btn btn-light" %>
        </li>

        <li class="nav-item dropdown" style="margin-right: 50px;">
          <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          <i class="fas fa-hospital-symbol"></i>&nbsp;ホスト
          </a>
          <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
            <span class="dropdown-item"><i class="fas fa-edit"></i></i><%= link_to  "お部屋を新規登録", new_room_path, class: "btn btn-white" %></span>
            <span class="dropdown-item"><i class="far fa-list-alt"></i><%= link_to  "登録したお部屋一覧", rooms_path, class: "btn btn-white" %></span>
            <span class="dropdown-item"><i class="far fa-list-alt"></i><%= link_to "受注予約の管理", your_reservations_path, class: "btn btn-white" %></span>
            
          </div>
        </li>

        <li class="nav-item dropdown">
          <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          <i class="fas fa-user-friends"></i>&nbsp;ゲスト
          </a>
          <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
            <span class="dropdown-item"><i class="far fa-list-alt"></i><%= link_to "ご予約の確認", your_trips_path, class: "btn btn-white" %></span>
            <span class="dropdown-item"><i class="far fa-credit-card"></i><%= link_to 'クレジットカード登録', settings_payment_path, class: "btn btn-white", data: { turbolinks: false} %></span>
          </div>
        </li>

      </ul>
    </div>
  </nav>
<% end %>



ブラウザ確認
http://localhost:3000/settings/payment


テストカードは「4242 4242 4242 4242」を使用して下さい。
あとの情報は何でも大丈夫です。
登録すると下4ケタが表示されます。

クレジットカード登録
クレジットカード登録



ユーザテーブルにStripeのIDが格納されます。

StripeID格納
StripeID格納



Stripeのダッシュボードで「顧客」を見ると登録されているのが確認できます。

Stripeダッシュボード
Stripeダッシュボード



↓↓クリックして頂けると励みになります。


| 36 |ページネーションの実装 <<  [ホーム] >> | 38 | Stripe Connectの実装

YAE C5 CLINIC(札幌美容クリニック)

関連記事(外部サイト)