August 18, 2016

How to take a payment with Rails and Stripe

Taking payments with your web application used to be a difficult if not downright daunting process. Merchant accounts, held to the whims of banks, or worse, Paypal who could just freeze your account if they decided that you are, well, anything at all really.

We however now live in "the future", and whilst we have not received flying cars, we have received Stripe, and as such taking payments from your Rails app has gotten a whole lot easier. But how does it work? After you have headed over to Stripe, how do you take a payment from a real live customer who wants to give you money???

In order to take payments from cards for you, but without you having to store physical card details, Stripe takes card information from your website and returns you a token that represents a physical card. This is a big plus as it means you don't have to store any card information in your application.

Stripe.js

In order to do this you need to use Stripe's Javascript library, stripe.js. First of all you need to include this in your application. Normally we would look to include this kind of thing in the asset pipeline, but with Stripe we don't want to do this, we want to include it directly from Stripe, this way any updates that they may make to their library will be included for you right away, and with something as important as taking credit card information, this is really really important. So let's include the Stripe javascript library in our application.

<head>
  <title>My Awesome Website</title>
  <%= stylesheet_link_tag 'application' %>
  <script type="text/javascript" src="https://js.stripe.com/v2/"></script>
  <%= javascript_include_tag 'application' %>
  <%= csrf_meta_tags %>
</head>

Next in order for stripe.js to know that it's your Stripe account asking for a token, you need to include your Stripe public key in your javascript. Your Stripe public key is obtained in your account settings on their site, under api key, so go get your test publishable key and let's add it to your Javascript now.

<head>
  <title>My Awesome Website</title>
  <%= stylesheet_link_tag 'application' %>
  <script type="text/javascript" src="https://js.stripe.com/v2/"></script>
  <script type="text/javascript">
    Stripe.setPublishableKey('<%= STRIPE_PUBLIC_KEY %>');
  </script>
  <%= javascript_include_tag 'application' %>
  <%= csrf_meta_tags %>
</head>

As you can see I've just used a Ruby constant for this, which you can set in an initializer, config/initializers/stripe.rb

STRPIE_PUBLIC_KEY = ENV["STRIPE_PUBLIC_KEY"]
Obtaining a token

Now that we have the stuff we need to talk to Stripe and get a token, we need to actually capture the details for a card to make a payment. I'm going to look at the option of using a custom form to do this as I think it's the best way to integrate your site with Stripe but still be able to maintain control of your look and feel completely and have it just the way you want.

In order to do this we need to build a form that has all the information to be collected that you wish to store for your own app, as well as then the section of your form dedicated to transforming a customers credit card into a chargeable card token that you can use. The form that we will use to do that is like this:

<%= form_for @purchase do |f| %>
  ## Customer details removed for brevity
  <div class="payment-errors"></div>    
  <p>
    <%= label_tag "Card Number" %>
    <%= text_field_tag nil, nil, placeholder: "Please enter the number of your card", data: { stripe: "number" }, id: "card-number" %>
  </p>

  <p>
    <%= label_tag "Card Name" %>
    <%= text_field_tag nil, nil, placeholder: "Please enter the name on your card", data: { stripe: "name" }, id: "card-name" %>
  </p>

  <p>
    <%= label_tag "Amount" %>
    <%= text_field_tag nil, f.object.display_price, disabled: "disabled" %>
  </p>

  <p>
    <%= label_tag "Expiry Month" %>
    <%= text_field_tag nil, nil, placeholder: "MM", data: { stripe: "exp-month"}, id: "card-expiry-month" %>
  </p>

  <p>
    <%= label_tag "Expiry Year" %>
    <%= text_field_tag nil, nil, placeholder: "YYYY", data: { stripe: "exp-year"}, id: "card-expiry-year" %>
  </p>

  <p>
    <%= label_tag "CVC" %>
    <%= text_field_tag nil, nil, data: { stripe: "cvc" }, id: "card-cvc" %>
  </p>

  <p class="button">
    <%= f.submit "Pay Now" %>
  </p>

<% end %>

This is a pretty standard form except for a few key things. The card information itself is not attached to your Rails form object and also uses some Stripe data attributes instead of a name. This is how Stripe knows what to send to their service and try to grant you a token to charge the card. This means that the card information itself will never go to your application server and you don't have to worry about all the difficult parts of storing credit cards and complaince with PCI, Stripe takes care of all of this for you.

Now that we can capture all the information that we need to send to Stripe, how do we do that?

