[Rails] I will write the mechanism and method of using payjp (API) very carefully (Credit card registration)

Introduction

While making a flea market app for school assignments, there was an implementation of a credit card registration / payment function using payjp. If you search, you can find the code because many previous graduates have written articles, but what are you doing? After all, it was best to read the official reference, so I will describe what I understood.

In this article ** What does it mean to use payjp in the first place? (API description) ** ** How to call payjp ** ** Implementation of credit card registration ** ** Implementation of payment by registered credit card **

I will describe the above.

environment

ruby 2.6.5 rails 6.0.3

What does it mean to use payjp in the first place?

Even if I write the code, I think that understanding here is important as a first step (I did not mention much here)

payjp is a ** API type **. There are various explanations about APIs if you google, but it feels like ** "I will provide some software functions to the outside" **.

It is a mechanism that you can use some of the functions of the software in your application by calling the API successfully according to the instructions of the side who prepared it.

To give a concrete example, there is a Google Map service, for example. By using the API provided by google and writing the code according to the instructions, it is possible to display the google map on your application.

** payjp is also an API related to Creca, and you can use it to register and make payments with your credit card. ** **

When using the API, what I personally think is important is to ** understand the flow of having the other party prepare it **.

Considering the implementation flow after recognizing the above, ** Prepare to call the API in the way you prepared ** ** Interact with and process the application over there in the way you have prepared **

It will be like that. This image is important because it makes it easier to understand what the subsequent work is doing.

Therefore, the most appropriate approach is to ** understand how to do it with the official documentation of the person who prepared it **. It's like reading the official manual.

How to call payjp

As a prerequisite, let's register with payjp and get the test public key and test private key. This is the key required for API authentication. The payjp side has these and judges that we are "users who are allowed to use".

After registering, read Official Reference for v1. The script that should be embedded as soon as you access it is properly described. Also, if you read a little further down, it also describes how to authenticate with a public key. We will see the actual description in the next section.

Implementation of credit card registration

Based on the above calling method, I will put the actual code first. After that, I will explain the flow.

haml:application.html.haml


!!!
%html
  %head
    %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
    %script{src: "https://js.pay.jp/v1/", type: "text/javascript"}
    = csrf_meta_tags
    = csp_meta_tag
    = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
    = javascript_include_tag 'application'

haml:new.html.haml


    .creditCreate
      .creditCreate__title
        %h1 Credit card information input
      .creditForm
        = form_with model: @credit, method: :post, id: "cardCreateForm" do |f|
          .creditForm__numberfield
            %label(for="cardnumber-input")card number
            %span.creditPoint required
            %br
            = f.text_field :card_number, type: "text", class: 'cardnumber-input', id:'card-number', placeholder: "Half-width numbers only", maxlength: 16
            .creditslist
              = image_tag "jcb.gif", class:"creditsIcon"
              = image_tag "visa.gif", class:"creditsIcon"
              = image_tag "master.gif", class:"creditsIcon"
              = image_tag "amex.gif", class:"creditsIcon"
          .creditForm__datefield
            %label expiration date
            %span.creditPoint required
            %br
            = f.select :exp_month, [["01",1],["02",2],["03",3],["04",4],["05",5],["06",6],["07",7],["08",8],["09",9],["10",10],["11",11],["12",12]],{} , class: 'dateSelect', name: 'exp_month'
Month
            = f.select :exp_year, [["20",2020],["21",2021],["22",2022],["23",2023],["24",2024],["25",2025],["26",2026],["27",2027],["28",2028],["29",2029]],{} , class: 'dateSelect', name: 'exp_year'
Year
          .creditForm__securityfield
            %label security code
            %span.creditPoint required
            %br
            = f.text_field :cvc, type: 'text', class: 'securityInput', id: 'cvc', placeholder: '4-digit or 3-digit number on the back of the card', maxlength: "4"
          #card_token.creditForm__submitfield
            = f.submit 'to add', class: 'creditsSubmit', id: 'token_submit'

payjp.js


$(function() {
  $('#cardCreateForm').on('submit', function(e) {
    e.preventDefault()
    Payjp.setPublicKey(['PAYJP_PUBLIC_KEY']);
    var card = {
      number: document.getElementById("card-number").value,
      exp_month: document.getElementById("credit_exp_month").value,
      exp_year: document.getElementById("credit_exp_year").value,
      cvc: document.getElementById("cvc").value
    };
    if (card.number == "" || card.cvc == "") {
      alert("There is an input omission");
    } else {
      Payjp.createToken(card, function(status, response) {
        if (status === 200 ) {
          $("#card_number").removeAttr("name");
          $("#cvc").removeAttr("name");
          $("#exp_month").removeAttr("name");
          $("#exp_year").removeAttr("name");
          $("#card_token").append(
            $('<input type="hidden" name="payjp-token">').val(response.id)
          );
          $('#cardCreateForm').get(0).submit();
          alert("Registration was successful");
        } else {
          alert("Card information is incorrect");
        }
      });
    }
  });
});

