はじめに
例によって、某プログラミングスクールの最終課題である、フリマアプリのクローンサイト作成において、購入機能実装時にPay.jpを利用したので、健忘録としてここに記す。
今回は「クレジットカード購入(決済)機能」の実装に取り掛かります。
バージョン情報
ruby '2.5.1'
Rails '5.2.4.2'
実装の流れ
- 実装の準備・APIの導入
- モデルの作成・クレジットカード登録
- クレジットカード詳細表示・削除
- クレジットカード購入(決済)機能 ← 今回の実装内容 いよいよラスト!
前提条件として
- 記載はhaml記法で
- 私の参考にしたrailsにhamlを導入するやり方はこちら、、
- ユーザー登録機能として、gemの
devise
を利用しています。 なのでcurrent_user
などのdevise
のメソッドが随所に出てきます - 参考記事: 【Rails】deviseを導入してみる
今回の実装機能のダイジェスト
商品の詳細表示から購入画面への以降はこんな感じ!
- 商品詳細画面からのパスで購入確認画面に以降(今回は
buyアクション
で定義) - 事前にクレジットカードが登録されていたらクレジットカード詳細画面が表示される。
- 購入完了すると、Pay.jp上で決済が行われ、購入完了画面に移行(今回は
payアクション
で定義)
4. もしクレジットカードを事前登録してなければ、専用のチェックボックスの入力フォームが出てきます。
5. 必要な入力情報を入力すると、同じように購入完了画面に移行します。
注意!
クレジットカード登録のテストの際には以下のサイトのテスト用のクレジット番号を必ずお使いください!
テストカード
有効期限は現在以降の年と月であればなんでもOK
CVC(セキュリティコード)はなんでもOK!
間違ってもご自身のカード番号は使わないように
前置きはこれくらいで、いよいよ実装!
いよいよ実装! まずはコントローラーの記載
まずはコントローラーの記載を行います。
今回は(DBで商品の購入管理を行う、Purchasesテーブルを作成していることもあり)、Purchasesコントローラーを作成してそちらにアクションを記載してます。
購入確認処理をbuyアクション
で、購入処理をpayアクション
で定義してます。
require "payjp"
def buy
# 購入する商品を引っ張ってきます。
@product = Product.find(params[:product_id])
# 商品ごとに複数枚写真を登録できるので、一応全部持ってきておきます。
@images = @product.images.all
# まずはログインしているか確認
if user_signed_in?
@user = current_user
# クレジットカードが登録されているか確認
if @user.credit_card.present?
# 前前前回credentials.yml.encに記載したAPI秘密鍵を呼び出します。
Payjp.api_key = Rails.application.credentials.dig(:payjp, :PAYJP_SECRET_KEY)
# ログインユーザーのクレジットカード情報を引っ張ってきます。
@card = CreditCard.find_by(user_id: current_user.id)
# (以下は以前のcredit_cardsコントローラーのshowアクションとほぼ一緒です)
# ログインユーザーのクレジットカード情報からPay.jpに登録されているカスタマー情報を引き出す
customer = Payjp::Customer.retrieve(@card.customer_id)
# カスタマー情報からカードの情報を引き出す
@customer_card = customer.cards.retrieve(@card.card_id)
##カードのアイコン表示のための定義づけ
@card_brand = @customer_card.brand
case @card_brand
when "Visa"
# 例えば、Pay.jpからとってきたカード情報の、ブランドが"Visa"だった場合は返り値として
# (画像として登録されている)Visa.pngを返す
@card_src = "visa.gif"
when "JCB"
@card_src = "jcb.gif"
when "MasterCard"
@card_src = "master.png"
when "American Express"
@card_src = "amex.gif"
when "Diners Club"
@card_src = "diners.gif"
when "Discover"
@card_src = "discover.gif"
end
# viewの記述を簡略化
## 有効期限'月'を定義
@exp_month = @customer_card.exp_month.to_s
## 有効期限'年'を定義
@exp_year = @customer_card.exp_year.to_s.slice(2,3)
else
end
else
# ログインしていなければ、商品の購入ができずに、ログイン画面に移動します。
redirect_to user_session_path, alert: "ログインしてください"
end
end
def pay
#ちなみに見やすさ考慮し、before_actionなどのリファクタリングなどはあえてしてません。
@product = Product.find(params[:product_id])
@images = @product.images.all
# 購入テーブル登録ずみ商品は2重で購入されないようにする
# (2重で決済されることを防ぐ)
if @product.purchase.present?
redirect_to product_path(@product.id), alert: "売り切れています。"
else
# 同時に2人が同時に購入し、二重で購入処理がされることを防ぐための記述
@product.with_lock do
if current_user.credit_card.present?
# ログインユーザーがクレジットカード登録済みの場合の処理
# ログインユーザーのクレジットカード情報を引っ張ってきます。
@card = CreditCard.find_by(user_id: current_user.id)
# 前前前回credentials.yml.encに記載したAPI秘密鍵を呼び出します。
Payjp.api_key = Rails.application.credentials.dig(:payjp, :PAYJP_SECRET_KEY)
#登録したカードでの、クレジットカード決済処理
charge = Payjp::Charge.create(
# 商品(product)の値段を引っ張ってきて決済金額(amount)に入れる
amount: @product.price,
customer: Payjp::Customer.retrieve(@card.customer_id),
currency: 'jpy'
)
else
# ログインユーザーがクレジットカード登録されていない場合(Checkout機能による処理を行います)
# APIの「Checkout」ライブラリによる決済処理の記述
Payjp::Charge.create(
amount: @product.price,
card: params['payjp-token'], # フォームを送信すると作成・送信されてくるトークン
currency: 'jpy'
)
end
#購入テーブルに登録処理(今回の実装では言及しませんが一応、、、)
@purchase = Purchase.create(buyer_id: current_user.id, product_id: params[:product_id])
end
end
end
(例によって、フラッシュメッセージをしれっと入れてます)
参考にした記事: 【Rails】flashメッセージを使用して簡易メッセージを表示させる詳しい方法と解説
補足として、buyアクション
は、クレジットカード情報を引っ張ってくる関係で、前回のcredit_cardsコントローラー
のshowアクション
と似た記述になってます。
payアクション
では今回購入のための決済処理が行われる記述がなされています。
require "payjp"
Payjp.api_key = Rails.application.credentials.dig(:payjp, :PAYJP_SECRET_KEY)
# credit_cardsテーブルからuser_idに紐付けてクレジットカード情報を取ってきます。
@card = CreditCard.find_by(user_id: current_user.id)
# 決済処理の記述
charge = Payjp::Charge.create(
amount: @product.price,
customer: Payjp::Customer.retrieve(@card.customer_id),
currency: 'jpy'
)
クレジットカード登録がされていないユーザーには、その場でカード情報を打ち込ませます。
その際に公式から便利なCheckoutと言うライブラリが提供されておりますので、コチラを利用します。
これにより、簡単にカード情報打ち込みフォームをモーダルで表示させます。
チェックアウトは< script >タグを1行で、 デザインされた決済フォーム、カード情報のバリデーション、カード情報のトークン化を行うフォームを生成するライブラリです。
Checkoutライブラリにより打ち込まれたカード情報から決済を行う際には、コントローラーに以下の記述を行います。
Payjp::Charge.create(
amount: @product.price,
card: params['payjp-token'], # フォームを送信すると作成・送信されてくるトークン
currency: 'jpy'
)
後ほど、ビューにもCheckout機能呼び出しの記述を行います。
ついでにルーティングも設定しておく
Rails.application.routes.draw do
resources :products do
# 他の記述は省略
resource :purchases do
member do
get "buy"
post "pay"
end
end
end
end
続いて、ビューの記載
ちなみに部分テンプレートを使用してます(ファイル名を参照!)。
buyビュー
の記載
.buy
.main_contents
.main_details
# 商品情報を部分テンプレートで共通化してます。今回は実装内容と逸れるので省略
= render "purchases/shared/purchase_product_info.html.haml", product: @product, images: @images
.purchase_registration
.purchase_registration_main
.payment
支払い金額
.amount
= "#{@product.price}" + "円"
# クレジットカードを登録済みの場合の表示
- if @user.credit_card.present?
.purchase_function
.credit_card_info_title
クレジットカード情報
# クレジットカード情報を表示させます。
.credit_card_info
.credit_card_info__brand
= image_tag "cards/#{@card_src}", class: "credit_card_info__brand__img", alt: @card_brand
.credit_card_info__numbers
.number
= "**** **** **** " + @customer_card.last4
.expiration_date
.expiration_date__title
有効期限
.expiration_date__info
= @exp_month + " / " + @exp_year
.purchase_function__btn
= link_to "登録クレジットカードで購入する", pay_product_purchases_path(product_id: @product.id), method: :post, class: "btn"
- else
.purchase_none_btn
# Checkoutのフォームを開くための記述
= form_tag(action: :pay, method: :post) do
%script.payjp-button{src: "https://checkout.pay.jp", type: "text/javascript" ,"data-text": "購入する" ,"data-key": "pk_test_(ご自身のAPI公開鍵)",}
ポイントはCheckoutのフォームを開くための記述です。
= form_tag(action: :pay, method: :post) do
%script.payjp-button{src: "https://checkout.pay.jp", type: "text/javascript" ,"data-text": "購入する" ,"data-key": "pk_test_(ご自身のAPI公開鍵)",}
この二行を表示したいビューに書けばボタンと入力画面(モーダルウィンドウ)が勝手に出現します。
表示例としてはこんな感じ
(クレジットカード登録時)
(クレジットカード未登録時)
購入ボタンを押すとモーダルが出現します。
ちなみにこの「購入する」ボタンはクラスの指定ができなかったので、chromeの検証ツールでid名を取得し、SCSSの指定を行なってます。
payビュー
の記載
.pay
.main_contents
.main_details
.main_details__message
購入が完了しました。
# 商品の表示を部分テンプレートで共通化
# 今回の実装にはあまり関係ないので省略
= render "purchases/shared/purchase_product_info.html.haml", product: @product, images: @images
%ul.purchased__links
%li.purchased__links__TopPage
= link_to "トップページに戻る", root_path, class: "purchased__links__TopPage__btn"
%li.purchased__links__index
= link_to "購入商品一覧", "#", class: "purchased__links__index__btn"
、、、特に言うことはないですね。
表示例としてこんな感じ
flashメッセージで購入完了のメッセージを出して、リダイレクトでトップページに遷移させれば、なくてもOKですね。
###クレジットカード購入(決済)機能 実装完了!!
最後に
以上でクレジットカード購入(決済)機能の実装は完成です!
何度も言いますが、登録テスト時には必ず、テストカードの番号での登録をお願いしますね!
ワタクシごとですが、私は購入機能の実装より、購入後に商品と購入ユーザーをpurchasesテーブルに登録する処理にてこずりましたwww
そして以上で、今回実装したPay.jpの実装は以上になります。
皆様の学習や実装の参考にして頂けましたら幸いで御座います。
参考リンク
- 公式サイト
- 参考にさせて頂いたQiita記事
- その他の機能実装時の参考サイト