LoginSignup
78
96

More than 1 year has passed since last update.

【Rails / 外部API】Payjpを使った購入機能を実装する

Last updated at Posted at 2020-03-29

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

終わり

78
96
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
78
96