payjpを利用したフリマアプリの作成1(クレジットカードの登録)

今回はチーム開発にてフリマアプリを作成したため、一部抜粋して投稿致します。
本件は初投稿であり、少しでも他者の理解を深めること、また自身のアウトプットの場として利用できればと作成致しましたので、ミス等理解の相違がある場合はご指摘頂けると幸いです！

それでは始めていきましょう⏩　LET'S　GO!!!
---
今回はクレジットカード情報の登録及び決済を行うために必要な開発者向けのサービスとして『PAY.JP(ペイジェーピー)』といった決済機能を持ち合わせたサービスを紹介していきます。

まずはこちらを活用していきましょう！

#①新規登録
https://pay.jp/
　　⬅︎こちらのリンクへ飛び、新規登録を行います。
<img width="1167" alt="スクリーンショット 2020-07-28 20.03.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682254/0fd726f3-74f0-59b0-53d0-2788d5a97ed3.png">






下部添付写真では１０百万円以上の売上を計上してますが、登録しただけでは売上や顧客データは存在しません。
商品購入機能テストした際の仮想データであるため、実質1円も計上されていません。


<img width="1134" alt="スクリーンショット 2020-07-28 20.06.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682254/8ba59d53-bc12-0a2b-0d01-605cddaa9f56.png">


#②登録が完了したら
Gemfile

```ruby:Gemfile
#最下部に記載
gem 'payjp'
```



ターミナルでbundle install　します。

#③開発アプリの中にpay.jpを読み込む記述を行いましょう！
　payjp.jsを使うにはhttps://js.pay.jpを直接読み込む必要があります。


```view/layouts/application.html.haml
  %head
    %meta{content: "text/html; charset=UTF-8", "http-equiv": "Content-Type"}/
    %title FleaMarket72c
    %script{src: "https://js.pay.jp/", type: "text/javascript"}
　　　　　　　　　　　　　　　⬆️こちらの一文を追記
    = csrf_meta_tags
    = csp_meta_tag
    = stylesheet_link_tag    'application', media: 'all'
    = javascript_include_tag 'application'
```

head内に決められたコード追記し、ページを読み込むことでトークン取得などのメソッドを使用できるようになります。


#④環境変数を設定しましょう！

ターミナル上で
`EDITOR="vi" bin/rails credentials:edit`を入力すると開発アプリの環境変数を設定できます。
こちらに記載するのは
`PAYJP_SECRET_KEY: "sk_test_◯◯◯◯◯◯◯◯◯"`のみです
sk_ から始まるものはサーバーサイドで用いるシークレットキーになるので、決して公開しないように注意しましょう。

<img width="1022" alt="スクリーンショット 2020-07-28 20.22.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682254/0d2b453f-07b1-171f-cdd9-ba7c56b56b86.png">

画像左下にAPIとあるので、ここをクリックすると各APIキーの情報が記載されています。
テスト鍵と本番鍵がありますが、開発中はテスト鍵のみを利用し、環境変数の設定に秘密鍵
PAYJP_SECRET_KEY: "sk_test_◯◯◯◯◯◯◯◯◯"と記述するようにしましょう。
※環境変数の設定は、環境により異なるのでご注意を！

第２STEP　⏩
---

READMEでは

```ruby:README.md
|user_id|references|null: false, foreign_key:true|
|customer_id|string|null: false|
|card_id|string|null: false|
```


このような形で設計。

#①マイグレーションファイルを作成

```ruby:20200709111341_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
```


『pay.jp』ではカード情報をpay.jp側に転送することで、自動的にcustomer_id,card_idを生成してくれます。
そのためカードの入力情報である「card_number（カード番号）」「exp_month（有効期限（月））」「exp_year（有効期限（年））」「card_form（セキュリティーコード）」等のカラムは不要であり、SQL（データベース上）でもカラムはuser_id,customer_id,card_idのみで問題ありません。
上記４つのカード入力情報はpay.jp側で保存されており、商品購入時に都度idと紐づいたクレジットカードを引っ張り出して決済手続きを行います。

しかし顧客idとカードidを取得するには、前提として顧客の”クレジットカード入力情報”をpay.jp側に上手に飛ばす必要があります。
私はここでつまづき、中々前に進むことができませんでしたので、しっかり押さえておきましょう！！！

#②routes.rbの設定

```ruby:routes.rb
  resources :credit_cards, except: :index do
    collection do                  #id無
      get 'regist_done'            #登録済
      get 'delete_done'            #削除済
    end
    member do                      #id有
      get 'buy'
      post 'pay'
    end
  end
```

