How to PAY.JP
概要
devise、Herokuに引き続き、フリマアプリ作成過程で扱った技術についてアウトプットします。今回はPAY.JPです。
PAY.JPとは
PAY.JPはクレジット機能を実装できるAPIです。クレジット機能の実装はカード番号などの個人情報の取り扱いもあるため、既存のAPIを利用する事が望ましいです。
PAY.JPを利用した場合の処理の流れは以下のようになっています。
①クライアント側でJavaScriptを用いてPAY.JPにカード情報をリクエスト
②PAY.JPはそれに対しレスポンスを返し、JavaScriptはトークンを生成(公開鍵)
③JavaScriptからサーバー側へトークンと決済情報をパラメーターとして送信
④サーバー側はトークンと決済情報をもとにPAY.JPと通信し、決済を行う(秘密鍵)
公開鍵、秘密鍵はPAY.JPのAPI情報のところから入手できます。
なお、PAY.JPの利用にはアカウント登録が必要です。
使い方
ここでは、クレジット決済の練習用アプリを作成します。
事前準備
まずはアプリを立ち上げます
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 %>
決済金額と日時の情報を記録する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
|
秘密鍵を呼び出せるように編集しました。
公開鍵の呼び出しには、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の実装が完了しました。
参考