To get our cardholder information to Stripe, when the user submits their payment form to pay you, we need to intercept the form submission with Javascript and send the information to the Stripe service. Since this is likely a standard Rails app, I am just using some jQuery, you don't have to, Stripe doesn't require any particular Javascript library or frameworks, apart from stripe.js of course.

$(document).on("submit", "form#new_purchase", function(event){
  var form = $(this);

  form.find(".payment-errors").text("");

  form.find('button').prop('disabled', true);

  Stripe.card.createToken(form, stripeResponseHandler);

  return false;
});

So what's going on here?

The CSS id that I've attached this to is just the standard Rails stuff for a new form, it's clearing out any previous errors received from Stripe if there have been any, disabling the form button so that our customer can't accidentally try to pay us more than once, then it's calling Stripes createToken function with the form and a function to handle the response from Stripes service. So now we need to handle Stripe's response:

function stripeResponseHandler(status, response) {
  var paymentForm = $('#new_purchase');

  if (response.error) {
    paymentForm.find('.payment-errors').text(response.error.message);
    paymentForm.find('button').prop('disabled', false);
  } else {
    var token = response.id;
    paymentForm.append($('<input type="hidden" name="purchase[payment_token]" />').val(token));
    submitPaymentForm();
  }
};

In this code, we are handling the response from Stripe form their createToken function. The part that we are interested is the response, and the form in our page that the card details were taken from. It's a pretty simple of code. If there is any problem with the response, caused by invalid card details, then we will receive an error message from Stripe, we'll display the errors to the user, and re enable the payment button. If there are no errors then we get our token from the response, this represents the card that we want to charge, and we add it into our form, then submit our form to the application server

Stripe with Rails

Whilst we are getting our card token to use to identify a card with Stripe using Javascript, the actual charging of the card is something that we do in Ruby. So our first port of call for that will be to add the Stripe Gem to our Rails app Gemfile.

gem 'stripe'

After this of course we need to install with Bundler, so we are going to run:

bundle install

Once the gem is installed, we need to add our Stripe api key. It is found in the same section as your publishable key, so let's add that to our config/initializers/stripe.rb

Stripe.api_key = ENV["STRIPE_API_KEY"]
STRIPE_PUBLIC_KEY = ENV["STRIPE_PUBLIC_KEY"]

And then we are ready to use Stripe in our Rails app to charge the card. In order to do this we need to build our controller action:

class PurchasesController < ApplicationController

  def create
    @purchase = Purchase.new(purchase_params)

    if @purchase.save_and_make_payment
      redirect_to confirmation_path(@purchase)
    else
      render :new
    end
  end

end

The great thing about being able to do this is that it is really standard Rails code. The only variation we are making from this is that we not calling save, we are creating a method for the purchase that will attempt to charge the card and only save if it's successful, like so:

class Purchase < ActiveRecord::Base

  attr_accessor :payment_token

  def save_and_make_payment
    if valid?
      begin
        charge = Stripe::Charge.create(
          amount: price_in_cents,
          currency: currency,
          source: payment_token,
        )
        save
      rescue Stripe::CardError => e
        errors.add :credit_card, e.message
        false
      end
    end
  end

end

So this code is only doing one thing that is probably out of the ordinary, and that's the if valid? part. Because we are not using the normal save method, then the validations for this model are not automatically triggered as they would be when you normally save a record with ActiveRecord. By not going with that approach you manage to avoid any unexpected side effects of messing with the Rails saving and validation chain of methods, and have a very explicit piece of code detailing how you are dealing with the situation.

Another thing to note is that we are not storing the payment_token, it will survive for life of this object and reattempt if necessary only, as it is intended to be used a single use token.

So if our Purchase object is valid, then our app will call out to Stripe using the gem we included, sending the information about the purchase. I have included the bare minimum here, the amount, the currency to make the purchase in and the token so Stripe knows which card to charge. You will then get a response, if successful then your object will be saved and we can execute whatever post purchase flow you prefer, I've left out though as it'll be quite specific to you, or an exception will be raised by the Stripe gem. If that happens, we rescue it, add it to our object as error message which we can display to the user back in the form, the most likely culprit being a transaction denied.

That's pretty much all there is to taking a one off purchase with Stripe. Of course this is not all you can do, Stripe supports other things such as subscriptions and saving a card to a customer so you can charge them later. I will go over these in a later post.

Hopefully now you have all the information that you need to get moving with charging your customers for things, but if more help is needed, please feel free to leave a comment or send me an email.