#Pay.jpの導入方法
##クレジットカード決済の代行サービスで、簡単なオープンAPIで導入する事ができる
#Why
##コード量も非常に少なく、Javascriptで簡単にフォーム送信までできるのでご紹介します(割愛しながらの説明になります)
#アプリケーションの作成
% cd ~/projects(好きなように)
% rails _6.0.0_ new payjp_practice -d mysql
% cd payjp_practice
% rails db:create
#Orderモデル作成
% rails g model order
#Ordersテーブル作成
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
#バリデーション
class Order < ApplicationRecord
validates :price, presence: true
end
#ルーティング
Rails.application.routes.draw do
root to: 'orders#index'
resources :orders, only:[:create]
end
#ordersコントローラー
% rails g controller orders
class OrdersController < ApplicationController
def index
end
def create
end
end
#ビューを作成
<%= form_with model: @order, 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" %>
</div>
<%= f.submit "購入" ,class:"button" %>
<% end %>
#CSS記述
.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;
}
#コントローラー編集
class OrdersController < ApplicationController
def index
@order = Order.new
end
def create
@order = Order.new(order_params)
if @order.valid?
@order.save
return redirect_to root_path
else
render 'index'
end
end
private
def order_params
params.require(:order).permit(:price)
end
end
#部分テンプレート
<%= form_with model: @order, id: 'charge-form', class: 'card-form',local: true do |f| %>
<%= render 'layouts/error_messages', model: @order %>
<div class='form-wrap'>
<%# 省略 %>
#ビュー記述
<%= form_with model: @order, id: 'charge-form', class: 'card-form',local: true do |f| %>
<%= render 'layouts/error_messages', model: @order %>
<div class='form-wrap'>
<%= f.label :price, "金額" %>
<%= f.text_field :price, class:"price", placeholder:"例)2000" %>
</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:"3" %>
</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_year", placeholder:"例)24" %>
<p>年</p>
</div>
</div>
<%= f.submit "購入" ,class:"button" %>
<% end %>
#turbolinks削除&コード追加
<%# 省略 %>
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>
<%# 省略 %>
// 省略
require("@rails/ujs").start()
// require("turbolinks").start() // コメントアウトする
require("@rails/activestorage").start()
require("channels")
// 省略
#payjp.js読み込み
<%# 省略 %>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<script type="text/javascript" src="https://js.pay.jp/v1/"></script>
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>
<%# 省略 %>
#トークン化準備
// 省略
require("@rails/ujs").start()
// require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("../card")
// 省略
#イベント発火
const pay = () => {
const form = document.getElementById("charge-form");
form.addEventListener("submit", (e) => {
e.preventDefault();
console.log("フォーム送信時にイベント発火")
});
};
window.addEventListener("load", pay);
#公開鍵設定
const pay = () => {
Payjp.setPublicKey("pk_test_******************"); // PAY.JPテスト公開鍵
const form = document.getElementById("charge-form");
form.addEventListener("submit", (e) => {
e.preventDefault();
console.log("フォーム送信時にイベント発火")
});
};
window.addEventListener("load", pay);
#フォームの情報取得
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("order[number]"),
cvc: formData.get("order[cvc]"),
exp_month: formData.get("order[exp_month]"),
exp_year: `20${formData.get("order[exp_year]")}`,
};
});
};
window.addEventListener("load", pay);
#カードの情報トークン化
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("order[number]"),
cvc: formData.get("order[cvc]"),
exp_month: formData.get("order[exp_month]"),
exp_year: `20${formData.get("order[exp_year]")}`,
};
Payjp.createToken(card, (status, response) => {
if (status == 200) {
const token = response.id;
console.log(token)
}
});
});
};
window.addEventListener("load", pay);
##一度pay.jpが用意しているテスト用のカード情報を入力してチェックしておきましょう!
カード番号 4242424242424242(16桁)
CVC 123
有効期限 登録時より未来(04/25など)
#トークンの値をフォームに含める
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("order[number]"),
cvc: formData.get("order[cvc]"),
exp_month: formData.get("order[exp_month]"),
exp_year: `20${formData.get("order[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} name='token'>`;
renderDom.insertAdjacentHTML("beforeend", tokenObj);
debugger;
}
});
});
};
window.addEventListener("load", pay);
#トークンの値を非表示
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("order[number]"),
cvc: formData.get("order[cvc]"),
exp_month: formData.get("order[exp_month]"),
exp_year: `20${formData.get("order[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} name='token' type="hidden"> `;
renderDom.insertAdjacentHTML("beforeend", tokenObj);
debugger;
}
});
});
};
window.addEventListener("load", pay);
#クレジットカードの情報を削除
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("order[number]"),
cvc: formData.get("order[cvc]"),
exp_month: formData.get("order[exp_month]"),
exp_year: `20${formData.get("order[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} name='token' type="hidden"> `;
renderDom.insertAdjacentHTML("beforeend", tokenObj);
}
document.getElementById("order_number").removeAttribute("name");
document.getElementById("order_cvc").removeAttribute("name");
document.getElementById("order_exp_month").removeAttribute("name");
document.getElementById("order_exp_year").removeAttribute("name");
});
});
};
window.addEventListener("load", pay);
#フォームの情報をサーバーサイドに送信
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("order[number]"),
cvc: formData.get("order[cvc]"),
exp_month: formData.get("order[exp_month]"),
exp_year: `20${formData.get("order[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} name='token' type="hidden"> `;
renderDom.insertAdjacentHTML("beforeend", tokenObj);
}
document.getElementById("order_number").removeAttribute("name");
document.getElementById("order_cvc").removeAttribute("name");
document.getElementById("order_exp_month").removeAttribute("name");
document.getElementById("order_exp_year").removeAttribute("name");
document.getElementById("charge-form").submit();
});
});
};
window.addEventListener("load", pay);
#ストロングパラメーター編集
#省略
private
def order_params
params.require(:order).permit(:price).merge(token: params[:token])
end
end
#Orderモデルに追記
class Order < ApplicationRecord
attr_accessor :token
validates :price, presence: true
end
#Gem導入
# 省略
gem 'payjp'
#決済処理の記述とリファクタリング
class OrdersController < ApplicationController
def index
@order = Order.new
end
def create
@order = Order.new(order_params)
if @order.valid?
pay_item
@order.save
return redirect_to root_path
else
render 'index'
end
end
private
def order_params
params.require(:order).permit(:price).merge(token: params[: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
#バリデーション
class Order < ApplicationRecord
attr_accessor :token
validates :price, presence: true
validates :token, presence: true
end
#環境変数(Mac Catalina以降の場合)
% vim ~/.zshrc
# iを押してインサートモードに移行し、下記を追記する。既存の記述は消去しない。
export PAYJP_SECRET_KEY='sk_test_************'
export PAYJP_PUBLIC_KEY='pk_test_************'
# 編集が終わったらescキーを押してから:wqと入力して保存して終了
# 編集した.zshrcを読み込み直して、追加した環境変数を使えるようにする
% source ~/.zshrc
#秘密鍵代入した環境変数の呼び込み
#省略
def pay_item
Payjp.api_key = ENV["PAYJP_SECRET_KEY"]
Payjp::Charge.create(
amount: order_params[:price],
card: order_params[:token],
currency:'jpy'
)
end
#JavaScriptで環境変数の呼び込み
% touch config/initializers/webpacker.rb
Webpacker::Compiler.env["PAYJP_PUBLIC_KEY"] = ENV["PAYJP_PUBLIC_KEY"]
const pay = () => {
Payjp.setPublicKey(process.env.PAYJP_PUBLIC_KEY);
// 省略
#まとめ
##簡単と言っておきながら意外と記述は多かったかもしれません。ですが、APIの中でも比較的簡単な決済機能の導入なので抑えておくといいかもしれません。オリジナルでカラム等追加する場合がほとんどだと思いますので、ゆっくり順に書くことをお勧めします!以上!