【Rails】PAY.JPを用いた購入機能の実装について
購入機能の実装が一段落したので関連する機能およびエラーについてアウトプットしていきたいと思います。 今回はPAY.JPを用いた購入機能の実装について。 ネットでもリアルでもクレジットカードが使えない場面というのは今やほとんどないですよね。(ラーメン屋さんとかは券売機使ってるから現金のみのところも多い気がしますが) スタンダードな決済機能、しっかりと実装していきましょう!
なぜオープンAPIを使うのか?
そもそもの前提の話をまず記録しておきます。 簡単な話、カード会社と直接連携しようとすると発生する以下2点の問題が解決できるからオープンAPIを使用します。
- 会社ごとに事務的手続きをして連携しなければならない
- 金銭の授受に関わるため、一定のセキュリティー基準をクリアしないといけない
尚、カード情報を直接アプリケーションに送ってサーバーにデータを保存・処理を行うことが2018年6月以降、改正割賦販売法によって禁じられています。 したがって、トークン(セキュリティを担保するために用いられる、一回のみ使用可能なパスワード)を使用して処理することで決済をおこないます。
実装の手順
実装の流れとしては、クライアントサイド→クライアントサイド(トークン送付)→サーバーサイド(決済機能)です。
・クライアントサイド実装・ ①turbolinksの確認 今回は手作業でJavaScriptを記述してフォーム送信処理等を実装していくので、turbolinksは使用しません。 そのため、以下2ファイルの記述を確認していきます。
<%# 省略 %> <%= stylesheet_link_tag 'application', media: 'all' %> <%= javascript_pack_tag 'application' %> <%# 省略 %>
require("@rails/ujs").start() require("turbolinks").start() →コメントアウトする require("@rails/activestorage").start() require("channels")
②クライアントサイドでPAY.JPのAPIを使用するために必要なJavaScriptを読み込む ビューファイルに以下の一文を記述し、JavaScriptを読み込ませます。
<head> <title>Furima</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <script type="text/javascript" src="https://js.pay.jp/v1/"></script> //追記 <%= stylesheet_link_tag 'application', media: 'all' %> <%= javascript_pack_tag 'application' %> </head>
③トークン化を行うJavaScriptファイルを作成
app/javascriptディレクトリに直接JavaScriptファイルを生成。
また、application.jsにrequire("xxx")
を記述し、JSファイルが読み込まれるようにします(xxxがファイル名、階層があるときはそれも記述しよう)。
手作業で生成したファイルに、以下を記述。
const pay = () => { Payjp.setPublicKey(process.env.PAYJP_PUBLIC_KEY); //② const form = document.getElementById("charge-form"); //①~ form.addEventListener("submit", (e) => { e.preventDefault(); //~① const formResult = document.getElementById("charge-form"); //③~ const formData = new FormData(formResult); const card = { number: formData.get("order[number]"), cvc: formData.get("order[cvc]"), exp_month: formData.get("order[exp_month]"), exp_year: `20${formData.get("order[exp_year]")}`, }; //~③ Payjp.createToken(card, (status, response) => { //④~ if (status == 200) { const token = response.id; //~④ } )}; }); }; window.addEventListener("load", pay);
console.log
の記述をおこない、それぞれが正しく機能しているか確認しましょう。
FormDataとは、フォームに記載された値を取得できるオブジェクト。 今回は
charge-form
でフォーム全体を指定し、そこで入力された値をformData
として生成します。
また、card
で各フォームの情報を取得します。
get
以降の()内は各フォームのname属性を指定するのですが、必ず検証ツールから取得しましょう。
exp_year
(有効期限の年の部分)は20xx年になるように記述することも忘れずに!
createToken
の第1引数にcard(取得したカード情報)、第2引数にstatus,responseをアロー関数(functionの代わりに() =>を用いて関数を定義)を用いて定義しています。
status
にはHTTPのステータスコード、response
にはそのレスポンスの内容が入ります。
変数token
にresponse.id
を入れることで、トークンの値を取得できます。
・トークン送付に関するクライアントサイドの実装・ ①トークン情報をフォームに追加・送信
const pay = () => { Payjp.setPublicKey(process.env.PAYJP_PUBLIC_KEY); const form = document.getElementById("charge-form"); form.addEventListener("submit", (e) => { e.preventDefault(); const formResult = document.getElementById("charge-form"); const formData = new FormData(formResult); const card = { number: formData.get("order[number]"), cvc: formData.get("order[cvc]"), exp_month: formData.get("order[exp_month]"), exp_year: `20${formData.get("order[exp_year]")}`, }; Payjp.createToken(card, (status, response) => { if (status == 200) { const token = response.id; const renderDom = document.getElementById("charge-form"); //①~ const tokenObj = `<input value=${token} name='token' type="hidden">`; renderDom.insertAdjacentHTML("beforeend", tokenObj); } //~① document.getElementById("card-number").removeAttribute("name"); //②~ document.getElementById("card-exp-month").removeAttribute("name"); document.getElementById("card-exp-year").removeAttribute("name"); document.getElementById("card-cvc").removeAttribute("name"); //~② document.getElementById("charge-form").submit(); //③ }); }); }; window.addEventListener("load", pay);
debugger;
で処理を確かめてから、input要素をtype="hidden"
で隠します。
e.preventDefault();
でRailsの送信処理をキャンセルしているため、JavaScriptから送信するような記述を行います。
・サーバーサイドにおける実装・ ①ストロングパラメーターにトークンの情報を追加
def order_params params.require(:order).permit(:post_code, :prefecture_id, :city, :address, :building, :phone_number).merge(user_id: current_user.id, item_id: params[:item_id], token:params[:token]) end
②モデルにトークンの情報を追加
class Order < ApplicationRecord attr_accessor :token ~~省略~~
attr_accessorメソッドとは、記述したクラスにゲッターとセッターを定義してくれるもの。 モデルに対応するテーブルのカラム名以外を扱いたい時に使用する。 ③PAY.JPによる決済処理
def pay_item Payjp.api_key = ENV["PAYJP_SECRET_KEY"] //① Payjp::Charge.create( amount: @item.price, card: order_params[:token], currency: 'jpy' ) //② end
Payjp::Charge.create
というクラス及びクラスメソッドを使用して値段、カード情報、通貨単位の情報を保存する。
④バリデーションの設定 空の情報は送信できないよう設定。
validates :token, presence:true
まとめ
JavaScriptの記述にまだ慣れていないので、とっても難しく感じています どのname属性、IDなどの値を取得するのかなど、一つ一つの処理の意味を考えていけば自ずとわかるはず… なのでまずは流れを確認しながら、繰り返し復習していきたいと思います!