4
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

【Rails】かんたん決済機能の実装

採用担当者さんはお忙しいだろうけど、
どうしても決済機能を導入したい!

そんな方に見てほしい記事です!

この記事では、かんたん決済機能の実装方法について解説しています。

ここで言うかんたん決済機能とは、
ワンクリックで決済「したことにする」という機能です。

ポートフォリオへの実装を想定しています。

一つだけ注意点があります。
この記事の方法では、Payjpなどの外部APIには決済履歴は残りません。
(tokenをダミーとして生成するためです)

参考になれば幸いです。

前提

注意:この記事ではクレジット決済は実装しません!

開発環境
・ruby 2.6.5
・Rails 6.0.3.3

完成イメージ

かんたん決済プレビュー

金額とカード情報を入力すると決済できます。
 
今回は、未入力で決済できる「簡単ギフト」ボタンを実装します!
(注意! この記事の方法では、外部APIには決済履歴は保存されません!)
 
ここでいうギフトとは、
誰かが、誰かにお金を送る。
という意味です。
 
AさんがBさんに500円送った。
みたいな感じです!

ER図

244b268980cfe94a12803201742cdfb4.png

ギフトに必要な2つのテーブルです
・usersテーブル
・giftsテーブル

各カラムについては、後ほど説明します。

実装の流れ

①user, giftモデル、テーブルの作成

②ルーティングを設定

③アソシエーションを書く

④giftsコントローラーにアクションを定義

⑤ビューにリンクを設置

①user, giftモデル、テーブルの作成

userモデルのマイグレーションを編集

userモデルは、deviseで作成しています。
deviseは導入していなくても大丈夫です!

db/migrate/xxxx_devise_create_users.rb
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :nickname,  null: false
      t.string :email,     null: false, default: ""
      #省略
    end
    #省略
  end
end

usersテーブルには、nicknameemailのカラムを用意しています。

giftモデルのマイグレーションを編集

db/migrate/xxxx_create_gifts.rb
class CreateGifts < ActiveRecord::Migration[6.0]
  def change
    create_table :gifts do |t|
      t.references :user,  foreign_key: true
      t.references :giver, foreign_key: { to_table: :users }
      t.integer :price,    null: false
      t.timestamps
    end
  end
end

giftsテーブルには、user, giver, priceという3つのカラムを用意しています。

それぞれ、
user   →ギフトを受け取る人
giver  →ギフトを送る人
price  →ギフトの金額
というデータを保存しています。

今回の決済では、ユーザー同士で行います。
一人のuserは複数のuserにギフトできます。
そのため、usersテーブル同士で「多対多」の関係を作る必要があります。

今回は、giftsテーブルを中間テーブルとして作成します。
したがって、giftsテーブルは、以下のように2つの役割を持っています。
・ギフト情報を管理
・usersテーブルと対応する中間テーブル

少し分かりづらいと思うので、
後ほど、アソシエーションを記述する時にしっかり説明します!

つづいて、giverのオプションについて説明します。
giverは、外部キーとして設定しています。
しかし、giverというテーブルは存在しません!
そこで「usersテーブルから参照してね!」
と、教えてあげる必要があります。
そのためオプションに、foreign_key: { to_table: :users }と書いています。

②ルーティングを設定

config/routes.rb

Rails.application.routes.draw do
  resources :users, only: [:edit, :update, :show] do
    member do
      post 'easygift', to: 'gifts#easy_gift'
    end
  end
end

かんたん決済のための、ルーティングを記述します。

pathには、easygiftと命名していますが、ここは好きな名前で大丈夫です。
アクション名も自由に決めてOKです。

③アソシエーションを書く

userモデル

app/models/user.rb
class User < ApplicationRecord
  has_many :gifts
  has_many :receivers, through: :gifts, source: :giver
  has_many :reverse_of_gifts, class_name: 'Gift', foreign_key: 'giver_id'
  has_many :giftings, through: :reverse_of_gifts, source: :user
end

userモデルのアソシエーションについて解説します。

大きく2つに分かれています。

1, 2行目が、giverを参照するための記述
3, 4行目が、userの参照するための記述

まず、1, 2行目について説明します。
has_many :giftsは、2行目の為の記述です。

2行目ですが、一つずつ順番に説明していきます。
has_many :receivers, through: :gifts, source: :giver
receiversは、架空のモデルです。
なぜ架空のモデルを作るのかについては、後ほど説明します!
through: :giftsは、giftsテーブルを経由して、
source: :giverは、
giverを参照してね!」という意味合いです。

まとめると、1, 2行目では、
giftsテーブルのgiverを参照してね!
ということをやっています!

 
もう少しついてきてください!
 
次に、3, 4行目について説明します。

3行目ですが、これまた長いので、一つずつ順番に説明していきます。
has_many :reverse_of_gifts, class_name: 'Gift', foreign_key: 'giver_id'
reverse_of_giftsも、架空のモデルです。
class_name: 'Gift'は、
reverse_of_giftsって言ったけど、実はgiftモデルのことなんだ笑」っていう感じです。
foreign_key: 'giver_id'は、
giver_idを参照して!」って意味になります。

