We have implemented the credit card registration function required when purchasing products using payjp. Keep a memo of the procedure from customizing the view to registering it.
・ Rails 5.2.4.2 ・ Ruby 2.5.1
https://pay.jp/docs/started Simply put, it is a service that handles credit card registration and payment on your behalf. Helps implement the feature by using the payjp gem.
By the way, it is prohibited to save the card information itself in the DB. By calling the information stored in payjp with the customer id or card id, it is a mechanism to respond to information acquisition and payment. The point is that the card and customer information created by payjp and the associated id are passed, so save that id in the DB and refer to it from payjp. I think that is the image. In terms of security, it is extremely dangerous to store credit card information in the DB, so I think it is in this form. (See URL below) http://payjp-announce.hatenablog.com/entry/2017/11/10/182738
https://pay.jp/
After registering and logging in, click on the API page to see your test private and test public keys.
gem 'payjp'
gem 'dotenv-rails'
Make pay.jp and dotenv available. dotenv is a gem that allows environment variables to be read in a file. dotenv, there is a process that you have to read the environment variables later, so add it first. After adding, bundle install.
After the install is complete, create an .env file in your home directory.
Enter the private and public keys you confirmed earlier in the .env file.PAYJP_PRIVATE_KEY ='sk_test_****************:'
PAYJP_KEY ='pk_test_************************'
If important information such as payjp key is leaked, it is dangerous, so add the following description to the .gitgnore file so that it will not go up to git.
/.env
You can also enter the code directly in the environment variable. https://qiita.com/daisukeshimizu/items/c01f29f8398cc7f5c396
%script{src: "https://js.pay.jp/", type: "text/javascript"}
Add this sentence so that payjp.js can be read by the head tag.
!!!
%html
%head
%meta{content: "text/html; charset=UTF-8", "http-equiv": "Content-Type"}/
%title EcApp
%script{src: "https://js.pay.jp/", type: "text/javascript"}
= csrf_meta_tags
= csp_meta_tag
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
= javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
%body
= yield
payjp.js is a library that specializes only in tokenizing card information. Since the payjp.js library is hosted at https://js.pay.jp, it will load and use this domain, so it will not work locally. By reading this, you will be able to use methods such as token acquisition from payjp.
<script type = "text / javascript" src = "https://checkout.pay.jp" class = "payjp-button" data-key = "public key"> </ script>
There is also a way to add one line of script tag and use the view prepared by payjp in advance, but this time I want to customize the view, so I will omit it.
Although it is in the process of being created, it will look like this.= render partial: "items/header"
.main
% h2 New member registration
= form_with url: creditcards_path, method: :post, id: 'charge-form', html: { name: "inputForm" } do |f|
.main__form
.main__form__registration
Credit card registration page
.main__form__field
% label.main__form__field__label Card number
= f.text_field: number, name: "number", id: "card_number", type: "text", placeholder:'half-width numbers only', class:'main__form__field__box', maxlength: "16"
=image_tag "material/credit/credit.png ", class:"main__form__field__credit-image"
.main__form__field2
% label.main__form__field__label Expiration date
= f.select :exp_month, [["--","--"],["01",1],["02",02],["03",03],["04",04],["05",05],["06",6],["07",07],["08",8],["09",9],["10",10],["11",11],["12",12]],{}, class: 'credit-box', name: "exp_month", id:"exp_month"
% span.month month
= f.select :exp_year, [["--","--"],["2019",2019],["2020",2020],["2021",2021],["2022",2022],["2023",2023],["2024",2024],["2025",2025],["2026",2026],["2027",2027],["2028",2028],["2029",2029]], {}, class: 'credit-box', name: "exp_year", id:"exp_year"
% span.year year
.main__form__field
% label.main__form__field__label security code
= f.text_field: cvc, name: "cvc", id: "cvc", type: "text", placeholder:'4 or 3 digit number on the back of the card', class: "main__form__field__box", maxlength: "4"
.main__form__field
= f.submit'Register', id: "token_submit", class: "main__form__field__submit-btn"
After this, the value received in the form with payjp.js is assigned to [payjpToken] as token data, and the data is sent to payjp, so It is mandatory to specify id and name attributes for form elements other than form and submit button, respectively.
Since jQuery is used, please set it if it is not set. https://qiita.com/ngron/items/95846bd630a723e00038 I think this article will be helpful.
Once you can use jQuery In the payjp.js file, create token data to be sent to payjp, and use that token as a key to register credit card information. After having your credit card information entered, we will implement it using Javascript to create a "token" based on that information.
$(document).on('turbolinks:load', function() {
var form = $ ("# charge-form"); // Assign the one with id ”charge-form” to form.
Payjp.setPublicKey ('pk_test_839895c840d4f91f7e75df7e'); // Write the public key directly so that you can refer to it.
$ (document) .on ("click", "# token_submit", function (e) {// Works when e is pressed.
e.preventDefault (); // First, stop the rails processing and perform the js processing first.
form.find("input[type=submit]").prop("disabled", true);
var card = {// Get the credit card information entered in the card variable based on the id and assign it to the card variable. ..
number: $("#card_number").val(),
cvc: $("#cvc").val(),
exp_month: $("#exp_month").val(),
exp_year: $("#exp_year").val(),
};
Payjp.createToken (card, function (s, response) {// Generate token. The previous card information is returned as an encrypted token.
if (response.error) {// If the value is an error
alert ('Incorrect card information');
}
else {// if no error occurs
$("#card_number").removeAttr("name");
$("#cvc").removeAttr("name");
$("#exp_month").removeAttr("name");
$ ("# exp_year"). removeAttr ("name"); // Delete the value because it is not saved in the DB.
var token = response.id;
alert ("Registration completed");
form.append ($ ('<input type = "hidden" name = "payjpToken" />'). val (token)); // Since the token is saved in db, insert the token created by js into form There is.
form.get (0) .submit (); // gets the data inserted earlier in form.
}
});
});
});
https://payjp.hatenablog.com/entry/2017/12/05/134933 Arranged by referring to the sample of payjp.js provided by pay.jp.
Payjp.setPublicKey('pk_test_xxxxxxxxxxxx'); If you do not describe this, communication with the Payjp server will not be performed and the token will not be issued, so enter the API key you confirmed earlier.
class CreateCards < ActiveRecord::Migration[5.2]
def change
create_table :cards do |t|
t.references :user, foreign_key: true, null: false
t.string :customer_id, null: false
t.string :card_id, null: false
t.timestamps
end
end
end
This is a table for creating a credit_card model and saving PAY.JP information. -User_id ... id of User table ・ Customer_id ... customer id of payjp ・ Card_id ... the id of the default card for payjp Make it a name corresponding to the column sent from payjp so that it can be registered in the DB.
class CreditCard < ApplicationRecord
#association
belongs_to :user
#Validation
validates :user_id, :customer_id, :card_id, presence: true
end
Rails.application.routes.draw do
#devise routing
devise_for :users, controllers: {
registrations: 'users/registrations'
}
devise_scope :user do
post 'users/sign_up', to: 'users/registrations#create'
get 'addresses', to: 'users/registrations#new_address'
post 'addresses', to: 'users/registrations#create_address'
get 'creditcards', to: 'users/registrations#new_credit_card'
post 'creditcards', to: 'users/registrations#pay'
end
I want to register in the same flow as new registration, so I put it together in the registrations controller. Please note that the description will change when creating a credit_card controller.
class Users::RegistrationsController < Devise::RegistrationsController
def create_address
@user = User.new(session["devise.regist_data"]["user"])
@address = Address.new(address_params)
unless @address.valid?
flash.now[:alert] = @address.errors.full_messages
render :new_address and return
end
@user.addresses.build(@address.attributes)
@user.save
session["devise.regist_data"]["user"].clear
sign_in(:user, @user)
redirect_to creditcards_path # Jump to new action for credit card registration
end
def new_credit_card
card = CreditCard.where(user_id: current_user.id)
#Substitute the hash containing the id of the logged-in user in the user_id column of the credit card in the card.
end
require "payjp" #Allow to get API key.
def pay
Payjp.api_key = ENV ["PAYJP_PRIVATE_KEY"] We are authenticating with the private key to get the value sent from #payjp.
if params['payjpToken'].blank?
render: new_credit_card If the payjpToken created by #JS is from, try again.
else
customer = Payjp :: Customer.create (card: params ['payjpToken'],) The acquired value is assigned to the #customer variable.
@creditcard = CreditCard.new(user_id: current_user.id, customer_id: customer.id, card_id: customer.default_card)
if @creditcard.save
redirect_to root_path
else
redirect_to action: "new_credit_card"
end
end
end
Since the official article was easy to understand for getting the value from pay.jp, I referred to it. https://pay.jp/docs/api/?ruby#payjp-api
After registering the address in the wizard format, I am redirect_toing the credit card registration, so I am filling in from the create_address method. I also posted a wizard-style implementation of the registration function using devise in the article, so I hope you find it helpful. https://qiita.com/Nosuke0808/items/00a8cac860abd68e2688
As an aside, when I implemented it, I used render in the wizard format implementation, but I thought it would be better to switch screens with redirect_to. Currently, if you reload the page while registering the address, a routing error will occur. The appearance of the user is the same whether it is render or redirect_to, and if it reloads and an error occurs, it will be useless, so when each registration is completed, save it in the DB and redirect to the next page. I felt that it was good. I'm looking for a way to avoid errors when reloading using render in the future, but I plan to rewrite it to redirect_to.
This completes the registration of the credit card using payjp. After implementing, the data created by js could not be sent to pay.jp, and the data acquisition from pay.jp did not go well, so it took a long time to implement. If you can understand it, it will not be difficult, so I hope it will be helpful for those who are going to implement payjp from now on.
https://techtechmedia.com/payjp-rails/ https://pay.jp/docs/started http://payjp-announce.hatenablog.com/entry/2017/11/10/182738 https://qiita.com/daisukeshimizu/items/c01f29f8398cc7f5c396 https://payjp.hatenablog.com/entry/2017/12/05/134933 https://pay.jp/docs/api/?ruby#payjp-api
Recommended Posts