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

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

Rails6.0 | 仕事売買サイトの構築 | 38 | Stripe(ストライプ)

[37]ステータスの表示 << [ホームに戻る] >> [39]Stripeコネクト


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


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


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

公開可能キーとシークレットキー
公開可能キーとシークレットキー


記述追加 GemFile(80行目)

gem 'stripe', '=4.18.1'



GemFile

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

ruby '2.6.3'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3'
# 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]

# Bulma
gem 'bulma-rails', '~> 0.7.4'
gem 'bulma-extensions-rails', '~> 1.0.30'

#デバイス
gem 'devise'

# 日本語化
gem 'rails-i18n'

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

# amazon s3
gem "aws-sdk"

# faker
gem 'faker', '=1.9.3'

#ページネーション
gem 'kaminari'

# 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>

<section class="section">
    <div class="container">
        <div class="columns is-centered">
            <div class="column is-8-tablet is-6-desktop is-6-widescreen">
                <p class="title has-text-centered">クレジットカード登録</p>
                <% if current_user.stripe_last_4 %>
                  <div class="card">
                      <div class="card-header-title is-centered">
                        <%= "登録カード: **** **** **** #{current_user.stripe_last_4}" %>
                      </div>
                  </div>
                <% end %>

                <%= form_with url: update_payment_path, local: true, id: "payment-form" do |f| %>
                    <div class="card form-row">
                        <div id="card-element">
                        <!-- ストライプ要素がここに挿入されます。 -->
                        </div>

                        <!-- フォームのエラーを表示するために使用されます。 -->
                        <div id="card-errors" role="alert"></div>
                    </div>
                    <div class="field is-grouped is-grouped-centered">
                        <%= f.submit "#{current_user.stripe_id ? "カードを更新する" : "カードを追加する"}", 
                                        class: "button is-primary m-t-20" %>
                    </div>
                <% end %>
            </div>
        </div>
    </div>
</section>

<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(20行目)

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



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

 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'

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

  get 'pages/home'
  get '/dashboard', to: 'users#dashboard'
  get '/users/:id', to: 'users#show', as: 'users'
  get '/selling_orders', to: 'orders#selling_orders'
  get '/buying_orders', to: 'orders#buying_orders'
  get '/all_requests', to: 'requests#list'
  get '/request_offers/:id', to: 'requests#offers', as: 'request_offers'
  get '/my_offers', to: 'requests#my_offers'
  get '/search', to: 'pages#search'
  get 'settings/payment', to: 'users#payment', as: 'settings_payment'

  post '/users/edit', to: 'users#update'
  post '/offers', to: 'offers#create'
  post '/reviews', to: 'reviews#create'
  post '/settings/payment', to: 'users#update_payment', as: "update_payment"

  put '/orders/:id/complete', to: 'orders#complete', as: 'complete_order'
  put '/offers/:id/accept', to: 'offers#accept', as: 'accept_offer'
  put '/offers/:id/reject', to: 'offers#reject', as: 'reject_offer'

  resources :gigs do
    member do
      delete :delete_photo
      post :upload_photo
    end
    resources :orders, only: [:create]
  end

  resources :requests

  # 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(23行目)

  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
  end

  def show
    @user = User.find(params[:id])
    @reviews = Review.where(seller_id: params[:id]).order("created_at desc")
  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(:from, :about, :status, :language, :avatar)
  end
  
end



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


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


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

<%= link_to 'クレジットカード登録', settings_payment_path, class: "navbar-item", data: { turbolinks: false} %>



app\views\shared\_navbar.html.erb