上記コードについて説明すると、except: [:index, :edit, :update] doと記載があるので７つのアクションの中で３つ以外のアクションに対応させることを意味します。
「new」      ・・・クレジットカード登録画面
「create」   ・・・クレジットカードの情報保存
「destroy」  ・・・クレジットカード削除画面
「show」     ・・・クレジットカードの詳細画面

ここではその他のアクションとして
「buy」　　　　　　・・・　商品購入画面
「pay」　　　　　　・・・　購入手続完了画面
「regist_done」　・・・　クレジットカード登録完了画面
「delete_done」　・・・　クレジットカード削除画面
を設定。

##〜予備知識〜
**※collectionとmemberについて**
7つの基本アクション以外でルーティングを定義する際、collectionかmemberを利用します。
collectionはルーティングに:idがつかず、memberは:idがつきます。
上記のbuy,payでは商品購入と支払完了時の定義であるため、商品を持ち合わせている必要があります。
よってitem.idを保有できるようにmemberを利用。


#③クレジットカード登録画面

<img width="539" alt="スクリーンショット 2020-07-29 18.58.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682254/934ad9c5-e906-b249-d5a1-0bf0bca3d2fc.png">


```ruby:views/credit_cards/new.html.haml
 %section.credit-card-content
    %h2.credit-card-content--head
      クレジットカード情報入力
    .credit-card-content--form
      =form_with model: @card, id: "all-form", url: credit_cards_path,local: true do |f| 
        .form-group
          .form-group--card-number
            %label.card-number
              カード番号
            %span.required
              必須
            .card-number__form
              = f.text_field :card_number, placeholder: "半角数字のみ",maxlength: "16", id: "number"
            .card-icon
              %ul
                %li
                  = image_tag "/logo_visa_ph001.png", size: "40x30", class: 'credit_logo'
                  = image_tag "/mc_vrt_pos.svg", size: "40x30", class: 'credit_logo'
                  = image_tag "/jcb-logomark-img-01.gif", size: "40x30", class: 'credit_logo'
                  = image_tag "/amex-logomark-img-04.gif", size: "40x30", class: 'credit_logo'
                  = image_tag "/diners-logomark-img-01.gif", size: "40x30", class: 'credit_logo'
          .form-group--expiration-date
            %label.expiration-date
              有効期限
            .expiration-date__form
              = f.select :exp_month, [["01", 1],["02", 2],["03", 3],["04",4],["05",5],["06",6],["07",7],["08",8],["09",9],["10",10],["11",11],["12",12]], id: "exp_month"
              .expiration-text 月
              = f.select :exp_year, [["2020", 2020],["2021", 2021],["2022", 2022],["2023",2023],["2024",2024],["2025",2025],["2026",2026],["2027",2027],["2028",2028],["2029",2029],["2030",2030]], id: "exp_year"
              .expiration-text 年
          .form-group--security-code
            %label.security-code
              セキュリティーコード
            .security-code--form
              = f.text_field :security_code, placeholder: "カード背面４桁もしくは３桁の番号", maxlength: "4", id: "cvc" 
          =f.submit "追加する", class: "submit-btn", id: "charge_form"
```


**form_with(Rails5.1以上で推奨されているフォームタグ、簡単にフォーム作成するためのヘルパー)**を用いてフォームを作成し、「card_number（カード番号）」「exp_month（有効期限（月））」「exp_year（有効期限（年））」「cvc（セキュリティーコード）」情報をpay.jpに送信できるようにそれぞれに対してidを付与します。
こうする事でpayjp.jsを作成した際に入力されたカード情報を取得できるようlet card = {}と変数でまとめるとpayjp側に情報を送信しやすくなるので準備していきましょう！

第３STEP　⏩
---
#①Jqueryを利用したJavaScriptを見ていきましょう！！！

