Posted at

RailsでPayjpを使った購入機能を実装する

こんばんは!

某フリマサイト開発でpayjpでAPIを使ったクレジットカード購入機能を実装したので備忘録


完成形

demo

demo


参考


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



.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アクション

    Payjp.api_key = ENV['PAYJP_PRIVATE_KEY']

charge = Payjp::Charge.create(
amount: 300,#決済金額の事。変数も使えるので後で解説
card: params['payjp-token'],
currency: 'jpy'
)

上記コードはフォームに入力したカード情報をトークン化して支払い処理を行う為の処理。APIの定型文です。決済するアクションはitem(productとか)コントローラが振る舞いとしてよろしいでしょう


items_controller.rb

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を取り出しましょう


items_controller.rb

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

スクリーンショット 2019-09-05 1.37.41.png

amount: @item.priceと書いただけでは怒られます

priceって値は@itemの中にnameやtextなどと一緒に格納されています。(語彙力ないので適切な表現でない事をお詫びします)

Railsではハッシュであるparamsを使ってビューからキーと一緒にパラメーターとして送られてきます。params[:キー名]という形で使用できます


items_controller.rb

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完成コード


items_controller.rb

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を使った決済機能の基幹部分が完成しました!

スクリーンショット 2019-09-05 2.11.50.png

payjp側でもちゃんと決済されています


画面遷移ってどうするの?


show.html.haml

= 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

ルートパスへ戻る

としました


items_controller.rb

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画面遷移ができます


routes.rb

#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の理解不足でハマっていた部分を除けば難しい事はなく実装できると思います。ただテストコードの書き方が分からずなので、誰かご教授頂きたいです。次は購入したら出品ステータスが取引中や取引完了になって購入できなくなる部分の実装とクレジットカード登録の実装が待っていますので、出力の低い脳みそフルフル回転させていきます!


終わり