credits_controller.rb



require 'payjp'

def create
    Payjp.api_key =  ENV['PAYJP_SECRET_KEY']
    if params['payjp-token'].blank?
      render :new
    else
      customer = Payjp::Customer.create(
        email: current_user.email,
        card: params['payjp-token'],
        metadata: {user_id: current_user.id}
      )
      @credit = Credit.new(user_id: current_user.id, customer_id: customer.id, card_id: customer.default_card)
      if @credit.save
        redirect_to user_path(current_user.id)
      else
        render :new
      end
    end
  end

The above is the code for credit card registration. The processing flow is as follows. I will explain along this flow.

** ① Make settings using the payjp API ** ** ② In the js file, prepare for registration by linking the input contents and pajyp when submitting the form ** ** ③ Skip the action to the controller, generate customer data on payjp, and save the id associated with customer data in the table **

① Make settings that use the payjp API

** Let's look at application.html.haml **

haml:application.html.haml


!!!
%html
  %head
    %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
    %script{src: "https://js.pay.jp/v1/", type: "text/javascript"}
    = csrf_meta_tags
    = csp_meta_tag
    = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
    = javascript_include_tag 'application'

The script part is the description that calls payjp. How to call is described in Official Reference. Just post it. If you look at the formula, you just post it.

(2) In the js file, prepare for registration by linking the input contents and pajyp when submitting the form.

** Let's take a look at the js file. ** ** Since the description corresponds to the haml file of the form submission, it will be easier to understand if you arrange them side by side.

payjp.js


$(function() {
  $('#cardCreateForm').on('submit', function(e) {
    e.preventDefault()
    Payjp.setPublicKey(['PAYJP_PUBLIC_KEY']);
    var card = {
      number: document.getElementById("card-number").value,
      exp_month: document.getElementById("credit_exp_month").value,
      exp_year: document.getElementById("credit_exp_year").value,
      cvc: document.getElementById("cvc").value
    };
    if (card.number == "" || card.cvc == "") {
      alert("There is an input omission");
    } else {
      Payjp.createToken(card, function(status, response) {
        if (status === 200 ) {
          $("#card_number").removeAttr("name");
          $("#cvc").removeAttr("name");
          $("#exp_month").removeAttr("name");
          $("#exp_year").removeAttr("name");
          $("#card_token").append(
            $('<input type="hidden" name="payjp-token">').val(response.id)
          );
          $('#cardCreateForm').get(0).submit();
          alert("Registration was successful");
        } else {
          alert("Card information is incorrect");
        }
      });
    }
  });
});

I will explain it separately.

payjp.js


$(function() {
  $('#cardCreateForm').on('submit', function(e) {
    e.preventDefault()

In jQuery, it is defined as the behavior when the form button is clicked.

payjp.js


Payjp.setPublicKey(['PAYJP_PUBLIC_KEY']);

Set the registered and obtained test public key to show the authentication (that you have registered to use payjp!). With this, payjp will allow you to use it.

The way to write setPublicKey suddenly appears, but this is also described by the method specified in Official Reference. It's just a copy. Don't be afraid.

payjp.js


    var card = {
      number: document.getElementById("card-number").value,
      exp_month: document.getElementById("credit_exp_month").value,
      exp_year: document.getElementById("credit_exp_year").value,
      cvc: document.getElementById("cvc").value
    };

It is a description of JS. As you can see in the way of writing get element by id, by specifying and specifying the id of each form field, each input content is defined and assigned to a variable called card.

payjp.js


    if (card.number == "" || card.cvc == "") {
      alert("There is an input omission");

It is a description of js. When number or cvc of the defined variable card is empty, it does not accept and returns an error.

payjp.js


    } else {
      Payjp.createToken(card, function(status, response) {
        if (status === 200 ) {
          $("#card_number").removeAttr("name");
          $("#cvc").removeAttr("name");
          $("#exp_month").removeAttr("name");
          $("#exp_year").removeAttr("name");
          $("#card_token").append(
            $('<input type="hidden" name="payjp-token">').val(response.id)
          );
          $('#cardCreateForm').get(0).submit();
          alert("Registration was successful");
        } else {
          alert("Card information is incorrect");
        }
      });
    }
  });
});

