目的
Rails 5.2にPayjpを導入してクレジットカードを登録する。
Payjpとは
開発者がクレジットカード情報を扱わなくてもいいように、入力されたカード情報をトークンに置き換えて管理するものです。
なぜPayjpを使うのか
クレジットカードの情報をデータベースに保存して管理するのはセキュリティリスクが高いため、情報をトークン化(暗号化)してやりとりする方法が推奨されているためです。
(前提、環境)
mac
macOS Mojave 10.14.5
ruby 2.5.1
rails 5.2.3
はじめに
公式
参考記事
導入手順
Payjpのgemを導入
Gemfileに追記してbundle installします。
# Gemfile
gem 'payjp'
# ターミナル
$ bundle install
userモデルとcardモデルを作成
クレジットカードを取り扱うためのモデルを作成します。
今回はcardモデルとして作成します。
また、登録者としてuserモデルを作成してアソシエーションを組んでおきます。
簡単なログイン機能も実装したいのでdeviseも合わせて導入します。
deviseのgemを導入
Gemfileに追記してbundle installします。
# Gemfile
gem 'devise'
# ターミナル
$ bundle install
$ rails g devise:install
$ rails g devise user
$ rake db:migrate
ルーティングは以下のように設定しておきます。
※仮置きです。
# config/routes.rb
Rails.application.routes.draw do
devise_for :users
root "cards#index"
resources :cards
end
# ターミナル
$ rails g model card
$ rails g devise user
カードモデルに関して、あとでトークンについて解説しますが、顧客IDを絶対保存させるバリデーションを設定しておきます。
また、カードとユーザーは1対1の関係性を示すためhas_oneでアソシエーションを組んでいます。
# app/models/card.rb
class Card < ApplicationRecord
belongs_to :user, optional: true
validates :customer_token, presence: true
end
# app/models/user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one :card, optional: true
end
またcardのマイグレーションは下記のようにします。
※userはdeviseを利用し他ので特に変更なしです。必要に応じてカラムを追加してください。
# db/migrations/~~~~~~~~~create_cards.rb
class CreateCards < ActiveRecord::Migration[5.2]
def change
create_table :cards do |t|
t.string :customer_token, null: false
t.references :user, forign_key: true
t.timestamps
end
end
end
Payjp APIの登録
公式サイトにアクセス
公式サイトにアクセスします。
https://pay.jp/signup
ログイン
APIキーを確認
https://pay.jp/d/settings
こちらにアクセスした上で登録されているAPIキーを確認しましょう!
こちらのキーはあとで登録に使うので要注意です!
Railsにキーをセット
今回Rails 5.2を入れているのでcredentials.ymlでセットします。
# ターミナル
$ EDITOR="vi" bin/rails credentials:edit
credentials.ymlが開いたら下記を登録しましょう。
payjp:
public_key: pkで始まる公開鍵
secret_key: skで始まる秘密鍵
Payjpのキーを使う
例としてあげますが、コントローラ内では以下のようにしてセットして使います。
# app/controllers/cards_controller.rb
Class CardsController < ApplicationController
def index
Payjp.api_key = Rails.application.credentials.payjp[:secret_key]
end
end
カード情報のトークン化
リファレンス(Payjp API公式サイト)を参照して作っていきます。
公式サイトをほぼそのまま活用しているのですが、jQueryを利用しての前提とした実装になっています。
今回はサンプルに記載のpayjp.jsを少しカスタマイズして作成していきます。
jQueryをRailsに導入
# Gemfile
gem 'jquery-rails'
# ターミナル
$ bundle install
# app/javascripts/application.js
//= require rails-ujs
//= require activestorage
//= require turbolinks
# ----追記----
//= require jquery
//= require rails-ujs
# -----------
//= require_tree .
登録フォームを実装
日付の入力部分など少し適当です笑
今回重要なのはpayjpのJavaScriptを読み込むためにscriptタグでpayjpが用意したファイルを呼び出していることです。
# app/views/cards/index.html.erb
<script type="text/javascript" src="https://js.pay.jp/"></script>
<script type="text/javascript">Payjp.setPublicKey("<pkから始まる公開鍵を入力します>");</script>
<%= form_with model: @card, id: "card_form" do |f| %>
<span class="charge-errors"></span>
<h4>支払い</h4>
<%= f.label :number, "カード番号"%>
<%= f.text_field :number, class: "number", maxlength: "16", placehplder: "カード番号"%>
<%= f.label :cvc, "CVC"%>
<%= f.number_field :cvc, class: "cvc", maxlength: "3", placehplder: "CVC"%>
<%= f.label :exp_month, "有効期限"%>
<%= f.number_field :exp_month, class: "exp_month", maxlength: "2", placehplder: "月"%>
<%= f.number_field :exp_year, class: "exp_year", maxlength: "4", placehplder: "年"%>
<%= f.submit "送信" ,id: "regist_card"%>
<div id="card_token"></div>
<% end %>
カードトークン作成
リファレンス(Payjp API公式サイト)を参照し、railsで用いているturbolinksの設定を追加しました。
また、カードトークンの説明です。
カード情報を代替するトークンオブジェクトです。
トークンは、カード番号やCVCなどのセキュアなデータを隠しつつも、カードと同じように扱うことができます。
顧客にカードを登録するときや、支払い処理を行うときにカード代わりとして使用します。
一度使用したトークンは再び使用することはできませんが、 顧客にカードを登録すれば、顧客IDを支払い手段として用いることで、何度でも同じカードで支払い処理ができるようになります。
通常inputタグに入力された値はparamsに渡されてコントローラーに向かいますが、クレジットカードの番号などの情報をparamsにそのまま入れることは禁じられています。(そのためにpayjpがあるのですが。)
そのため、一旦それらの貴重な情報を暗号化したトークンにして管理してしまうのです。
以下がリファレンスを参照にしたJavaScriptです。
# app/assets/javascripts/cards.js
$(document).on('turbolinks:load', function () {
var form = $("#card_form");
number = form.find(".number"),
cvc = form.find(".cvc"),
exp_month = form.find(".exp_month"),
exp_year = form.find(".exp_year");
$("#card_form").on("click", "#regist_card", function (e) {
e.preventDefault();
form.find("input[type=submit]").prop("disabled", true);
var card = {
number: number.val(),
cvc: cvc.val(),
exp_month: exp_month.val(),
exp_year: exp_year.val()
};
Payjp.createToken(card, function (s, response) {
if (response.error) {
alert("error")
form.find('button').prop('disabled', false);
}
else {
$(".number").removeAttr("name");
$(".cvc").removeAttr("name");
$(".exp_month").removeAttr("name");
$(".exp_year").removeAttr("name");
var token = response.id;
$("#card_form").append(`<input type="hidden" name="card_token" class="payjp-token" value=${token} />`)
$("#card_form").get(0).submit();
}
});
});
});
顧客IDの作成
カードトークンは一度使用すると二度と使えなくなってしまうので、顧客IDを同時に作成して保持します。
# app/controllers/cards_controller.rb
class CardsController < ApplicationController
def index
@card = Card.new
end
def create
email = current_user.email
Payjp.api_key = Rails.application.credentials.payjp[:secret_key]
customer = Payjp::Customer.create( ## 顧客の作成
email: email,
card: params[:card_token]
)
@card = Card.new(customer_token: customer&.id)
redirect_to action: "new", alert: "カードの登録に失敗しました。" and return if @card.invalid?
## 保存に成功した場合
@card.user_id = current_user.id
@card.save
redirect_to cards_path and return
end
end
結果
顧客情報がトークン化されて色々なことができるようになる。
終わりに
全然機能の紹介が終わらないのでシリーズ化します。
まだ顧客情報の作成だけとは・・・・泣
※payjpの実装はリファレンスをとにかくよく読んで欲しいです!
しかし、APIのやり取りをするということのいい練習になるかと思います。
普通にアプリ作っていたらクレカの登録とかさせないといけないし必須機能ですかね〜!
自分の備忘録としても役に立ちそうです。