こんばんは!
某フリマサイト開発でpayjpでAPIを使ったクレジットカード購入機能を実装したので備忘録
完成形
![demo]
(https://gyazo.com/46bfce5c462972409f215f84bbddbaba/raw)
参考
Rails で Payjp を使って決済システムを導入する
https://qiita.com/hirotakasasaki/items/794c920016ac7c33da74
Pay.jpを使用してクレジットカード登録削除機能の実装をしてみた
https://qiita.com/wrtenniss/items/75dc631778506f8bce16
【Rails】payjpを使用した決済機能を実装する①〜クレジットカードの登録編〜
https://qiita.com/dice9494/items/4aa04da1056d1f15919e
Railsで単発決済システム(Pay.jp)を実装
https://qiita.com/nobu0717/items/52b744350ef24047e1c1
payjpAPIの使い方は上記の記事を確認頂ければ丁寧に書いてくれてますので割愛
躓いたところ
環境変数どこに書くの?
秘密鍵や公開鍵をコードに直張りはセキュリティの都合上よろしくないので、
Payjp.api_key = ENV['PAYJP_PRIVATE_KEY']
こんなふうにコード書いて.bash_profileへ記述します
$ vim ~/.bash_profile
//まず「i」押下して入力モードにしましょう
export PAYJP_ACCESS_KEY='sk_test_*************'
export PAYJP_PUBLIC_KEY='pk_test_*************'
//escキー=> : => w => qの順で.bash_profileから抜けます
//環境変数が設定できているか確認
$ printenv
exportコマンド
で定義した環境変数を確認する時はprintenvコマンド
使いましょう。環境変数名=値
と出力されればokです
これどこに書くの?
Payjp.api_key = ENV['PAYJP_PRIVATE_KEY']
charge = Payjp::Charge.create(
amount: 300,#決済金額の事。変数も使えるので後で解説
card: params['payjp-token'],
currency: 'jpy'
)
上記コードはフォームに入力したカード情報をトークン化して支払い処理を行う為の処理。APIの定型文です。決済するアクションはitem(productとか)コントローラ
が振る舞いとしてよろしいでしょう
class ItemsController < ApplicationController
def index
#省略
end
def new
#省略
end
def create
#省略
end
def pay
Payjp.api_key = ENV['PAYJP_PRIVATE_KEY']
charge = Payjp::Charge.create(
amount: 300,
card: params['payjp-token'],
currency: 'jpy'
)
end
end
決済される金額どうするの?
今のコードでは常に300円の決済しかできません。@itemに格納されているpriceを取り出しましょう
class ItemsController < ApplicationController
def pay
Payjp.api_key = ENV['PAYJP_PRIVATE_KEY']
charge = Payjp::Charge.create(
amount: @item.price,
card: params['payjp-token'],
currency: 'jpy'
)
end
end
これでok!
にはなりません。私はここでピュアRubyの理解が全くできていない事が分かりました。Railsは私のようなRuby知識しょぼ人間でも動いてくれていたんですね...
price for nill:NilClass
amount: @item.price
と書いただけでは怒られます
priceって値は@itemの中にnameやtextなどと一緒に格納されています。(語彙力ないので適切な表現でない事をお詫びします)
Railsではハッシュであるparamsを使ってビューからキーと一緒にパラメーターとして送られてきます。params[:キー名]
という形で使用できます
class ItemsController < ApplicationController
def pay
Payjp.api_key = ENV['PAYJP_PRIVATE_KEY']
charge = Payjp::Charge.create(
amount: @item.price,
card: params['payjp-token'],
currency: 'jpy'
)
end
private
def item_params
params.require(:item).permit(
:name,
:text,
:price,
#この辺の他コードは関係ない部分なので省略してます
).merge(user_id: current_user.id)
end
end
item_params
の中にpriceがいますね。この値をpayアクションに渡してあげればよさそうです
@item = Item.find(params[:id])
itemモデルからfindメソッドでid(キー)を取り出して@itemに格納します。その@itemにはgifでいう所のクロノ・トリガーの画像や商品名、商品説明、価格などが格納されています。その中から価格(price)を取り出してあげれば、その価格をpayjpで決済できますね
controller完成コード
class ItemsController < ApplicationController
def pay
@item = Item.find(params[:id])
Payjp.api_key = ENV['PAYJP_PRIVATE_KEY']
charge = Payjp::Charge.create(
amount: @item.price,
card: params['payjp-token'],
currency: 'jpy'
)
end
private
def item_params
params.require(:item).permit(
:name,
:text,
:price,
#この辺の他コードは関係ない部分なので省略してます
).merge(user_id: current_user.id)
end
end
これでpayjpを使った決済機能の基幹部分が完成しました!
payjp側でもちゃんと決済されています
画面遷移ってどうするの?
= form_tag(action: :pay, method: :post) do
%script.payjp-button{:src => "https://checkout.pay.jp", :type => "text/javascript" ,"data-text" => "購入する" ,"data-key" => "pk_test_**************"}
この二行を表示したいビューに書けばボタンと入力画面(モーダルウィンドウ)が勝手に出現します。ダミーカード番号を入力すれば決済できるのですが、間違っても自分のカード番号は入れないで下さい
カード番号: 4242424242424242
※visaカードのテストカード番号
有効期限: 12/20
※未来の日付なら何でもok
CVC: 123
※3桁なら何でもok
名前: YUI ARAGAKI
可愛い娘の名前なら何でもok
ビューはオリジナルでも問題ありません。ただ私はまだそこまで実装できていないのでこの記事には書けません。参考記事にあるのでそちらでご確認お願いします
複数画面の遷移はどうするの?
ここまでのコードではpayアクションはform_tag(action: :pay, method: :post) do ~
を埋め込んだビューでしか動きません。私の場合は商品詳細画面〜購入確認画面〜購入完了画面〜ホーム画面と遷移させたかったので
商品詳細画面=>show
購入確認画面=>purchase
※ここにform_tagを埋め込んだ
購入完了画面=>done
ルートパスへ戻る
としました
class ItemsController < ApplicationController
before_action :set_item, only: [:show, :purchase, :pay]
#これは繰り返しの記述を避けたいのでprivate以下にset_itemアクションとして記述しbefore_actionで呼び出しています
def index
@items = Item.order('id DESC').limit(4)
end
def show
end
def new
@item = Item.new
@item.item_images.build
@item.build_shipping
end
def create
@item = Item.new(item_params)
if @item.save
redirect_to action: :index
end
end
def purchase
end
def pay
Payjp.api_key = ENV['PAYJP_PRIVATE_KEY']
charge = Payjp::Charge.create(
amount: @item.price,
card: params['payjp-token'],
currency: 'jpy'
)
redirect_to action: :done
end
def done
end
private
def set_item
@item = Item.find(params[:id])
end
def item_params
params.require(:item).permit(
:name,
:text,
:price,
).merge(user_id: current_user.id)
end
end
これで後はルーティングを設定すれば3画面遷移ができます
#payjpに必要な箇所だけ抜粋
resources :items do
collection do
get 'purchase/:id'=> 'items#purchase', as: 'purchase'
post 'pay/:id'=> 'items#pay', as: 'pay'#httpメソッドはpostなので注意
get 'done'=> 'items#done', as: 'done'
end
end
まとめ
Rubyの理解不足でハマっていた部分を除けば難しい事はなく実装できると思います。ただテストコードの書き方が分からずなので、誰かご教授頂きたいです。次は購入したら出品ステータスが取引中や取引完了になって購入できなくなる部分の実装とクレジットカード登録の実装が待っていますので、出力の低い脳みそフルフル回転させていきます!