LaunchSchool - An Online School for Developers /

Blog

Stripe Payments in Rails, Part 2 - Use Custom Forms With Stripe.js

This is the second article of the “Process Payments with Stripe in Rails” tutorial series:

Stripe.js is a javascript library that allows us to integrate Stripe payments with our own forms so we can control the look and feel. Like all Stripe solutions, the customer’s credit card information won’t flow through our server so we don’t have to worry about PCI Compliance.

We will continue to use our “Cookery School” Rails example to demonstrate how custom forms work with Stripe.js. For this tutorial, we will work on the cards branch:

1
2
3
4
5
6
7
8
git clone -b cards git@github.com:gotealeaf/stripe-basics.git
cd stripe-basics
rake db:drop
rake db:create
rake db:migrate
rake db:seed
rails s
open http://localhost:3000

Make sure you follow the first steps in Stripe Payments in Rails, Part 1 – Stripe Checkout:

  • Step 1. Create Stripe Account
  • Step 2. Configure project

Step 1. Create the registration form

In our registration form app/views/registrations/new.html.haml we should add the javascript library, card info inputs and the form submit button:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
      = javascript_include_tag "https://js.stripe.com/v2/"
      :javascript
        Stripe.setPublishableKey('#{Rails.application.secrets.stripe_publishable_key}');

      = label_tag "Card Number", nil, required: true
      .control-group
        .controls
          = text_field_tag :card_number, nil, class: "input-block-level", "data-stripe" => "number"

      = label_tag "Card Verification", nil, required: true
      .control-group
        .controls
          = text_field_tag :card_verification, nil, class: "input-block-level", "data-stripe" => "cvv"

      = label_tag "Card Expires", nil, required: true
      = select_tag :exp_month, options_for_select(Date::MONTHNAMES.compact.each_with_index.map { |name,i| ["#{i+1} - #{name}", i+1] }), include_blank: false, "data-stripe" => "exp-month", class: "span2"
      = select_tag :exp_year, options_for_select((Date.today.year..(Date.today.year+10)).to_a), include_blank: false, "data-stripe" => "exp-year", class: "span1"

      .actions
        = f.submit "Registration Payment", class: "btn", style: "color: white;background: rgb(242, 118, 73);"

‘Stripe.js’ will recognize the card data because we have marked the inputs with ‘data-stripe’ attribute as: number, cvv, exp-month and exp-year.

Step 2. Prepare jQuery triggers to send credit card info to Stripe

We’ll write some javascript to capture the submit event of the form to send the card data to Stripe’s server.

app/assets/javascripts/credit_card_form.js

1
2
3
4
5
6
7
8
9
jQuery(function ($) {
  var show_error, stripeResponseHandler;
  $("#new_registration").submit(function (event) {
    var $form;
    $form = $(this);
    $form.find("input[type=submit]").prop("disabled", true);
    Stripe.card.createToken($form, stripeResponseHandler);
    return false;
  });

The response from Stripe contains a token that we will insert into our form to be submitted to our own server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  stripeResponseHandler = function (status, response) {
    var $form, token;
    $form = $("#new_registration");
    if (response.error) {
      show_error(response.error.message);
      $form.find("input[type=submit]").prop("disabled", false);
    } else {
      token = response.id;
      $form.append($("<input type=\"hidden\" name=\"registration[card_token]\" />").val(token));
      $("[data-stripe=number]").remove();
      $("[data-stripe=cvv]").remove();
      $("[data-stripe=exp-year]").remove();
      $("[data-stripe=exp-month]").remove();
      $form.get(0).submit();
    }
    return false;
  };

  show_error = function (message) {
    $("#flash-messages").html('<div class="alert alert-warning"><a class="close" data-dismiss="alert">×</a><div id="flash_alert">' + message + '</div></div>');
    $('.alert').delay(5000).fadeOut(3000);
    return false;
  };
});

Very importantly, we removed all card info from the form before we submit this form to our own server to satisfy PCI Compliance requirements.

Step 3. Process payment in the controller

Based on the controller from the previous tutorial we just need to have one fix: include email and card_token in our permitted params:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  def create
    @registration = Registration.new registration_params.merge(email: stripe_params["stripeEmail"],
                                                               card_token: stripe_params["stripeToken"])
    raise "Please, check registration errors" unless @registration.valid?
    @registration.process_payment
    @registration.save
    redirect_to @registration, notice: 'Registration was successfully created.'
  rescue Exception => e
    flash[:error] = e.message
    render :new
  end

  private

  def registration_params
    params.require(:registration).permit(:course_id, :full_name, :company, :telephone, :email, :card_token)
  end

Test this feature

Select a course, fill the form and press registration button:

If all is OK you are redirected to the acceptance page:

Finally you can check the payment in your Stripe Dashboard:

Conclusion

With Stripe.js, we can use our own custom payment form to control the look and feel. It requires us to write a few extra lines of code but Stripe makes it easy to process the payments. In the next tutorial Stripe Payments in Rails, Part 3 – Recurring Payments with Stripe, we will look at how to process recurring subscription payments with Stripe.

For further information on working with Stripe.js, take a look at Stripe.js documentation.