** Use gem ** devise PAY.JP for Ruby Gon gem
However, it seems that it is prohibited by law for EC site operators to have card numbers. Therefore, such information cannot be registered in the DB. Instead, you will need to register your payjp ID.
** Card registration flow ** ・ The end user enters the card number etc. and clicks submit ・ Data will be sent to payjp and the card token will be returned. ・ Create a customer (owner) from the returned card token ・ Register customer ID and card ID in DB from created customer information
In my case, the association is
user has_one :card
card belongs_to :user
cards table
migrate/***create_cards.rb
t.references :user, foreign_key: true,index:{unique: true}
t.string :customer_id, null: false
t.string :card_id, null: false
Now, let's add the following to the Gemfile and bundle install.
Gemfile
gem 'payjp'
gem 'gon'
** Here is gon ** It is a gem that can pass ruby variables to JQuery (javascript).
** It may be wrong, but ** I think that css and JQuery (javascript) can be seen by the verification tool of the browser. So, I think it's a good idea to embed the key directly in JQuery (javascript). A test key may be fine, but you can't use a test key in production. There is also a way to write it in credentials.yml, but since personal keys are personal, I think it is desirable to manage them individually.
After the install is finished, you can use gon. application.html.haml Added above javascript_include_tag
ruby:app/views/layouts/application.html.haml
= include_gon
= javascript_include_tag 'application'
app/controllers/card_controller.rb
def new
gon.api_key = ENV["PAYJP_PUBLIC_KEY"]
end
Because there is no need to load card-related js on every page This time I wrote the following directly in card / new.html.haml.
ruby:app/views/card/new.html.haml
:javascript
let payjp = Payjp(gon.api_key);
Now that the environment variables are being read by JQuery, it seems that you don't have to expose the key. If you are co-developing, register each person who needs it in payjp and write it in the environment variable.
Since the new action is made above, create a registration form.
javascript:app/views/card/new.html.haml
= form_with url: card_index_path,local:true,id:"cardNew__form" do |f| //card_index_path is the path to the card controller's create action
%input#cardNew__form--inputHidden{type:"hidden",name:"payjp-token"}
#cardNew__form--input
#cardNew__form--errMessage
= f.submit "to register",id:"cardNew__form--submit"
%script{src: "https://js.pay.jp/v2/pay.js", type: "text/javascript"}
:javascript //Here is JQuery.
let payjp = Payjp(gon.api_key); //This is gon.
let elements = payjp.elements();
let cardElement = elements.create('card');
cardElement.mount('#cardNew__form--input');
//Up to this point, the input fields prepared by PAYJP have been created. One point of the card is cute so I will use it as it is^^
$(".cardNew").on("click","#cardNew__form--submit",
function(e){
e.preventDefault();
payjp.createToken(cardElement).then(function(response){ //Here, send the card information to payjp and get the card token.
if(response.error){
$('#cardNew__form--errMessage').text(response.error.message);
}else{
$("#cardNew__form--inputHidden").attr("value",response.id); //response.id is the card token. Here input name="payjp-token"Give the value to.
$("#cardNew__form").submit();
}
});
}
);
Card number: 424242424242 ↑ The expiration date is in the future, and CVC seems to be anything. Test card
Now press submit and the token will be sent to the create action. card_index_path is the path to the create action. If you don't see it well, please adjust it with CSS.
app/controllers/card_controller.rb
def create
if params["payjp-token"].blank?
render :new
else
Payjp.api_key = ENV["PAYJP_SECRET_KEY"]
customer = Payjp::Customer.create( #Here, create a customer who is associated with the card token.
card: params["payjp-token"]
)
@card = Card.new(
user_id: current_user.id,
customer_id: customer.id, #This is the customer's id.
card_id: customer.default_card #This is the id of the card associated with the customer.
)
if @card.save
redirect_to new_card_path
else
render :new
end
end
end
I think that you can register the card in the DB up to this point. If you want to make it easier, go to here
=form_with url: card_index_path,local:true,id:"new_form" do |f|
%script{src:"https://checkout.pay.jp/",class:"payjp-button","data-key":"#{@api_key}"}
Copy and paste to the empty data-key to your public key Create @ape_key with new controller. The url is the same as before. id is arbitrary. I haven't tried class so I don't know. As for the controller, the new one is gon specification, so
@api_key = ENV["PAYJP_PUBLIC_KEY"]
I think it's OK if you set it to. It seems to send a token as the value of the payjp-token parameter. With this alone, it seems that a high-quality card input screen is completed. It's amazing.
Reference primary source PAY.JP Announcement payjp.js v2 reference payjp.js v2 Guide Create a customer
Reference blog [Rails] I briefly summarized the procedure for implementing the payment function using PAYJP Implementing credit card registration and deletion function with Payjp (Rails)
As a flow
Click to purchase on the product details page Confirm purchase on the purchase confirmation page Credit card payment Create a payment ID and who bought it in the payments table Reduce the stock field in the items table and update
association
item has_many :payments
payment belogs_to :item
payments table
***create_payments.rb
t.string :charge_id, null: false
t.references :user, foreign_key: true
t.references :item, foreign_key: true
t.integer :quantity, null: false
haml:views/payments/new.html.haml
= form_with url:item_payments_path,local:true,id:"new_form" do |f|
= f.number_field :quantity
= f.submit "To buy" #I'm afraid of something, so I try not to skip data such as the amount of money. Only the number of purchases.
payments_controller.rb
def create
item = Item.find(params[:item_id]) #Nest the routing to include the id of the item.
item.stock -= params[:quantity].to_i #Subtract the number of purchases from the item stock. All you have to do is update
payment = Payment.new(payment_params)
Payjp.api_key = ENV["PAYJP_SECRET_KEY"]
charge = Payjp::Charge.create( #Settle here.
amount: item.price.to_i * params[:quantity].to_i, #Settlement amount
customer: Card.find_by(user_id: current_user.id).customer_id, #Customer
currency: 'jpy' #Japanese yen
)
payment.charge_id = charge.id #I put the payment ID in the payment instance I generated earlier. No need to separate. as you like^^
if payment.save && item.update(stock: item.stock)
redirect_to root_path
else
render "new"
end
end
private
def payment_params
params.permit(
:item_id,
:quantity,
).merge(user_id: current_user.id)
end
It is like this. Various parts have been omitted for the sake of clarity. If you don't know the routing nesting
config/routes.rb
resources :items do
resources :payments,only: [:new,:create]
end
It is like this.
I think this will probably work.
Reference primary source payjp.js API Guide [PAY.JP charge object](https://pay.jp/docs/api/#charge%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82 % AF% E3% 83% 88) ↑ I omitted it, but for those who want to display the last 4 digits of the card or VIZA (please use retrieve)
Reference blog Introduce a payment system using Payjp with Rails
In my case routing
config/routes.rb
resources :card,only: [:new,:create,:destroy]
card_index POST /card(.:format) card#create
new_card GET /card/new(.:format) card#new
card DELETE /card/:id(.:format) card#destroy
So I changed the above view. What has changed is from if to else at the top It is only the definition judgment of gon of JQuery.
view/card/new.html.haml
- if current_user.card
#cardNew__form
#cardNew__form--type= @card.brand.upcase
#cardNew__form--num= "**** **** **** #{@card.last4}"
#cardNew__form--name= @card.name
=link_to "Delete","/card/#{current_user.card.id}"
- else
= form_with url: card_index_path,local:true,id:"cardNew__form" do |f|
%input#cardNew__form--inputHidden{type:"hidden",name:"payjp-token"}
#cardNew__form--input
#cardNew__form--errMessage
= f.submit "to register",id:"cardNew__form--submit"
%script{src: "https://js.pay.jp/v2/pay.js", type: "text/javascript"}
:javascript
if(typeof gon != 'undefined'){
const style = {
base: {
backgroundColor:'#ffffff',
'::placeholder': {
color: '#bbbbbb',
}
},
invalid: {
color: 'red',
}
}
let payjp = Payjp(gon.api_key);
let elements = payjp.elements();
let cardElement = elements.create('card', {style: style});
cardElement.mount('#cardNew__form--input');
$(".cardNew").on("click","#cardNew__form--submit",
function(e){
e.preventDefault();
payjp.createToken(cardElement).then(function(response){
if(response.error){
$('#cardNew__form--errMessage').text(response.error.message);
}else{
$("#cardNew__form--inputHidden").attr("value",response.id);
$("#cardNew__form").submit();
}
});
}
);
}
card_controller.rb
def new
if current_user.card
Payjp.api_key = ENV["PAYJP_SECRET_KEY"]
customer = Payjp::Customer.retrieve(current_user.card.customer_id)
@card = customer.cards.retrieve(current_user.card.card_id)
else
gon.api_key = ENV["PAYJP_PUBLIC_KEY"]
end
end
Recommended Posts