```javascript:payjp.js
$(function() {
  Payjp.setPublicKey('pk_test_○○◯◯◯◯◯◯◯◯◯◯◯◯◯◯');
  $("#charge_form").on('click', function(e){
    e.preventDefault(); //「追加する」のクリックイベントが行われた時に動作を一度停止
    var year = $("#exp_year").val()
    var month = $("#exp_month").val()
    exp_year = String(year);
    exp_month= String(month);
    let card = { //カード登録に入力された情報をハッシュで格納
      number: $("#number").val(),
      cvc: $("#cvc").val(), 
      exp_month: exp_month,
      exp_year: exp_year
    };
    //PAY.JPのサーバーと通信し、カード情報を送信することで認証を行い、カードトークンをresponse.idで受けとる。
    Payjp.createToken(card, function(status, response){
      if (status === 200) {  //サーバーとのやり取りが200=正常に行えた場合
        $("#all-form").append(
          $('<input type="hidden" name="payjp-token">').val(response.id)
        );
        $('#all-form').submit();  //all-formはform_withのidであり、createアクションにparamsを送信します。
      } else {
        alert("カード情報が正しくありません。");
      }
    });
  });
});
```
##〜「追加する」ボタンを押した時の動作確認〜
1.クレジットカード登録画面でカードを追加するボタンを押す
2.ボタンを押すとjqueryでイベント発火。ブラウザのデフォルト（createアクションへの遷移）を停止。
3.jqueryで公開鍵を利用してpayjpと繋がり、入力したカードの情報をpayjpに送信してカードトークンを作らせる（response.id）
4.カードトークンをparamsとしてcreateアクションに送る
5.createアクションで秘密鍵を使ってpayjpと繋がり、カードトークンをpayjpに送ってcustomerを作らせる
6.customerの情報やカードトークンの情報をDBに保存する

```js:
 Payjp.createToken(card, function(status, response){
      if (status === 200) {  //サーバーとのやり取りが200=正常に行えた場合
        $("#all-form").append(
          $('<input type="hidden" name="payjp-token">').val(response.id)
        );
        $('#all-form').submit();  //all-formはform_withのidであり、createアクションにparamsを送信します。
      } else {
        alert("カード情報が正しくありません。");
      }
    });
  });
```
##⬆︎トークン作成について⬆︎
・引数１にstatusでサーバーとやり取りが上手く行えるか
・引数２にカードトークンを受け取るためのresponseを設定。
・条件分岐で、200というpayjpサーバーとのやり取りが上手くいった数値の時、隠しフォームでresponse.id（カードトークン＝作成されたcustomer_idとcard_id）を取得
・appendメソッドでhtml要素を動的にcreateアクションに追加。
・サーバーが正常に行われなかった場合には、アラートを出力される。
　

#②次にコントローラーを見ていきましょう！

```ruby:credit_cards.controller.rb
class CreditCardsController < ApplicationController
  before_action :move_to_root
  before_action :set_card, only: [:new, :show, :destroy, :buy, :pay ]
  
  require "payjp"

  def new
    if @card.present?  #カード情報が登録されている場合
      redirect_to credit_card_path(current_user.id)  #showアクションへ
    else
      card = CreditCard.where(user_id: current_user.id)
    end
  end

  def create
    #まず秘密鍵を取得し、payjpと照合
    Payjp.api_key = Rails.application.credentials[:PAYJP_SECRET_KEY]
    if params['payjp-token'].blank?
      redirect_to action: :new
    else
      # トークン発行後、payjp上で顧客データを生成(カードトークンを生成してもそれを紐付ける顧客が必要であるため)
      customer = Payjp::Customer.create(
        card: params['payjp-token'],  #newアクション後のJQueryで取得したトークンを顧客に紐付け
        metadata: {user_id: current_user.id},
        description: 'test'
      )
      #railsのDB上にもカード情報とそれに紐づく顧客情報を保存
      @card = CreditCard.new(user_id: current_user.id, customer_id: customer.id, card_id: customer.default_card)
      if @card.save  #保存できたらカード登録完了ページへ遷移
        redirect_to regist_done_credit_cards_path
      else
        redirect_to action: :new  #保存できなければカード登録ページへ遷移
      end
    end
  end

  private
  def move_to_root  #ログインしていなければ、トップ画面に遷移
    redirect_to root_path unless user_signed_in?
  end

  def set_card   #各アクション内でuser_idとデータベースに保存れたcard情報を紐付けておく
    @card = CreditCard.find_by(user_id: current_user.id)
  end
```
　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　以　　上
<img width="744" alt="スクリーンショット 2020-08-04 19.49.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682254/c524f59c-8fac-8adf-64f4-e7966f94dcfe.png">
<img width="730" alt="スクリーンショット 2020-08-04 19.50.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682254/6f813cb5-2cdb-c1e5-8d24-05a88921c97f.png">


##これでクレジットカードの登録と保存が完了
次の記事では[『商品を購入した際の決済機能』](https://qiita.com/datti/items/55101bd233767e114682)について見ていきましょう！！