We have introduced PAY.JP to make credit card payments with a personal app. There was a slight stumbling block in the introduction, so I have listed it as a memorandum.
Register from the following URL. PAY.JP
When registration is complete, you will be taken to the screen above. (Because it was used, sales are high.) Initially it is in test mode. In order to make a real transaction, you need to apply and switch to live mode. This time I'm using the test mode because it's for personal apps and not for commercial purposes.
Use the contents of "API" in the menu bar.
I will use this test private key and test public key for my app.
Add the following to Gemfile
gem 'payjp'
From here, we will prepare to create an input form for card information, but there are two ways to do it.
About tokenization of card information
At checkout
<form action="/pay" method="post">
<script src="https://checkout.pay.jp/" class="payjp-button" data-key=""></script>
</form>
By describing the above, you can use the form prepared by PAYJP.
This time I wanted to customize the form myself, so I did the following:
Add the following to the head part of application.html.haml.
%script{src: "https://js.pay.jp", type: "text/javascript"}
Officially, in addition to the above, the public key is also described, but I described it separately (described later).
Next, write the PAYJP public key and private key in credentials.yml.enc.
A little preparation is required to edit the file. First, make settings so that VS Code can be started from the terminal. In VSCode, press Command + Shift + P at the same time to open the command palette. Then enter "shell". In the menu, the item "Install'code' command in PATH" is displayed. Click it. By doing this, you can now start VS Code by typing "code" from the terminal.
Next, edit the contents of the file below.
$ pwd
#Make sure you are in your app directory
$ EDITOR='code --wait' rails credentials:edit
After a while, the file will open as shown below.
Write here as follows. Notice that it is nested.
payjp:
PAYJP_SECRET_KEY: sk_test_...
PAYJP_PUBLIC_KEY: pk_test_...
If you write your own key, save it, and then close the file ”New credentials encrypted and saved.” Is output to the terminal, so it is complete.
You can check if it has been saved with the following command.
$ rails c
$ Rails.application.credentials[:payjp][:PAYJP_SECRET_KEY]
$ Rails.application.credentials[:payjp][:PAYJP_PUBLIC_KEY]
This time we will name it the Card model. I created it as follows.
class Card < ApplicationRecord
belongs_to :user
has_one :order, dependent: :nullify
require 'payjp'
Payjp.api_key = Rails.application.credentials.dig(:payjp, :PAYJP_SECRET_KEY)
def self.create_card_to_payjp(params)
#Create token
token = Payjp::Token.create({
card: {
number: params['number'],
cvc: params['cvc'],
exp_month: params['valid_month'],
exp_year: params['valid_year']
}},
{'X-Payjp-Direct-Token-Generate': 'true'}
)
#Create customer information based on the token created above
Payjp::Customer.create(card: token.id)
end
Since the EC site was created in the personal application, the association is User and Order.
We are tokenizing the card information here.
I created a table with the following contents.
class CreateCards < ActiveRecord::Migration[5.2]
def change
create_table :cards do |t|
t.string :customer_id, null: false
t.string :card_id, null: false
t.references :user, null: false, foreign_key: true
t.timestamps
end
end
end
I didn't know at first, but customer_id and card_id need to be created as columns on the mast. Records are registered in the table as follows.
This time, I configured the controller with 4 actions as follows.
class CardsController < ApplicationController
before_action :set_card, only: [:new, :show, :destroy]
before_action :set_payjpSecretKey, except: :new
before_action :set_cart
before_action :set_user
require "payjp"
def new
redirect_to action: :show, id: current_user.id if @card.present?
@card = Card.new
gon.payjpPublicKey = Rails.application.credentials[:payjp][:PAYJP_PUBLIC_KEY]
end
def create
render action: :new if params['payjpToken'].blank?
customer = Payjp::Customer.create(
card: params['payjpToken']
)
@card = Card.new(
card_id: customer.default_card,
user_id: current_user.id,
customer_id: customer.id
)
if @card.save
flash[:notice] = 'Credit card registration is complete'
redirect_to action: :show, id: current_user.id
else
flash[:alert] = 'Credit card registration failed'
redirect_to action: :new
end
end
def show
redirect_to action: :new if @card.blank?
customer = Payjp::Customer.retrieve(@card.customer_id)
default_card_information = customer.cards.retrieve(@card.card_id)
@card_info = customer.cards.retrieve(@card.card_id)
@exp_month = default_card_information.exp_month.to_s
@exp_year = default_card_information.exp_year.to_s.slice(2,3)
customer_card = customer.cards.retrieve(@card.card_id)
@card_brand = customer_card.brand
case @card_brand
when "Visa"
@card_src = "icon_visa.png "
when "JCB"
@card_src = "icon_jcb.png "
when "MasterCard"
@card_src = "icon_mastercard.png "
when "American Express"
@card_src = "icon_amex.png "
when "Diners Club"
@card_src = "icon_diners.png "
when "Discover"
@card_src = "icon_discover.png "
end
end
def destroy
customer = Payjp::Customer.retrieve(@card.customer_id)
@card.destroy
customer.delete
flash[:notice] = 'Credit card deleted'
redirect_to controller: :users, action: :show, id: current_user.id
end
private
def set_card
@card = Card.where(user_id: current_user.id).first
end
def set_payjpSecretKey
Payjp.api_key = Rails.application.credentials[:payjp][:PAYJP_SECRET_KEY]
end
def set_cart
@cart = current_cart
end
def set_user
@user = current_user
end
end
In addition, in order to avoid writing the PAYJP public key in a JS file, which will be described later, we have introduced a gem called "gon". It is a recognition to use when you want to use ruby variables in JS files.
Add the following to Gemfile
gem 'gon'
Add the following to the head part of application.html.haml.
= include_gon(init: true)
Now you are ready to go.
From here, create a JS file that works with new.html.haml, which is called by the controller's new action. First, write new.html.haml.
.cardNew
.title
Credit card information input
.cardForm
= form_with model: @card, id: "form" do |f|
.cardForm__number
= f.label :card number, class: "cardForm__number__title"
%span.must_check required
.cardForm__field
= f.text_field :card_number, id: "card_number", placeholder: "Half-width numbers only", class: "form-group__input", maxlength: 16
.cardForm__image
= image_tag(image_path('cards/icon_visa.png'), class: 'visa', width: 58, height: 28)
= image_tag(image_path('cards/icon_mastercard.png'), class: 'master', width: 47, height: 36)
= image_tag(image_path('cards/icon_jcb.png'), class: 'jcb', width: 40, height: 30)
= image_tag(image_path('cards/icon_amex.png'), class: 'amex', width: 40, height: 30)
= image_tag(image_path('cards/icon_diners.png'), class: 'diners', width: 45, height: 32)
= image_tag(image_path('cards/icon_discover.png'), class: 'discover', width: 47, height: 30)
.cardForm__expirationdate
.cardForm__expirationdate__details
= f.label :expiration date
%span.must_check required
%br
.cardForm__expirationdate__choice
.cardForm__expirationdate__choice__month
= f.select :expiration_month, options_for_select(["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"]), {}, {id: "exp_month", name: "exp_month", type: "text"}
= f.label :Month, class: "cardForm__expirationdate__choice__month__label"
.cardForm__expirationdate__choice__year
= f.select :expiration_year, options_for_select((2020..2030)), {}, {id: "exp_year", name: "exp_year", type: "text"}
= f.label :Year, class: "cardForm__expirationdate__choice__year__label"
.cardForm__securitycode
.cardForm__securitycode__details
.cardForm__securitycode__details__title
= f.label :security code, class: "label"
%span.must_check required
.cardForm__securitycode__details__field
= f.text_field :cvc, id: "cvc", class: "cvc", placeholder: "Card back 3~4-digit number", maxlength: "4"
.cardForm__securitycode__details__hatena
= link_to "What is the number on the back of the card??", "#", class: "cardForm__securitycode__details__hatena__link"
#card_token
= f.submit "sign up", id: "token_submit", url: cards_path, method: :post
Next, create a JS file corresponding to the above view file.
$(document).on('turbolinks:load', function() {
$(function() {
Payjp.setPublicKey(gon.payjpPublicKey);
$("#token_submit").on('click', function(e){
e.preventDefault();
let card = {
number: $('#card_number').val(),
cvc:$('#cvc').val(),
exp_month: $('#exp_month').val(),
exp_year: $('#exp_year').val()
};
Payjp.createToken(card, function(status, response) {
if (response.error) {
$("#token_submit").prop('disabled', false);
alert("The card information is incorrect.");
}
else {
$("#card_number").removeAttr("name");
$("#cvc").removeAttr("name");
$("#exp_month").removeAttr("name");
$("#exp_year").removeAttr("name");
let token = response.id;
$("#form").append(`<input type="hidden" name="payjpToken" value=${token}>`);
$("#form").get(0).submit();
alert("Registration has been completed");
}
});
});
});
});
If you press the register button (= f.submit "submit", id: "token_submit") in the view file, JS will fire. Token creation is done with a method called Payjp.createToken.
With the above, card registration is complete.
Finally, I will briefly describe the delete function. I installed a delete button in show.html.haml that I call with show action as follows.
.payment
.payment__content
.payment__content__title
Card information
.payment__content__box
.payment__content__box__cardImage
= image_tag "cards/#{@card_src}", width: 40, height: 28
.payment__content__box__details
.payment__content__box__details__cardNumber
= "card number:**** **** **** " + @card_info.last4
.payment__content____boxdetails__cardMMYY
= "expiration date:" + @exp_month + " / " + @exp_year
.payment__cardDelete
= button_to "delete", card_path(@card.id), {method: :delete, id: 'charge-form', name: "inputForm", class: "payment__cardDelete__deleteBtn"}
Next time, I will describe the payment function with the registered card.
Recommended Posts