決済機能のサーバーサイド実装
1. サーバーサイドでトークンの情報が受け取れるようにする
2. Orderモデルにトークンの値を追加する
3. PAY.JPによる決済処理を行う
4. バリデーションを設定し、テストコードを実装する
サーバーサイドでトークンの情報を受け取る
決済処理にはトークン(token)の値も必要になるため、tokenを受け取れるようにストロングパラメーターを編集
paramsを確認 binding.pryを設定
app/controllers/orders_controller.rb
#省略
def create
binding.pry
@order = Order.new(order_params)
if @order.valid?
@order.save
return redirect_to root_path
else
render 'index', status: :unprocessable_entity
end
end
#省略
フォームの送信を行い、paramsの中身を確かめ
priceの情報はorderというキーのバリューとして、ハッシュ形式で含まれていることがわかる
一方、tokenの情報はorderというキーのバリューとしては含まれていない
したがって、mergeメソッドを用いてこのtokenの値を結合する
ストロングパラメーターを編集
app/controllers/orders_controller.rb
#省略
private
def order_params
params.require(:order).permit(:price).merge(token: params[:token])
end
#省略
order_params[:price]としてpriceの情報が、order_params[:token]としてtokenの情報が取得できる
order_paramsの中にそれぞれの情報が含まれていることを確認
確認できたら、binding.pryの記述は削除
Orderモデルにトークンの値を追加
tokenの値もOrderモデルで取り扱えるようにする
app/models/order.rb
class Order < ApplicationRecord
attr_accessor :token
validates :price, presence: true
end
決済処理を実装
決済処理を行うときに必要となるGem
Gemfile最下部に記述
# 省略
gem 'payjp'
記述したら、bundle installを実行し、導入
秘密鍵を取得
PAY.JPにログイン後、ページの左側の「API」をクリックすると確認できる
※公開鍵同様、この秘密鍵も外部に漏らしてはいけない、環境変数化した上で公開することができる
決済処理を記述
app/controllers/orders_controller.rb
#省略
def create
@order = Order.new(order_params)
if @order.valid?
Payjp.api_key = "sk_test_***********" # 自身のPAY.JPテスト秘密鍵を記述
Payjp::Charge.create(
amount: order_params[:price], # 商品の値段
card: order_params[:token], # カードトークン
currency: 'jpy' # 通貨の種類(日本円)
)
@order.save
return redirect_to root_path
else
render 'index', status: :unprocessable_entity
end
end
#省略
バリデーションを正常に通過した時のみに、決済処理が行われる
amountには、実際に決済する金額が入る
cardには、トークン化されたカード情報が入る
currencyには取引に使用する通貨の種類を記述
決済処理が行われるか確認
サーバーを再起動
必要な情報を入力し、金額がordersテーブルに保存されるか、PAY.JP上で決済の記録が残るかを確認
PAY.JPにログインし、売上の項目を選択することで閲覧できる
リファクタリングする
コントローラーの記述はやや複雑で読みにくい
新たにメソッドを定義し、その中に決済処理を移動
app/controllers/orders_controller.rb
#省略
def create
@order = Order.new(order_params)
if @order.valid?
pay_item
@order.save
return redirect_to root_path
else
render 'index', status: :unprocessable_entity
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
#省略
pay_itemというメソッドに切り出し、コントローラーの処理の可読性を高める
再度正しく決済処理が実行されるか確かめる
バリデーションを実装し、テストコードを実装
tokenが空では保存できないというバリデーションを設定
app/models/order.rb
class Order < ApplicationRecord
attr_accessor :token
validates :price, presence: true
validates :token, presence: true
end
テストコードを編集して実行
spec/factories/orders.rb
FactoryBot.define do
factory :order do
price {3000}
token {"tok_abcdefghijk00000000000000000"}
end
end
spec/models/order_spec.rb
require 'rails_helper'
RSpec.describe Order, type: :model do
before do
@order = FactoryBot.build(:order)
end
context '内容に問題ない場合' do
it "priceとtokenがあれば保存ができること" do
expect(@order).to be_valid
end
end
context '内容に問題がある場合' do
it "priceが空では保存ができないこと" do
@order.price = nil
@order.valid?
expect(@order.errors.full_messages).to include("Price can't be blank")
end
it "tokenが空では登録できないこと" do
@order.token = nil
@order.valid?
expect(@order.errors.full_messages).to include("Token can't be blank")
end
end
end
以下のコマンドを実行し、正しくテストが完了することを確かめる
% bundle exec rspec spec/models/order_spec.rb
ブラウザで挙動を確認
何も入力せずに「購入ボタンをクリックして、priceとtokenについてのエラーメッセージが表示されることを確かめる
次は環境変数の設定