Generate a token based on the card information. This is also [Official reference of payjp API](https://pay.jp/docs/api/#%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3% Please read 82% 92% E4% BD% 9C% E6% 88% 90).

If you read the part of generating tokens, you should see a textual explanation of how tokens are generated and the actual description method on the right side.

Suddenly, the description "Payjp.create ~" came out, but this is just the reference saying "If you write this way, you can do it", so if you read it properly, you can just do it. Since it is written that it is necessary to pass 4 values such as number here, I defined 4 values in the card variable earlier. In order, it is defined because it is needed here.

if status === 200 is the status value returned by payjp when the card information is valid and the token is generated correctly. Therefore, returning an error with else indicates "when registration was not successful".

Let's take a closer look from here.

payjp.js



          $("#card_number").removeAttr("name");
          $("#cvc").removeAttr("name");
          $("#exp_month").removeAttr("name");
          $("#exp_year").removeAttr("name");

It is a description of JS and jQuery. If the card is valid, the value entered in each form is removed from the viewpoint of security (the process is to remove the name attribute of the form specified by id).

payjp.js



          $("#card_token").append(
            $('<input type="hidden" name="payjp-token">').val(response.id)
          );
          $('#cardCreateForm').get(0).submit();
          alert("Registration was successful");

The data (response.id) returned from Payjp when the card is valid is returned to the form by append. By specifying type = hidden, the data is sent to the form, although it is invisible to the user.

On top of that, by submitting, the data returned from Payjp is sent to the controller after that, and the create action is performed.

It's a description for successfully sending data to the controller. With this, you can look at the processing of the controller next.

③ Skip the action to the controller and generate customer data on payjp ・ Save the id associated with customer data in the table

credits_controller.rb



require 'payjp'

def create
    Payjp.api_key =  ENV['PAYJP_SECRET_KEY']
    if params['payjp-token'].blank?
      render :new
    else
      customer = Payjp::Customer.create(
        email: current_user.email,
        card: params['payjp-token'],
        metadata: {user_id: current_user.id}
      )
      @credit = Credit.new(user_id: current_user.id, customer_id: customer.id, card_id: customer.default_card)
      if @credit.save
        redirect_to user_path(current_user.id)
      else
        render :new
      end
    end
  end

The process can be done well so far, and finally the data is created by the controller. Let's go step by step.

credits_controller.rb



require 'payjp'

def create
    Payjp.api_key =  ENV['PAYJP_SECRET_KEY']
    if params['payjp-token'].blank?
      render :new

First, enable payjp with require'payjp'. Next, specify SECRET_KEY to indicate that this is the user who thought the right to use payjp (listed using environment variables).

On top of that, the data sent when the response was normal earlier is params ['payjp-token'], so if that is the case, the process to render without processing is described.

credits_controller.rb



def create
    
    else
      customer = Payjp::Customer.create(
        email: current_user.email,
        card: params['payjp-token'],
        metadata: {user_id: current_user.id} #This is optional
      )

Here, Payjp customer data is created and assigned to the variable customer. Suddenly this way of writing came out. As I say many times, [Official Reference](https://pay.jp/docs/api/#%E9%A1%A7%E5%AE%A2%E3%82%92%E4%BD%9C% If you look at E6% 88% 90), you can see that everything is bought.

This time I want to create customer data, so look at the customer data column in the reference. Then, the description method of Payjp :: Customer.create and its arguments should be written.

If you look at the contents of the arguments, you should be able to easily understand what to put. ** For example, the argument card says to specify the token ID, so you can see that you should set the token ID that you set to send earlier. ** **

credits_controller.rb



def create

      @credit = Credit.new(user_id: current_user.id, customer_id: customer.id, card_id: customer.default_card)
      if @credit.save
        redirect_to user_path(current_user.id)
      else
        render :new
      end
end

From here, we will talk about table processing on the Rails side. You can also check the response data of Payjp customers by referring to the reference above. Since the Payjp customer data created earlier was assigned to the variable customer, the response data can be saved in the table by specifying it with customer.id etc.

(Because it is not legally possible to save the Creca data in your own table, have the payjp side save the data itself, and save the customer_id and card data in the table as a link to call the data. That's why.

At the end

It's quite a long sentence, so I'll just register it, but the important thing is to ** understand the flow and read the reference **, and I think that's all.

Regarding the flow of card payment based on this registered data, what kind of description is required for payment while reading the reference? What to specify for the argument? I was able to implement it rather easily by looking at it. (As you can see from the fact that it was linked to the table earlier, it is not so difficult to imagine that you should call the card data required for payment and pass it as an argument.)

However, I was not familiar with the API, so it was implemented with great help from Qiita's article. However, the conclusion ** The formula is the strongest **, and it was an experience that I could understand this word with a real feeling, so I will record it.

Recommended Posts

[Rails] I will write the mechanism and method of using payjp (API) very carefully (Credit card registration)
Using PAY.JP API with Rails ~ Card Registration ~ (payjp.js v2)
[Rails] I will explain the implementation procedure of the follow function using form_with.
[Rails] Implement credit card registration / deletion function in PAY.JP
Let's introduce the credit card function using payjp (preparation)
[Rails] I tried using the button_to method for the first time
Rails API mode I tried to implement the keyword multiple search function using arrays and iterative processing.
[Rails 6] destroy using the resources method
I want to pass the argument of Annotation and the argument of the calling method to aspect
[Rails] Implement the product purchase function with a credit card registered with PAY.JP
[Rails] I studied the difference between new method, save method, build method and create method.