#実装する機能
今回、Payjp(Pay.jp)を利用して入力フォームを作ります。
実装するものとしては下記のとおりです。
- クレジットカード情報入力フォーム
- カード情報とユーザー情報の紐づけ
- サブスク決済(定期課金)の機能
イメージとしてはこんな感じです。
#バージョン情報
ruby 2.5.1
Rails 5.2.4.4
#前提条件
- deviseが導入済みでログインができている
#1.Payjpのアカウントを作成しよう
Payjpのサイトでアカウントを作成します。
#2.APIを確認しよう
ダッシュボードのAPIより確認ができます。
今回はテストモードでの実装なので、テスト秘密鍵とテスト公開鍵を使用します。
#3.環境変数を設定しよう
環境変数は credentials.yml.enc に設定します。
ターミナルで $ EDITOR='code --wait' rails credentials:edit
と打ち込むとvscodeで編集が可能です。
payjp:
PAYJP_PRIVATE_KEY: sk_test_*******************
セットするのは、秘密鍵のみでオッケーです。yamlファイルはインデントが崩れると読み込めなくなるのでご注意ください。
※環境変数の設定は、環境により異なります。
#4.payjpのgemを設置しよう
下記をgemfileに記載しbundle installを実施します。
gem 'payjp'
#5.payjp.jsを読み込めるようにしよう
<script src="https://js.pay.jp/" type="text/javascript"></script>
を下記の通り追記します。
<head>
<title>appname</title>
<script src="https://js.pay.jp/" type="text/javascript"></script> #追記
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
#6.Userテーブルを編集しよう
新たにカラムを追加しよう。(subscription_idカラムとpremiumカラム)
ユーザーがプレミアム会員かどうかの判別するためにpremiumカラムを追加。
ユーザーが定期課金(プレミアム会員になったら)したら、subscription_idに値を持つ。
class DeviseCreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
t.string :reset_password_token
t.datetime :reset_password_sent_at
t.datetime :remember_created_at
t.string :subscription_id #追記
t.boolean :premium, default: false, null: false #追記
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
end
end
#7.Cardテーブルを用意しよう
コマンドにて下記を実行
$ rails g model Card
user_id(ユーザーと紐づけるため)
customer_id(payjpの顧客id)
card_id(payjpのデフォルトカードid)
class CreateCards < ActiveRecord::Migration[5.2]
def change
create_table :cards do |t|
t.integer :user_id
t.string :customer_id
t.string :card_id
t.timestamps
end
end
end
ここまで来たら一旦、マイグレーションを実施します。
$ rails db:migrate
#8.アソシエーションを組もう
ユーザーが複数のカード情報を持っていて、カードは一人のユーザーに紐づいている。
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :cards, dependent: :destroy
end
class Card < ApplicationRecord
belongs_to :user
end
#9.コントローラーを作成しよう
下記コマンドでコントローラーを作成します。
$ rails g controller card
作成したコントローラーの内容を変更します。
class CardsController < ApplicationController
require 'payjp'
before_action :set_api_key
def plan #定期課金プラン
Payjp::Plan.create(
:amount => 1000,
:interval => 'month',
:billing_day => 27,
:currency => 'jpy',
)
end
def create #カード登録メソッド
if params['payjp-token'].blank?
redirect_to action: "new"
# トークンが取得出来てなければループ
else
user_id = current_user.id
customer = Payjp::Customer.create(
card: params['payjp-token']
# params['payjp-token'](response.id)からcustomerを作成
)
@card = Card.new(user_id: user_id, customer_id: customer.id, card_id: customer.default_card)
if @card.save
pay #カード情報を保存できたらpayアクションを呼び出す。
else
flash[:alert] = 'カード情報を登録できませんでした'
redirect_to action: "new"
end
end
end
def pay
card = Card.where(user_id: current_user.id).first
Payjp.api_key = Rails.application.credentials.payjp[:PAYJP_PRIVATE_KEY]
subscription = Payjp::Subscription.create( #サブスク情報を作成して変数subscriptionに代入
:customer => card.customer_id,
:plan => plan, #plamアクションで定義した情報を呼び出す
metadata: {user_id: current_user.id}
)
current_user.update(subscription_id: subscription.id, premium: true)
#userテーブルのsubscription_idに値を持たせ、premiumカラムをtrueにして、user情報をアップデート
flash[:alert] = '定期課金に登録できました'
redirect_to "/"
end
def cancel
Payjp.api_key = Rails.application.credentials.payjp[:PAYJP_PRIVATE_KEY]
subscription = Payjp::Subscription.retrieve(current_user.subscription_id)
subscription.cancel
current_user.update(premium: false) #キャンセルしたユーザーは、premiumカラムをfalseにする
flash[:alert] = '定期課金を解除できました'
redirect_to "/"
end
def set_api_key
Payjp.api_key = Rails.application.credentials[:payjp][:PAYJP_PRIVATE_KEY]
end
end
ポイントは、Payjp::Subscription.create
で定期課金情報を作成して、変数subscription
に代入するところです。
変数subscriptionの中身は下記のように定期課金IDと顧客IDとプランIDを持ってます。
プラン詳細画面でも設定したプラン通りになっているのがわかります。
定期課金情報を作成してしまえば、課金日ごとに売上も月々で自動的に計上されます。
#10.ルートを作成しよう
Rails.application.routes.draw do
devise_for :users
resources :cards, only:[:index, :new, :create,:destroy,:show] do
end
end
#11.カードの登録画面&決済画面を作成しよう
今回はカード登録と同時に決済がされる画面を作成します。
完成したらhttp://localhost:3000/cards/new
にアクセスしましょう。
<p>クレジットード情報入力</p>
<%= form_with model: @card, url: cards_path, local: true, html: { name: 'inputForm' } do |f| %>
<p>カード番号</p>
<%= f.text_field :card_number, id: "card_number",class: "card_form",maxlength: '16' %>
<p>有効期限</p>
<%= f.collection_select :exp_month , Month.all, :id, :name , {},class: "card-check-box",id: "exp_month" %>
<%= f.collection_select :exp_year, Year.all, :id, :name, {}, class: "card-check-box",id: "exp_year" %>
<p>セキュリティコード</p>
<%= f.password_field :cvc, id: 'cvc',class: "card_form" ,maxlength: '6',autocomplete: "on" %>
<div class="card-btn" id="card_token"></div>
<%= f.submit 'サブスク決済をする', id: 'token_submit',class: 'card-btn' %>
<% end %>
※有効期限のセレクトボックスにはactivehashを用いています。
詳しくは、こちら→active_hashまとめ
#12.Payjpにデータを送りトークンを取得しよう
jQueryを使用するので、railsに未設定の場合は設定をしてください。
設定方法はこちら!
document.addEventListener(
"DOMContentLoaded", e => {
if (document.getElementById("token_submit") != null) {
// "token_submit"というidをもつhtmlがあるページか?つまりカード作成ページか
Payjp.setPublicKey("pk_test_bf8f6b07458f0197ea990312");
let btn = document.getElementById("token_submit"); // 送信ボタンをbtnに格納
btn.addEventListener("click", e => { // 送信ボタンがクリックされたとき
e.preventDefault(); // デフォルトのブラウザの動きをいったんとめる(createアクションへの遷移を)
let card = { // cardに入力された値をハッシュで格納
number: document.getElementById("card_number").value,
cvc: document.getElementById("cvc").value,
exp_month: document.getElementById("exp_month").value,
exp_year: document.getElementById("exp_year").value
};
Payjp.createToken(card, (status, response) => {
// カード情報をpayjpに送りカードトークンを(response.id)を受け取る。
if (status === 200) { // 正常な値の場合
$("#card_number").removeAttr("name");
$("#cvc").removeAttr("name");
$("#exp_month").removeAttr("name");
$("#exp_year").removeAttr("name");
// name属性を削除することにより、dataベースに送るのを防ぐ。
$("#card_token").append(
$('<input type="hidden" name="payjp-token">').val(response.id)
// <input type="hidden" name="payjp-token" value= response.id>が#card_tokenに追加される。
);
if(!confirm('この内容でよろしかったですか?')){
/* キャンセルの時の処理 */
return false;
}else{
/* OKの時の処理 今回は特に処理がないので空*/
}
document.inputForm.submit(); // inputFormのsubmitを発動。(上記で停止していた)
} else {
alert("カード情報が正しくありません。");
}
});
});
}
},
false
);
#13.テストカードで決済してみよう
こちらのテストカードで登録するようにしてください。
それ以外を打ち込んだ場合はトークンが発行できずはねられてしまいます。
以上でカード登録からサブスク決済まで一通り実装できました。
プランなどを自由に組み替えてコントローラーで処理を分ければ、決済機能だけのアプリを作ることもできそうです。