こんばんは!
某フリマサイト開発でpayjpでAPIを使ったクレジットカード購入機能を実装したので備忘録
完成形
参考
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とか)コントローラ
が振る舞いとしてよろしいでしょう
classItemsController<ApplicationControllerdefindex#省略enddefnew#省略enddefcreate#省略enddefpayPayjp.api_key=ENV['PAYJP_PRIVATE_KEY']charge=Payjp::Charge.create(amount: 300,card: params['payjp-token'],currency: 'jpy')endend
決済される金額どうするの?
今のコードでは常に300円の決済しかできません。@itemに格納されているpriceを取り出しましょう
classItemsController<ApplicationControllerdefpayPayjp.api_key=ENV['PAYJP_PRIVATE_KEY']charge=Payjp::Charge.create(amount: @item.price,card: params['payjp-token'],currency: 'jpy')endend
これでok!
にはなりません。私はここでピュアRubyの理解が全くできていない事が分かりました。Railsは私のようなRuby知識しょぼ人間でも動いてくれていたんですね...
price for nill:NilClass
amount: @item.price
と書いただけでは怒られます
priceって値は@itemの中にnameやtextなどと一緒に格納されています。(語彙力ないので適切な表現でない事をお詫びします)
Railsではハッシュであるparamsを使ってビューからキーと一緒にパラメーターとして送られてきます。params[:キー名]
という形で使用できます
classItemsController<ApplicationControllerdefpayPayjp.api_key=ENV['PAYJP_PRIVATE_KEY']charge=Payjp::Charge.create(amount: @item.price,card: params['payjp-token'],currency: 'jpy')endprivatedefitem_paramsparams.require(:item).permit(:name,:text,:price,#この辺の他コードは関係ない部分なので省略してます).merge(user_id: current_user.id)endend
item_params
の中にpriceがいますね。この値をpayアクションに渡してあげればよさそうです
@item = Item.find(params[:id])
itemモデルからfindメソッドでid(キー)を取り出して@itemに格納します。その@itemにはgifでいう所のクロノ・トリガーの画像や商品名、商品説明、価格などが格納されています。その中から価格(price)を取り出してあげれば、その価格をpayjpで決済できますね
controller完成コード
classItemsController<ApplicationControllerdefpay@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')endprivatedefitem_paramsparams.require(:item).permit(:name,:text,:price,#この辺の他コードは関係ない部分なので省略してます).merge(user_id: current_user.id)endend
これで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
※未来の日付なら何でもokCVC: 123
※3桁なら何でもok名前: YUI ARAGAKI
可愛い娘の名前なら何でもok
ビューはオリジナルでも問題ありません。ただ私はまだそこまで実装できていないのでこの記事には書けません。参考記事にあるのでそちらでご確認お願いします
複数画面の遷移はどうするの?
ここまでのコードではpayアクションはform_tag(action: :pay, method: :post) do ~
を埋め込んだビューでしか動きません。私の場合は商品詳細画面〜購入確認画面〜購入完了画面〜ホーム画面と遷移させたかったので商品詳細画面=>show
購入確認画面=>purchase
※ここにform_tagを埋め込んだ購入完了画面=>done
ルートパスへ戻る
としました
classItemsController<ApplicationControllerbefore_action:set_item,only: [:show,:purchase,:pay]#これは繰り返しの記述を避けたいのでprivate以下にset_itemアクションとして記述しbefore_actionで呼び出していますdefindex@items=Item.order('id DESC').limit(4)enddefshowenddefnew@item=Item.new@item.item_images.build@item.build_shippingenddefcreate@item=Item.new(item_params)if@item.saveredirect_toaction: :indexendenddefpurchaseenddefpayPayjp.api_key=ENV['PAYJP_PRIVATE_KEY']charge=Payjp::Charge.create(amount: @item.price,card: params['payjp-token'],currency: 'jpy')redirect_toaction: :doneenddefdoneendprivatedefset_item@item=Item.find(params[:id])enddefitem_paramsparams.require(:item).permit(:name,:text,:price,).merge(user_id: current_user.id)endend
これで後はルーティングを設定すれば3画面遷移ができます
#payjpに必要な箇所だけ抜粋resources:itemsdocollectiondoget'purchase/:id'=>'items#purchase',as: 'purchase'post'pay/:id'=>'items#pay',as: 'pay'#httpメソッドはpostなので注意get'done'=>'items#done',as: 'done'endend
まとめ
Rubyの理解不足でハマっていた部分を除けば難しい事はなく実装できると思います。ただテストコードの書き方が分からずなので、誰かご教授頂きたいです。次は購入したら出品ステータスが取引中や取引完了になって購入できなくなる部分の実装とクレジットカード登録の実装が待っていますので、出力の低い脳みそフルフル回転させていきます!