[Rails]Pay.jpを利用したクレジット決済機能実装　②　~モデルの作成・クレジットカード登録~

## はじめに
例によって、某プログラミングスクールの最終課題である、フリマアプリのクローンサイト作成において、購入機能実装時にPay.jpを利用したので、健忘録としてここに記す。
今回は主にクレジットカード登録機能の実装に取り掛かります。
(お待たせいたしました、、、)

## バージョン情報
ruby '2.5.1'
Rails '5.2.4.2'

## 実装の流れ
1. [実装の準備・APIの導入](https://qiita.com/Shunsuke_i_21/items/19c8326282510ae30eab)
2. <font color="DeepPink">モデルの作成・クレジットカード登録</font>　←　今回の実装内容
3. [クレジットカード詳細表示・削除](https://qiita.com/Shunsuke_i_21/items/75c13102c5db3246a6c6)
4. [クレジットカード購入(決済)機能](https://qiita.com/Shunsuke_i_21/items/8745c245205fa182f974)

## 前提条件として
* 記載はhaml記法で
 * [私の参考にしたrailsにhamlを導入するやり方はこちら、、](https://qiita.com/MitsuguSueyoshi/items/f1b0b047553fde49bbc1)
* ユーザー登録機能として、gemの``devise``を利用しています。 なので``current_user``などの``devise``のメソッドが随所に出てきます
 * 参考記事: [【Rails】deviseを導入してみる](https://qiita.com/Hal_mai/items/350c400e8763ce0487a3) 

## 今回の実装機能のダイジェスト
### クレジットカード登録の様子
[![Image from Gyazo](https://i.gyazo.com/a81c992fd203e59794f163399a100bdd.gif)](https://gyazo.com/a81c992fd203e59794f163399a100bdd)

1. まず、新規登録画面で特定のカード情報が入力される
2. 入力された情報は、トークン化処理(第３者にカード情報が漏れないように暗号化)する。(jQueryの``payjp.js``で実装)
3. トークン化された情報は、自アプリの任意の自テーブルで登録される
4. 登録が完了したら(今回の例では)createビューが表示される
5. もし、登録途中でエラーがあれば、その旨のflashメッセージが表示される。

## <font color="red">  *注意！*</font>
**クレジットカード登録のテストの際には以下のサイトのテスト用のクレジット番号を必ずお使いください！**
[テストカード](https://pay.jp/docs/testcard)
**有効期限は現在以降の年と月であればなんでもOK**
**CVC(セキュリティコード)はなんでもOK！**
**<font color="red">間違ってもご自身のカード番号は使わないように</font>**

<font color="blue">前置きはこれくらいで、いよいよ実装！</font>

## **いよいよ実装！** まずはモデルの作成
(こちらは主旨に若干外れてしまいますので、さらっと行きます。参考程度でお願いします)
今回は以下のテーブルを作成しました。
### credit_cardsテーブル
|Column|Type|Options|
|------|----|-------|
|user_id|references|null: false,foreign_key: true|
|customer_id|string|null: false|
|card_id|string|null: false|

### credit_cardsマイグレーションファイル
```ruby:db/migrate/###_create_credit_cards.rb
class CreateCreditCards < ActiveRecord::Migration[5.2]
  def change
    create_table :credit_cards do |t|
      t.references    :user,        null: false, foreign_key: true
      t.string       :customer_id,  null: false
      t.string       :card_id,      null: false
      t.timestamps 
    end
  end
end
```
こちらを``rails db:migrate``

```:ターミナル
$ rails db:migrate
```
(もしDBのテーブル作成に不安があれば、[(私が参考にした)こちらのサイト](https://qiita.com/daisuke19840125/items/b052292c51eb7e537231)をご覧ください)

**ちなみにカード情報そのものを保存することは禁止されています。**
[カード情報非通過化対応のお願い](http://payjp-announce.hatenablog.com/entry/2017/11/10/182738)

### アソシエーションはこんな感じ
```ruby:models/credit_card.rb
class CreditCard < ApplicationRecord
  belongs_to :user
end
```
(今回は1人１枚のみクレジットカード登録できるように設定(``has_one``で))

```ruby:models/user.rb
# 今回の実装に不要なアソシエーションは省略
class User < ApplicationRecord
  has_one :credit_card, dependent: :destroy
end
```

## コントローラーを作る！
今回は(credit_cardsモデルもあることだし)credit_cardsコントローラーを作って、そちらにアクションを記載していきます。

```:ターミナル
$ rails g controller Credit_cards
```

今回はクレジットカード新規登録ということで、先に以下のようにコントローラーに``newアクション``と``createアクション``を追記

(newアクション後のトークン作成処理をまだ行なっていないため、``createアクション``の内容が分かりにくいかもしれませんが、分からなければ一旦置いておいてOK！)

```ruby:controllers/credit_cards_controller.rb
class CreditCardsController < ApplicationController
  require "payjp" #PAYJPとやり取りするために、payjpをロード

  def new
    # 後ほど、showアクション(登録クレジットカード詳細表示機能)実装時に追記します。
  end

  def create
    # 前回credentials.yml.encに記載したAPI秘密鍵を呼び出します。
    Payjp.api_key = Rails.application.credentials.dig(:payjp, :PAYJP_SECRET_KEY)

    # 後ほどトークン作成処理を行いますが、そちらの完了の有無でフラッシュメッセージを表示させます。
    if params["payjp_token"].blank?
      redirect_to action: "new", alert: "クレジットカードを登録できませんでした。"
    else
    # 無事トークン作成された場合のアクション(こっちが本命のアクション)
    # まずは生成したトークンから、顧客情報と紐付け、PAY.JP管理サイトに登録
      customer = Payjp::Customer.create(
        email: current_user.email,
        card: params["payjp_token"],
        metadata: {user_id: current_user.id} #最悪なくてもOK！
      )
      # 今度はトークン化した情報を自アプリのCredit_cardsテーブルに登録！
      @card = CreditCard.new(user_id: current_user.id, customer_id: customer.id, card_id: customer.default_card)
      # 無事、トークン作成とともにcredit_cardsテーブルに登録された場合、createビューが表示されるように条件分岐
      if @card.save
        #もしcreateビューを作成しない場合はredirect_toなどで表示ビューを指定
      else
        redirect_to action: "create"
      end
    end
  end

end
```
しれっと記載してますが、フラッシュメッセージ表示機能も実装しています。
(なので、フラッシュメッセージ機能実装しないとうまく動かないかも)

参考にした記事: [【Rails】flashメッセージを使用して簡易メッセージを表示させる詳しい方法と解説](https://qiita.com/dice9494/items/2a0e92aba58a516e42e9)

### ついでにルーティングも設定しておく
```ruby:config/routes.rb
Rails.application.routes.draw do

# したの方に追記
  resources :credit_cards, only: [:new, :create] do
  end
end
```

<font color="blue">コントローラーの記載、ルーティングは完了！</font>

## ビューを記載(基本的にレイアウトなどはお好みで、、)
### ビューの表示例
[![Image from Gyazo](https://i.gyazo.com/f2b0bc0f9f4478399f328a79e6aaa656.png)](https://gyazo.com/f2b0bc0f9f4478399f328a79e6aaa656)

### ``newアクション`` のviewを記載
ちなみに部分テンプレートを使用してます(ファイル名を参照！)。
基本的にはこの次の、jQueryの``payjp.js``記述時の**id名**との照らし合わせ用として、ご確認ください。

```haml:views/credit_cards/new/_new.html.haml
.card_add 
  .title クレジットカード情報入力

  -#form_tagメソッドでcreateアクションに入力情報を渡します。
  = form_tag(credit_cards_path, method: :post, id: "charge-form", class: "details", name: "inputForm") do
    .number-details
      .card-number カード番号
      %span.must_check 必須
    .number
      = text_field_tag 'payment_card_no', "", class: 'cardnumber', id: "payment_card_no", placeholder: '半角数字のみ', type: "text", maxlength: "16"
    -# 事前にassets/imagesにcardsファイルを作成、中に各カード会社の画像を入れておき、呼び出す。
    .image
      = image_tag(image_path('cards/visa.png'), class: 'visa')
      = image_tag(image_path('cards/master.png'), class: 'master')
      = image_tag(image_path('cards/jcb.png'), class: 'jcb')
      = image_tag(image_path('cards/amex.png'), class: 'amex')
      = image_tag(image_path('cards/diners.png'), class: 'diners')
      = image_tag(image_path('cards/discover.png'), class: 'discover')
      
    .expirationdate
      .expirationdate__details
        .date 有効期限
        %span.must_check 必須
      .expirationdate__choice
        .month
          %select#payment_card_month.card-default{name: "payment_card_month", type: "text"}
            %option{value: "1"} 01
            %option{value: "2"} 02
            %option{value: "3"} 03
            %option{value: "4"} 04
            %option{value: "5"} 05
            %option{value: "6"} 06
            %option{value: "7"} 07
            %option{value: "8"} 08
            %option{value: "9"} 09
            %option{value: "10"} 10
            %option{value: "11"} 11
            %option{value: "12"} 12
          .month-details 月
        .year
          %select#payment_card_year.card-default{name: "payment_card_year", type: "text"}
            %option{value: "2020"} 20
            %option{value: "2021"} 21
            %option{value: "2022"} 22
            %option{value: "2023"} 23
            %option{value: "2024"} 24
            %option{value: "2025"} 25
            %option{value: "2026"} 26
            %option{value: "2027"} 27
            %option{value: "2028"} 28
            %option{value: "2029"} 29
            %option{value: "2030"} 30
          .year-details 年
        
    .securitycode
      .securitycode__details
        .securitycode-details__title セキュリティコード
        %span.must_check 必須
      .securitycode__cardsecurity
        = text_field_tag  "payment_card_cvc", "", type: 'text', class: 'securitycode__cardsecurity__form', id: "payment_card_cvc", maxlength: "4", placeholder: 'カード背面4桁もしくは3桁の番号'
      .card-question カード裏面の番号とは？
    .add 
      = submit_tag "登録する", class: "add__btn", id: "payment_card_submit-button"
```
scssは省略で、、、

## 一番重要！　トークン化処理のための``payjp.js``記載
実装は提供されている
[クレジットカード情報をトークン化するpayjp.jsの使い方](https://payjp.hatenablog.com/entry/2017/12/05/134933)
を参考に
~~ですが正直言ってだいぶ分かりにくいです、、、~~

ちなみにjQueryを利用してますので、未設定の場合はこちらを参考に導入お願いします！
参考にさせていただいた記事: [RailsでjQueryを使えるようにする方法](https://qiita.com/ngron/items/95846bd630a723e00038)

```javascript:assets/javascripts/payjp.js
window.addEventListener('DOMContentLoaded', function(){

  //id名が"payment_card_submit-button"というボタンが押されたら取得
  let submit = document.getElementById("payment_card_submit-button");

  Payjp.setPublicKey('pk_test_###'); //公開鍵の記述(ご自身の公開鍵コードを記述しよう！)

    submit.addEventListener('click', function(e){ //ボタンが押されたらトークン作成開始。

    e.preventDefault(); //ボタンを1度無効化

    let card = { //入力されたカード情報を取得(id名の記載ミスに注意！)
        number: document.getElementById("payment_card_no").value,
        cvc: document.getElementById("payment_card_cvc").value,
        exp_month: document.getElementById("payment_card_month").value,
        exp_year: document.getElementById("payment_card_year").value
    };

    Payjp.createToken(card, function(status, response) {  // トークンを生成
      if (status === 200) { //成功した場合(status === 200はリクエストが成功している状況です。)
        //データを自サーバにpostしないようにremoveAttr("name")で削除
        $(".number").removeAttr("name");
        $(".cvc").removeAttr("name");
        $(".exp_month").removeAttr("name");
        $(".exp_year").removeAttr("name"); 
        $("#charge-form").append(
          $('<input type="hidden" name="payjp_token">').val(response.id)
        ); //取得したトークンを送信できる状態にします
        document.inputForm.submit();
        alert("登録が完了しました"); //正常処理完了確認用。createビューがあればつけなくてもOKかな
      } else {
        alert("カード情報が正しくありません。"); //エラー確認用
      }
    });
  });
});
```

ちなみにこちらの実装が一番難しいと思うので、複数のサイトを比較して実装を進めることをおすすめします。

参考にさせていただいた記事:
[Railsフリマアプリ　payjpを使ってクレジットカードを登録・削除](https://qiita.com/oitakazuki/items/8382a4aabffde9c18cf7)
[Pay.jpをRailsで！【登録・削除・購入】](https://qiita.com/Masataka_Sugi/items/57b68eb19de0c014ef12)
[Payjpでクレジットカード登録と削除機能を実装する(Rails)](https://qiita.com/takachan_coding/items/f7e70794b9ca03b559dd)
[[HowTo]Pay.jpを用いたクレジットカードの登録機能実装について/カスタムフォーム版
](https://qiita.com/Tatsu88/items/23fe4b83d0ff8d78709d)


**これにより、クレジットカード新規登録、登録内容入力(``newメソッド``)、入力内容をトークン化(``payjp.js``での処理)、トークン化された入力内容がuser_idと紐づいてcredit_cardsテーブルに登録される(``createメソッド``)処理が実装されました！**

## おまけ　createビューの記述
最後に、問題なく登録が完了された場合のcreateビューを記述して完成です。
(特にこだわり等なければ、``redirect_to``等でマイページやトップページに遷移させても問題ないです。)
### createビューの表示例
[![Image from Gyazo](https://i.gyazo.com/01124ffc78d3dac33073155def9d134d.png)](https://gyazo.com/01124ffc78d3dac33073155def9d134d)

### ``createアクション``のviewを記載
(例によって部分テンプレートを使っています。)

```haml:views/credit_cards/create/_create.html.haml
.create
  .create__frame
    .create__frame__message
      登録が完了しました。
    %ul.create__frame__btns
      -# マイページに戻るパスを指定
      %li.my_page_link
        = link_to "マイページに戻る", users_path(current_user.id), class: "my_page_link__btn"
      -# のちに実装する、登録クレジットカード詳細表示(showアクション)に行くパスを指定
      %li.credit_cards_index_link
        = link_to "登録カード一覧", credit_card_path(current_user.id), class: "credit_cards_index_link__btn"
```

###<font color="blue">クレジットカード新規登録機能実装　完了！！</font>

## 最後に
以上でクレジットカードの新規登録機能の実装は完成です！
さらに発展させると、ユーザー登録時にクレジットカード登録機能を差し込むこともできそうですね。

あと、何度も言いますが、**登録テスト時には必ず、[テストカード](https://pay.jp/docs/testcard)の番号での登録**をお願いしますね！

次回は、「クレジットカード編集・削除　機能」の実装を行います。

＞[クレジットカード編集・削除　機能](https://qiita.com/Shunsuke_i_21/items/75c13102c5db3246a6c6)

## 参考リンク
* 公式サイト
    * [公式リファレンス](https://pay.jp/docs/payjs)
    * [公式ガイド](https://pay.jp/docs/api/?ruby#payjp-api)
* 参考にさせて頂いたQiita記事
    * [【Ruby on Rails で簡単！】PAY.JPを利用したクレジットカード決済の導入](https://qiita.com/mylevel/items/7462ca28260581509d19)
    * [【Rails】payjpを使用した決済機能を実装する①〜クレジットカードの登録編〜](https://qiita.com/dice9494/items/4aa04da1056d1f15919e)
* 主に``payjp.js``(トークン化処理)の記述で参考にしたサイト
    * [Railsフリマアプリ　payjpを使ってクレジットカードを登録・削除](https://qiita.com/oitakazuki/items/8382a4aabffde9c18cf7)
    * [Pay.jpをRailsで！【登録・削除・購入】](https://qiita.com/Masataka_Sugi/items/57b68eb19de0c014ef12)
    * [Payjpでクレジットカード登録と削除機能を実装する(Rails)](https://qiita.com/takachan_coding/items/f7e70794b9ca03b559dd)
    * [[HowTo]Pay.jpを用いたクレジットカードの登録機能実装について/カスタムフォーム版](https://qiita.com/Tatsu88/items/23fe4b83d0ff8d78709d)
* その他の機能実装時の参考サイト
    * [Railsでhamlを導入する！](https://qiita.com/MitsuguSueyoshi/items/f1b0b047553fde49bbc1)
    * [【Rails】deviseを導入してみる](https://qiita.com/Hal_mai/items/350c400e8763ce0487a3) 
    * [railsでのモデル作成](https://qiita.com/daisuke19840125/items/b052292c51eb7e537231)
    * [【Rails】flashメッセージを使用して簡易メッセージを表示させる詳しい方法と解説](https://qiita.com/dice9494/items/2a0e92aba58a516e42e9)
    * [RailsでjQueryを使えるようにする方法](https://qiita.com/ngron/items/95846bd630a723e00038)