【Rails】PAYJPでトークン生成時のエラー解決

プログラミング学習中で、これはメモしたいと思ったものを、自分ように分かりやすく残しておきます。

# PAYJPを導入して、決済システムを実装したい！
今回もっていきたいゴールはこちらです。
<img width="500" alt="スクリーンショット 2020-09-20 9.45.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676996/596774e8-45e2-9fe9-05cf-f73249e9b966.png">

テスト用のカード情報を入力して、決済が完了している状態です。

# 決済が成功しない...
まずは、`payjp`のgemを導入し、学習した通りにJavaScriptのファイルを作ったり、コントローラーやビューへ記述しました。

そして、テスト用のカード情報を打ち込んで、
<img width="300" alt="スクリーンショット 2020-09-20 10.01.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676996/9a3f2a97-51f3-93b3-6910-906fd112a7ce.png">

購入ボタンを押しますが、
<img width="300" alt="スクリーンショット 2020-09-20 10.14.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676996/7e8bd5e0-cc86-6fc5-a7f3-13d292b633c7.png">

元の画面に戻ってしまう...
（んん？？　トークンが空っぽだと？？？）

これはコントローラーで、決済が成功しなかったら画面が変わらないように設定しており、エラーも表示させるようにしているからですが、最初は原因が分かりませんでした...


現在のコントローラーのコードはこちらです。（読みづらいかと思いますが、長くなるのでcreateアクションの部分のみ抜粋しております。）

```ruby:****_controller.rb
  def create
    @order = ItemOrder.new(order_params)
    if @order.valid?
      pay_item
      @order.save
      return redirect_to root_path
    else
      render 'new'
    end
  end
```

カードの情報を受け取って、トークンを生成するためのJavaScriptの記述はこちらです。

```javascript:****.js

const pay = () => {
  Payjp.setPublicKey(process.env.PAYJP_PUBLIC_KEY);
  const form = document.getElementById("charge-form");
  form.addEventListener("submit", (e) => {
    e.preventDefault();
 
    const formResult = document.getElementById("charge-form");
    const formData = new FormData(formResult);

    const card = {
      card_number: formData.get("card-number"),
      card_cvc: formData.get("card-cvc"),
      card_exp_month: formData.get("card-exp-month"),
      card_exp_year: `20${formData.get("card-exp-year")}`,
    };
    Payjp.createToken(card, (status, response) => {
      if (status == 200) {
        const token = response.id;
        const renderDom = document.getElementById("charge-form");
        const tokenObj = `<input value=${token} type="hidden" name='token'>`;
        renderDom.insertAdjacentHTML("beforeend", tokenObj);
      }
      document.getElementById("card-number").removeAttribute("name");
      document.getElementById("card-cvc").removeAttribute("name");
      document.getElementById("card-exp-month").removeAttribute("name");
      document.getElementById("card-exp-year").removeAttribute("name");
 
      document.getElementById("charge-form").submit();
      document.getElementById("charge-form").reset();
    });
  });
 };
 window.addEventListener("load", pay);
```

# 現状把握
どこが間違っているのかを見るために、`pry-rails`のgemを導入し、createアクションに`binding.pry`を記述して、`params`の中身を確認しました。

```terminal
pry(#<OrdersController>)> params
=> <ActionController::Parameters {"authenticity_token"=>"pvPlrZPKlxtcYotX8kK4N/OjbTuWNkiq5bOCJxqNI+OYt59wXZuTClFnz7XDmAVWelWQJraGSTdIccYspnstlw==", "item_order"=>{"postal_code"=>"555-0000", "prefecture_id"=>"3", "city"=>"市区町村", "block_number"=>"番地", "building_name"=>"建物名", "phone_number"=>"09012345678"}, "controller"=>"orders", "action"=>"create", "id"=>"1"} permitted: false>

```

たしかに、`token`がいない....
（`authenticity_token`は全く別のものだそうです。）

ということは、うまくtokenが生成されていない可能性があります。

次に、JavaScriptへの記述を調べるために,`console.log()`を使用して調べました。

```javascript
const pay = () => {
  Payjp.setPublicKey(process.env.PAYJP_PUBLIC_KEY);
  console.log(process.env.PAYJP_PUBLIC_KEY)
  // 環境変数が定義できているか確認
  const form = document.getElementById("charge-form");
  form.addEventListener("submit", (e) => {
    e.preventDefault();
 
    const formResult = document.getElementById("charge-form");
    const formData = new FormData(formResult);

    const card = {
      card_number: formData.get("card-number"),
      card_cvc: formData.get("card-cvc"),
      card_exp_month: formData.get("card-exp-month"),
      card_exp_year: `20${formData.get("card-exp-year")}`,
    };
    console.log(card)
    // カード情報が受け取れているかの確認
    Payjp.createToken(card, (status, response) => {
      console.log(status)
      // ステータスの数字を確認
      if (status == 200) {
        const token = response.id;
        const renderDom = document.getElementById("charge-form");
        const tokenObj = `<input value=${token} type="hidden" name='token'>`;
        renderDom.insertAdjacentHTML("beforeend", tokenObj);
      }
      document.getElementById("card-number").removeAttribute("name");
      document.getElementById("card-cvc").removeAttribute("name");
      document.getElementById("card-exp-month").removeAttribute("name");
      document.getElementById("card-exp-year").removeAttribute("name");
 
      document.getElementById("charge-form").submit();
      document.getElementById("charge-form").reset();
    });
  });
 };
 window.addEventListener("load", pay);
```

そして、binding_pryで停止しているので、そこでコンソールを確認すると、
<img width="500" alt="スクリーンショット 2020-09-20 10.41.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676996/de14125c-1a32-f982-65aa-025807137e0d.png">
カード情報はしっかり受け取れているみたいです！
しかし、ステータスが`400`なのでトークンが生成できない...

カード情報が受け取れているのに、なんで？？？

# 解決
そこで、知識のある方に相談させて頂き、ようやく解決しました！
取得するcard情報を格納する記述に誤りがあったみたいです。

この部分の記述の、

```javascript:****.js
const card = {
      card_number: formData.get("card-number"),
      card_cvc: formData.get("card-cvc"),
      card_exp_month: formData.get("card-exp-month"),
      card_exp_year: `20${formData.get("card-exp-year")}`,
    };
```

`card_number:`、`card_cvc:`、`card_exp_month:`、`card_exp_year:`の記述をすると、正しくPayjpと通信しないみたいです。

この形は決まっていると教わりまして、
`number:`、`cvc:`、`exp_month:`、`exp_year:`というように記述を直しました。

```javascript
const card = {
      number: formData.get("card-number"),
      cvc: formData.get("card-cvc"),
      exp_month: formData.get("card-exp-month"),
      exp_year: `20${formData.get("card-exp-year")}`,
    };
```

と記述することで、

無事ステータスも、
<img width="500" alt="スクリーンショット 2020-09-20 10.59.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676996/525d8b77-e323-8d0f-ef30-821666646447.png">
`200`になり、

決済も成功させることができました！
<img width="500" alt="スクリーンショット 2020-09-20 11.02.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676996/2e03b84a-4be8-8f16-08c6-f42e610b7f12.png">

# まとめ
`PAYJP`を導入する時は、取得したcard情報を格納する記述を、決まっている形式の記述にする必要がある。
`binding_pry`と`console.log()`を使うことで、どこで不具合が起きているのかを探すことができる。