<nav class="navbar is-danger" role="navigation" aria-label="main navigation">
    <div class="navbar-brand">
        <a class="navbar-item" href="/">
            <h1>テストサイトOshigoto</h1>
        </a>
        <a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
        </a>
    </div>
    <div id="navbarBasicExample" class="navbar-menu">
        <div class="navbar-start">
            <div class="navbar-item">
                <%= form_tag search_path, method: :get do %>
                    <div class="field has-addons <%= 'is-hidden' if current_page?(root_path) %>">
                        <div class="control has-icons-left">
                            <span class="icon is-small is-left">
                                <i class="fa fa-search"></i>
                            </span>
                            <%= text_field_tag 'q', '', class: "input", placeholder: "どんなお仕事を?" %>
                        </div>
                        <div class="control">
                            <button class="button is-success" type="submit">検索</button>
                        </div>
                    </div>
                <% end %>
            </div>
        </div>

        <div class="navbar-end">
            <a class="navbar-item"></a>
            <a class="navbar-item"></a>

            <!-- もしログインしていなかったら-->
            <% if (!user_signed_in?) %>
                <div class="navbar-item">
                    <div class="buttons">
                        <%= link_to  "新規ユーザ登録", new_user_registration_path, class: "button is-primary" %>
                        <%= link_to  "ログイン", new_user_session_path, class: "button is-light" %>
                    </div>
                </div>

            <!-- ログインしていたら -->
            <% else %>
                <div class="navbar-item has-dropdown is-hoverable">
                     <a class="navbar-link">
                        <figure class="image is-48x48 m-r-5 avatar <%= current_user.status ? "online" : "offline" %>">
                            <div style="margin-top: 0.6rem;">
                                <%= image_tag avatar_url(current_user), class: "is-rounded" %>
                            </div>
                        </figure>
                        <%= current_user.full_name %>
                    </a>
                    <div class="navbar-dropdown">
                        <%= link_to 'ダッシュボード', dashboard_path, class: "navbar-item" %>
                        <%= link_to  "ユーザ登録情報編集", edit_user_registration_path, class: "navbar-item" %>
                        <hr class="navbar-divider">
                        <%= link_to  "ログアウト", destroy_user_session_path, method: :delete, class: "navbar-item" %>
                    </div>
                </div>
            <% end %>
        </div>
    </div>
</nav>

<% if (user_signed_in?) && !current_page?(root_path) && !current_page?(search_path) && !current_page?("/gigs/#{params[:id]}") && !current_page?("/users/#{params[:id]}") %>
    <nav class="navbar has-shadow" style="z-index: 5;">
        <div class="container">
            <div class="navbar">
                <%= link_to 'ダッシュボード', dashboard_path, class: "navbar-item" %>
                <div class="navbar-item has-dropdown is-hoverable">
                    <a class="navbar-link">お仕事を売る人</a>
                    <div class="navbar-dropdown">
                        <%= link_to 'お仕事を登録する', new_gig_path, class: "navbar-item" %>
                        <%= link_to '売れた注文の確認', selling_orders_path, class: "navbar-item" %>
                         <%= link_to 'リクエストを見る', all_requests_path, class: "navbar-item" %>
                         <%= link_to '申込みの確認', my_offers_path, class: "navbar-item" %>
                    </div>
                </div>
                <div class="navbar-item has-dropdown is-hoverable">
                    <a class="navbar-link">お仕事を買う人</a>
                    <div class="navbar-dropdown">
                        <%= link_to '買った注文の確認', buying_orders_path, class: "navbar-item" %>
                        <%= link_to 'お仕事をリクエストする', new_request_path, class: "navbar-item" %>
                        <%= link_to 'リクエストの確認', requests_path, class: "navbar-item" %>
                        <%= link_to 'クレジットカード登録', settings_payment_path, class: "navbar-item", data: { turbolinks: false} %>
                    </div>
                </div>
            </div>
        </div>
    </nav>
<% end %>

<script>
$(document).ready(function() {
  // navbar burgerアイコンでクリックイベントを確認する
  $(".navbar-burger").click(function() {
      // 「navbar-burger」と「navbar-menu」の両方で「is-active」クラスを切り替える
      $(".navbar-burger").toggleClass("is-active");
      $(".navbar-menu").toggleClass("is-active");
  });
});
</script>



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


テストカードは「4242 4242 4242 4242」を使用して下さい。
あとの情報は何でも大丈夫です。

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



登録すると下4ケタが表示されます。

下4ケタ
下4ケタ



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

StripeID格納
StripeID格納



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

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



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


[37]ステータスの表示 << [ホームに戻る] >> [39]Stripeコネクト