4行目も、一つずつ見ていきます。
has_many :giftings, through: :reverse_of_gifts, source: :user
has_many :giftingsも、架空のモデルです。
through: :reverse_of_giftsは、reverse_of_giftsテーブルを経由して、
source: :userは、
userを参照してね!」という意味合いです。
やっていることは2行目と同じです。

まとめると、3, 4行目では、
giftsテーブルのuserを参照してね!
ということをやっています!

 
さて、「なぜ架空のモデルを作るのか!」について解説します!
結論から言うと、
アソシエーションの区別がつかなくなってしまうからです!

その理由について解説します。
まず、それぞれのアソシエーションの最初の方だけに注目しましょう。

  has_many :gifts
  has_many :receivers
  has_many :reverse_of_gifts
  has_many :giftings

これだと、別々のモデルを参照してるように見えますね。

続いて、仮に架空のモデルを作らなかった場合の記述をしていきます。

app/models/user.rb
class User < ApplicationRecord
  has_many :gifts
  has_many :users, through: :gifts, source: :giver
  has_many :gifts, foreign_key: 'giver_id'
  has_many :users, through: :gifts, source: :user
end

すると、
giftsテーブルを中間テーブルとするアソシエーションが、
2パターン記述されている
ことが分かります。

先ほどと同じように、それぞれのアソシエーションの最初の方だけに注目しましょう。

app/models/user.rb
class User < ApplicationRecord
  has_many :gifts
  has_many :users
  has_many :gifts
  has_many :users
end

って、どれがどれやねぇぇぇん!

もうおわかりでしょうか。
架空のモデルを記述しない場合、
アソシエーションの区別がつかなくなってしまいます

そうなることを防ぐために、架空のモデルを設けています!

giftモデル

app/models/gift.rb
class Gift < ApplicationRecord
  belongs_to :user
  belongs_to :giver, class_name: 'User'

  attr_accessor :token

  validates :user_id, presence: true
  validates :giver_id, presence: true
  validates :price, presence: true
  validates :token, presence: true
end

giverのオプションclass_name: 'User'は、「userモデルを参照してください!」という意味です。

バリデーションも設定しています。

④giftsコントローラーにアクションを定義

かんたん決済のアクションを記述します。

app/controllers/gifts_controller.rb
class RoomsController < ApplicationController
    #省略
      def easy_gift
      reciver = User.find(params[:id])
      gift = Gift.create(
        price: 500,
        user_id: reciver.id,
        giver_id: current_user.id,
        token: 'dummy'
      )
      redirect_to root_path
    end
    #省略
end

本記事では、実際のカード決済の記述は割愛します。
参考資料 : payjp/payjp-ruby

 
easy_giftは、ルーティングで定義したアクション名です。

gift = Gift.create(...)の中で、各キーにバリューをセットしています。

ここで、注目してほしいのが、token: 'dummy'です。
他のカラムとは違い、tokenはテーブルに保存しません。

「じゃあ、tokenに値渡さなくて良くない?」

そう思った方、、、私もそう思いました笑

しかし思い出してください!モデルの記述を!

app/models/gift.rb
class Gift < ApplicationRecord
  belongs_to :user
  belongs_to :giver, class_name: 'User'

  attr_accessor :token

  validates :user_id, presence: true
  validates :giver_id, presence: true
  validates :price, presence: true
  validates :token, presence: true   # 👈tokenにバリデーションの設定している!
end

tokenには、presence: trueというバリデーションを設定しています。
そのため、ギフトを生成する際に、tokenが無かったら、
バリデーションエラーとなり、ギフトはレコードとして保存されません。

そのため、ダミーのtokenを用意する必要があります。

今回は、tokenにバリュー 'dummy'を渡しています。
肝心なのは、バリデーションエラーにならないように工夫することです。

さて、これでコントローラーの記述は完了です。

 
最後に、ビューへかんたん決済のリンクを設置します。

⑤ビューにリンクを設置

app/views/gifts/new.html.erb
<%= link_to '簡単ギフト', easygift_user_path(@reciver), method: :post %>

ボタンを表示させたいビューに、リンクを設置します。
pathは、ターミナルでrails routesコマンドを使って探します。

ターミナル
% rails routes

Prefix           Verb      URI Pattern                           Controller#Action
easygift_user        POST      /users/:id/easygift(.:format)         gifts#easy_gift

pathは、コントローラー名とアクション名と対応しています。
また、URI Patternを見ると、idを引数として渡す必要があると分かります。
これは、ルーティングをusersにネストしているためです。
このアプリでは、idと対応している、@reciverを引数として渡しています。

かんたん決済プレビュー

これで実装完了です。

最後までお付き合いいただきありがとうございました!:smile:

参考資料

Active Record バリデーション - Railsガイド

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
4
Help us understand the problem. What are the problem?