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

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

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

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

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

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


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

参考になれば幸いです。
前提
---
__注意:この記事ではクレジット決済は実装しません！__


開発環境
・ruby 2.6.5
・Rails 6.0.3.3

完成イメージ
---

<img width="500px" alt="かんたん決済プレビュー" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516687/4101e1b2-0922-200b-f040-81ed05c3278f.gif">


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

ER図
---


![244b268980cfe94a12803201742cdfb4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516687/ba0df6f9-94ab-3c26-bb1b-716ce498dc2e.png)

ギフトに必要な２つのテーブルです
・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テーブルには、`nickname`と`email`のカラムを用意しています。

__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`という３つのカラムを用意しています。

それぞれ、
`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`は、２行目の為の記述です。

２行目ですが、一つずつ順番に説明していきます。
`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`を参照してね！」という意味合いです。
やっていることは２行目と同じです。

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


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

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

```.rb
  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テーブルを中間テーブルとするアソシエーションが、
２パターン記述されている`ことが分かります。

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

```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](https://github.com/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'`を渡しています。
肝心なのは、バリデーションエラーにならないように工夫することです。

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

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

⑤ビューにリンクを設置
---



```erb: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`を引数として渡しています。



<img width="500px" alt="かんたん決済プレビュー" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516687/4101e1b2-0922-200b-f040-81ed05c3278f.gif">

これで実装完了です。

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

参考資料
---
・[Active Record バリデーション - Railsガイド](https://railsguides.jp/active_record_validations.html)