エンジニア転職日記

エンジニア転職に向けての日記です

How to PAY.JP

概要

devise、Herokuに引き続き、フリマアプリ作成過程で扱った技術についてアウトプットします。今回はPAY.JPです。

 

shangang7321.hatenablog.com

 

 

shangang7321.hatenablog.com

 

 

PAY.JPとは

PAY.JPはクレジット機能を実装できるAPIです。クレジット機能の実装はカード番号などの個人情報の取り扱いもあるため、既存のAPIを利用する事が望ましいです。

PAY.JPを利用した場合の処理の流れは以下のようになっています。

 

①クライアント側でJavaScriptを用いてPAY.JPにカード情報をリクエス

②PAY.JPはそれに対しレスポンスを返し、JavaScriptトークンを生成(公開鍵)

JavaScriptからサーバー側へトークンと決済情報をパラメーターとして送信

④サーバー側はトークンと決済情報をもとにPAY.JPと通信し、決済を行う(秘密鍵

 

公開鍵、秘密鍵はPAY.JPのAPI情報のところから入手できます。

なお、PAY.JPの利用にはアカウント登録が必要です。

pay.jp

使い方

ここでは、クレジット決済の練習用アプリを作成します。

事前準備

まずはアプリを立ち上げます

% cd ~/projects
% rails _6.0.0_ new payjp_practice -d mysql
% cd payjp_practice
% rails db:create

gemをインストールします。

1
2
# Gemfile 最下
gem 'payjp'
% bundle install

 

ルーティングの設定をします。今回作るアプリは、TOPページで金額とカード情報を入力して決済を実行するだけなので、indexとcreateのみルート設定します。

Rails.application.routes.draw do
  root to: 'orders#index'
  resources :orders, only:[:create]
end

 

コントローラーを作成します。

% rails g controller orders

 

class OrdersController < ApplicationController

  def index
  end

  def create
  end

end

 

HTML&CSS

<%= form_with  url: orders_path, id: 'charge-form', class: 'card-form',local: true do |f| %>
  <div class='form-wrap'>
    <%= f.label :price, "金額" %>
    <%= f.text_field :price, class:"price", placeholder:"例)2000", maxlength:"16" %>
  </div>
  <div class='form-wrap'>
    <%= f.label :number,  "カード番号" %>
    <%= f.text_field :number, class:"number", placeholder:"カード番号(半角英数字)", maxlength:"16" %>
  </div>
  <div class='form-wrap'>
    <%= f.label :cvc ,"CVC" %>
    <%= f.text_field :cvc, class:"cvc", placeholder:"カード背面4桁もしくは3桁の番号", maxlength:"16" %>
  </div>
  <div class='form-wrap'>
    <p>有効期限</p>
    <div class='input-expiration-date-wrap'>
      <%= f.text_field :exp_month, class:"exp_month", placeholder:"例)3" %>
      <p></p>
      <%= f.text_field :exp_year, class:"exp_month", placeholder:"例)24" %>
      <p></p>
    </div>
  </div>
  <%= f.submit "購入" ,class:"button" %>
<% end %>

 

.card-form{
  width: 500px;
}

.form-wrap{
  display: flex;
  flex-direction: column;
}

.exp_month{
  resize:none;
}

.exp_year{
  resize:none;
}

.input-expiration-date-wrap{
  display: flex;
}


.button{
  margin-top: 30px;
  height: 30px;
  width: 100px;
}

 

決済金額と日時の情報を記録するorderモデルを作ります。

% rails g model order

マイグレーションファイルを編集します。

class CreateOrders < ActiveRecord::Migration[6.0]
  def change
    create_table :orders do |t|
      t.integer :price  ,null: false

      t.timestamps
    end
  end
end

データベースに反映させます。

% rails db:migrate

orderモデルにバリデーションをかけます。

class Order < ApplicationRecord
   with_options presence: true do
       validates :price
   end
end

 

クレジット決済処理実装

JavaScriptの記述

payjp.jsを利用できるようにします。payjp.jsカードのトークン化を可能にするJavaScriptのライブラリです。

 

まずは、payjp.jsを読み込めるようにします。

# app/views/layouts/application.html.erb
<%= csrf_meta_tags %> <%= csp_meta_tag %> <script type="text/javascript" src="https://js.pay.jp/v1/"></script> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

 

トークン化するJavaScriptファイルを呼び出せるように記述します。

// app/javascript/packs/application.js

require
("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") require("../card");

 

app/javascriptにcard.jsを作成します。そして以下のように記述します。

const pay = () => {
  Payjp.setPublicKey("pk_test_〇〇〇〇〇〇"); // PAY.JPテスト公開鍵
  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 = {
      number: formData.get("number"),
      cvc: formData.get("cvc"),
      exp_month: formData.get("exp_month"),
      exp_year: `20${formData.get("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("number").removeAttribute("name");
        document.getElementById("cvc").removeAttribute("name");
        document.getElementById("exp_month").removeAttribute("name");
        document.getElementById("exp_year").removeAttribute("name");

        document.getElementById("charge-form").submit();
        document.getElementById("charge-form").reset();
      } else {
      }
    });
  });
};

window.addEventListener("load", pay);

処理の流れは以下のようになっています。

Payjp.setPublicKey("pk_test_〇〇〇〇〇〇"); // PAY.JPテスト公開鍵

PAY.JPを読み込むために公開鍵をセットしています。

const form = document.getElementById("charge-form");
form.addEventListener("submit", (e) => {

charge-form内のsubmitが実行されるとイベントが発火します。

e.preventDefault();

preventDefault();メソッドを用いてRailsのフォーム送信をキャンセルしています。

Railsのフォームを使ってサーバーサイドへ情報を送るのではなく、Javascriptでサーバーサイドへ値を送るためです。

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

カード情報を変数に代入します。

const token = response.id;
const renderDom = document.getElementById("charge-form");
const tokenObj = `<input value=${token} type="hidden" name='token'>`;
renderDom.insertAdjacentHTML("beforeend", tokenObj);

変数に代入された情報が正常であれば、生成されたトークンを変数に代入します(19行目)。

トークンをパラメータとして送るために、form内にhidden属性としてトークンが入っているHTMLを作成しています。(21行目)

20行目で生成するHTML要素を取得、21行目でHTMLの定義、22行目でformの最後に追加する処理を行っています。

document.getElementById("number").removeAttribute("name");
document.getElementById("cvc").removeAttribute("name");
document.getElementById("exp_month").removeAttribute("name");
document.getElementById("exp_year").removeAttribute("name");

カード情報そのものはパラメータとして送らないため、JavaScript内で削除します。

document.getElementById("charge-form").submit();

最後に、formの情報をサーバーサイドへ送信します。

 

サーバーサイドの記述

ストロングパラメータを設定します。

class OrdersController < ApplicationController

  def index
  end

  def create
  end

  private

  def order_params
    params.permit(:price, :token)
  end

end

 

// card.js
const
tokenObj = `<input value=${token} type="hidden" name='token'>`;

ここでトークンのname属性をtokenにしているので、パラメータでは:tokenとして受け取れます。

 

受け取った値をもとに、インスタンスを生成します。

class OrdersController < ApplicationController

 def index
 end

 def create
   @order = Order.new(price: order_params[:price])
 end

 private

 def order_params
   params.permit(:price, :token)
 end

end

 

購入の処理には、Chargeオブジェクトを利用します。Chargeオブジェクトは、PAY.JPで用意されている、支払情報を生成するオブジェクトです。PAY.JP::Charge.createで生成できます。

 

class OrdersController < ApplicationController

 def index
 end

 def create
   @order = Order.new(price: order_params[:price])
 end

 private

 def order_params
   params.permit(:price, :token)
 end

 def pay_item
   Payjp.api_key = "sk_test_〇〇〇〇〇〇"  # PAY.JPテスト秘密鍵
   Payjp::Charge.create(
     amount: order_params[:price],  # 商品の値段
     card: order_params[:token],    # カードトーク
     currency:'jpy'                 # 通貨の種類
   )
 end

end

PAY.JP利用のため、秘密鍵を読み込みます。その後、Chargeオブジェクトを利用して支払情報を生成しています。

 

最後に、金額と決済日時をorderテーブルに保存する処理を記述し、完成です。

class OrdersController < ApplicationController

 def index
 end

 def create
   @order = Order.new(price: order_params[:price])
   if @order.valid?
     pay_item
     @order.save
     return redirect_to root_path
   else
     render 'index'
   end
 end

 private

 def order_params
   params.permit(:price, :token)
 end

 def pay_item
   Payjp.api_key = "sk_test_〇〇〇〇〇〇"
   Payjp::Charge.create(
     amount: order_params[:price],
     card: order_params[:token],
     currency:'jpy'
   )
 end

end

 

動作確認には、テストカードの情報を使います。

カード番号:4242424242424242

CVC:123

有効期限:登録時より未来

 

うまく記述できていれば、orderテーブルとPAY.JPの売り上げ情報にデータが保存されているはずです。

 

環境変数の設定

公開鍵、秘密鍵環境変数に保存します。

% vim ~/.zshrc
# iを押してインサートモードに移行し、下記を追記する。既存の記述は消去しない。
export PAYJP_SECRET_KEY='sk_test_000000000000'
export PAYJP_PUBLIC_KEY='pk_test_000000000000'
# 編集が終わったらescapeキーを押してから:wqと入力して保存して終了

編集したzshrcを再読み込みします。

% source ~/.zshrc

これで環境変数は設定できました。

 

次はこれを呼び出せるようにソースコードを編集します。

# app/controllers/orders_controller.rb
def
pay_item Payjp.api_key = ENV["PAYJP_SECRET_KEY"] Payjp::Charge.create( amount: price_params[:price], card: params[:token], currency:'jpy' ) end

秘密鍵を呼び出せるように編集しました。

 

公開鍵の呼び出しには、webpackerを用います。

webpackerのファイルを作成します。

% touch config/initializers/webpacker.rb

編集します。

 

Webpacker::Compiler.env["PAYJP_PUBLIC_KEY"] = ENV["PAYJP_PUBLIC_KEY"]

 

card.jsも環境変数の呼び出しを行うコードに変えます。

const pay = () => {
 Payjp.setPublicKey(process.env.PAYJP_PUBLIC_KEY);
  // 省略

以上でPAY.JPの実装が完了しました。

 

参考

https://pay.jp/introduction