homeASCIIcasts

288: Billing With Stripe 

(view original Railscast)

Other translations: Ja

If you ever need to process credit card payments through your Rails applications you should take a look at Stripe. Stripe is a payment gateway that is easy to set up and which is very developer friendly. It only charges fees on a per-transaction basis and these are very reasonable. There are no monthly fees or other hidden costs. (By the way we’re not being paid to say this).

Stripe is currently only available in the United States so you’ll need an account at a U.S. bank if you want to use it in your applications. International support is being worked on, however, and should be available soon. This doesn’t mean that you can’t bill international customers, the only restriction is that the seller must be in the U.S.

Adding Stripe to a Rails Application

In this episode we’ll use Stripe to add recurring payments to a Rails application. The application we’ll use is a site that sells Llama Kisses. Why? Well Ryan Bates grew up on a llama ranch and missed out on the opportunity to sell llama kisses then so he’s making up for lost time now.

The Llama Kisses site.

Most of the application is already written. There are four different plans and users can sign up for any one of them to create a subscription. There’s only an email field on the sign-up page right now and when we fill that field in and submit the form a new subscription will be created. So that we can take payments through Stripe we’ll add some more fields for credit card details.

The signup form.

Getting started with Stripe is easy. It we visit stripe.com and click the “Get Started with Stripe” button we’ll be taken to a page where we can process test transactions. We can also do this from the command line with curl. If we copy the example code from the “Getting Started” page and run it in a terminal window we should see some JSON returned.

$ curl https://api.stripe.com/v1/charges \
>   -u zVyGPfdmfhSGpHAxhFiJT7kFnHeSi9ZN: \
>   -d amount=100 \
>   -d currency=usd \
>   -d 'card[number]=4242424242424242' \
>   -d 'card[exp_month]=5' \
>   -d 'card[exp_year]=2012' \
>   -d 'description=test charge IFnFXZgdZk'
{
  "amount": 100,
  "created": 1318355241,
  "currency": "usd",
  "description": "test charge IFnFXZgdZk",
  "fee": 0,
  "id": "ch_oVfzHImv8sN5TV",
  "livemode": false,
  "object": "charge",
  "paid": true,
  "refunded": false,
  "card": {
    "country": "US",
    "exp_month": 5,
    "exp_year": 2012,
    "last4": "4242",
    "object": "card",
    "type": "Visa"
  }
}

Whether we run a test transaction through the site or through the terminal window the recent payments are shown in the dashboard at the bottom of Stripe’s management page.

Stripe�s dashboard showing our test payments.

Creating an account is easy; all we need to is enter an email address and a password on the management page. Once we’ve done that we can visit the page for our account where we’ll see, amongst other things, the API keys we’ll need to communicate with Stripe’s REST API. Stripe also provide a Ruby gem that makes using this API in Rails applications much easier. We’ll install this gem in our application now. As with most Ruby gems we do this by adding a reference to it the Gemfile and then running bundle.

/Gemfile

source 'http://rubygems.org'
gem 'rails', '3.1.1'
gem 'sqlite3'
# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'sass-rails',   '~> 3.1.4'
  gem 'coffee-rails', '~> 3.1.1'
  gem 'uglifier', '>= 1.0.3'
end
gem 'jquery-rails'
gem 'stripe'

Stripe needs two API keys so that it knows which account it’s working with. We’ll store these in a new stripe.rb initializer file in the application’s /config/initializers directory. The keys we need to store here are the secret test key that’s shown in our account page and the public API key here. We’ll store the public key in a constant as it isn’t used by the Ruby gem, only in views and JavaScript. This key should hold the value from the publishable test key on our Stripe account page.

/config/initializers/stripe.rb

Stripe.api_key = "5J7dr36JmNpLrXXFwAXChdRzZZwLyCHV"
STRIPE_PUBLIC_KEY = "pk_B1SaM15nXXruDU3g2D6uns2kJeu9m"

As this file contains sensitive information it’s a good idea to add it to the application’s .gitignore file so that it isn’t included in the repository. Also, as we’ll use different keys on the production site we don’t want this file being accidentally copied over to the production codebase.

Adding Stripe’s JavaScript and The API Key

When a user submits their credit card information in a form the credit card information goes directly to Stripe’s server and not to our application at all. This makes it a lot easier to get PCI compliance. If we look at the example form on Stripe’s API page we’ll see that none of its fields have a name attribute. This means that when the form is submitted these fields aren’t passed to our application’s server. Instead they’ll read by some JavaScript code that is triggered when a user submits the form.

To use Stripe in our application we need to include a JavaScript file from Stripe’s server. Then we set our publishable key and add some JavaScript that will fire when the form containing the credit card fields is submitted. This JavaScript will generate a token based on the credit card information from the form.

We’ll add Stripe’s JavaScript file in our application’s layout file. It needs to be placed before the application’s own JavaScript file. We could add some code that checks if the current page is the checkout page and only adds the script when this is the case, but we won’t do that here. We also need to make our public key available to JavaScript and we’ll do this the same way that Rails adds its CSRF key, in a meta tag in the head of the page.

/app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
<head>
  <title>Llama Kisses</title>
  <%= stylesheet_link_tag    "application" %>
  <%= javascript_include_tag "https://js.stripe.com/v1/", "application" %>
  <%= csrf_meta_tags %>
  <%= tag :meta, :name => "stripe-key", ↵ 
    :content => STRIPE_PUBLIC_KEY %>
</head>
<body>
  <!-- Body omitted -->
</body>
</html>

Adding Credit Card Fields To The Subscription Form

Next we’ll add the credit card fields to the subscription form. The form currently contains a hidden field that holds the id of the selected plan and a text field for an email address. We’ll add more fields for a credit card number, security code and expiration date.

/app/views/subscriptions/new.html.erb

<%= form_for @subscription do |f| %>
  <% if @subscription.errors.any? %>
    <div class="error_messages">
      <h2><%= pluralize(@subscription.errors.count, "error") %> prohibited this subscription from being saved:</h2>
      <ul>
      <% @subscription.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
  <%= f.hidden_field :plan_id %>
  <div class="field">
    <%= f.label :email %>
    <%= f.text_field :email %>
  </div>  
  <div class="field">
      <%= label_tag :card_number, "Credit Card Number " %>
      <%= text_field_tag :card_number, nil, name: nil %>
  </div>
  <div class="field">
      <%= label_tag :card_code, "Security Code on Card (CVV)" %>
      <%= text_field_tag :card_code, nil, name: nil %>
  </div>
  <div class="field">
    <%= label_tag :card_month, "Card Expiration" %>
    <%= select_month nil, {add_month_numbers_true: true}, {name: nil, id: "card_month"}%>
    <%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year+15}, {name: nil, id: "card_year"}%>
  </div>
  <div class="actions"><%= f.submit "Subscribe" %></div>
<% end %>

We’ve added two text fields to the form, one for a credit card number, and one for the security code and also two dropdowns for the expiry date. There are a few things worth noting about the fields we’ve added. For the credit card name and security code we’ve used label_tag and text_field_tag instead of going through the form builder as these new fields aren’t attributes on our Subscription model. We don’t want them to be submitted back to the server at all so we’ve also specified a nil name for each field. Similarly for the expiry date fields we’re using select_month and select_year for the expiry date and specifying the id manually so that we don’t have to deal with the one that Rails generates.

When we reload the page the new fields are shown in the form.

The subscription form with its new credit card fields.

Next we want to write the JavaScript that fires when the form is submitted. This script will submit the credit card information to Stripe and submit the token that it receives back from Stripe through the form. As our application is written in Rails 3.1 we’ll write this code in CoffeeScript in the subscriptions.js.coffee file.

/app/assets/javascripts/subscriptions.js.coffee

jQuery ->
  Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'))
  subscription.setupForm()
subscription =
  setupForm: ->
    $('#new_subscription').submit ->
      $('input[type=submit]').attr('disabled', true)
      subscription.processCard()
  processCard: ->
    card =
      number: $('#card_number').val()
      cvc: $('#card_code').val()
      expMonth: $('#card_month').val()
      expYear: $('#card_year').val()
    Stripe.createToken(card, subscription.handleStripeResponse)
  handleStripeResponse: (status, response) ->
    if status == 200
      alert(response.id)
    else
	 alert(response.error.message)

This code should only run after the page’s DOM has loaded and so it’s all wrapped in the jQuery function. The first thing it does is set up Stripe by specifying the publishable key. The key’s value is read from the meta tag we added in the application’s layout file. We fetch it by looking for a meta tag with a name attribute that has a value of stripe and reading that tag’s content attribute. The rest of the logic is handled through a subscription object. This object has a function called setupForm that will do all of the work.

Next we create the subscription object and give it a setupForm function. In this function we get the subscription form by its id and add a callback that fires when the form’s submit event fires. When the form is submitted we disable the submit button to stop the it from being accidentally submitted again by setting the button’s disabled attribute to true. Then we call a processCard function and return false so that the form isn’t submitted.

The processCard function creates an object representing the credit card’s values in the format that Stripe expects and reads the appropriate values for each field from the form’s fields using val(). The next line is the important one. We call Stripe.createToken with two arguments. The first is the card object that we’ve just created (we can also pass in an optional amount here). The second is a function that will handle the response from Stripe, which happens asynchronously. We’ll pass in subscription.handleStripeResponse here.

The handleStripeResponse function takes two arguments: a status and a response. If the status has a value of 200 then the transaction succeeded and we’ll have a response token stored in response.id. For now we’ll just alert the response value so that we can see what it is. If the status isn’t 200 then an error has occurred so we’ll alert the error message.

We can give our new form a try out now. If we reload the new subscription form and submit valid card details we get a response token back.

The response token shown for a valid request.

If we enter an invalid card number we’ll get an error message returned instead.

An error message is returned if we enter invalid details.

Handling The Response

Now that we know that the calls to Stripe are being processed correctly we’ll improve the code that handles the error messages. Instead of showing an alert we’ll show the error message on the page in a div with and id of stripe_error and re-enable the submit button so that the user can enter their details again.

/app/assets/javascripts/subscriptions.js.coffee

handleStripeResponse: (status, response) ->
  if status == 200
    alert(response.id)
  else
    $('#stripe_error').text(response.error.message)
    $('input[type=submit]').attr('disabled', false)

Having done this we’ll need to add the div to the view.

/app/views/subscriptions/new.html.erb

<%= form_for @subscription do |f| %>
  <!-- Form fields omitted -->
  <div id="stripe_error">
    <noscript>JavaScript is not enabled and is required for this form. First enable it in your web browser settings.</noscript>
  </div>
  <div class="actions"><%= f.submit "Subscribe" %></div>
<% end %>

We’ve placed a noscript element in the div so that an error is shown if anyone tries to use the form in a browser that doesn’t have JavaScript enabled. When we reload the page and submit the form with an invalid credit card number again we’ll see the error message in the stripe_error div instead of an alert.

The error message is now shown on the page.

Next we’ll change the code that handles successful transactions. Instead of displaying the response token in an alert we’ll add it to a new hidden field in the form and then submit the form. We’ll add a new hidden field in the view and call it stripe_card_token.

/app/views/subscriptions/new.html.erb

<%= f.hidden_field :stripe_card_token %>

There’s no stripe_card_token field in the subscriptions table in the database so we’ll need to add this field as a virtual attribute in our Subscription model.

/app/models/subscription.rb

class Subscription < ActiveRecord::Base
  belongs_to :plan
  validates_presence_of :plan_id
  validates_presence_of :email
  attr_accessible :stripe_card_token
end

Back in our CoffeeScript file we’ll replace the alert with code that sets the value of our new hidden field to the value of the response token and then submits the form. We submit the form by calling submit directly on the form. Doing this bypasses the onsubmit callback so that the form is POSTed back to the server.

/app/assets/javascripts/subscriptions.js.coffee

handleStripeResponse: (status, response) ->
  if status == 200
    $('#subscription_stripe_card_token').val(response.id)
    $('#new_subscription')[0].submit()
  else
    $('#stripe_error').text(response.error.message)
    $('input[type=submit]').attr('disabled', false)

The form is submitted to the SubscriptionsController’s create action and the token will be passed in to a new Subscription. We could handle the payment through a before_create callback here but instead we’ll create a new method in the Subscription model called save_with_payment and do it there. Before we write this method we’ll replace the call to save with save_with_payment.

/app/controllers/subscription_controller.rb

def create
  @subscription = Subscription.new(params[:subscription])
  if @subscription.save_with_payment
    redirect_to @subscription, :notice => "Thank you for ↵ 
      subscribing!"
  else
    render :new
  end
end

The save_with_payment method will use the stripe gem to handle the payment. We could use the response token to make a payment using the Stripe::Charge.create method but we don’t want to do this as the response token can only be used once and we want to make a recurring payment. Instead we’ll use the token to create a new Stripe::Customer. We can then assign a plan to that customer with recurring payments. Stripe will then automatically handle recurring payments for us. All of this is covered in Stripe’s excellent API documentation.

Before we make any changes to our Subscription model we’ll go to our Stripe account and set up some plans so that it knows how to handle our recurring payments. We’ll add four plans with names and ids that match the plans in our Rails application.

The plans added to our Stripe account.

Back in our Subscription model we can now write the save_with_payment method.

/app/models/subscription.rb

class Subscription < ActiveRecord::Base
  belongs_to :plan
  validates_presence_of :plan_id
  validates_presence_of :email
  attr_accessor :stripe_card_token
  def save_with_payment
    if valid?
      customer = Stripe::Customer.create(description:email, ↵ 
        plan: plan_id, card: stripe_card_token)
      self.stripe_customer_token = customer.id
      save!
    end
  end
end

This method checks that the model is valid and if it is it will create a new Stripe::Customer, passing it the customer’s email address as a description, the plan_id and the stripe_card_token. Stripe will create a new id for the customer which we’ll need to save to the database. We’ll save it in to a new stripe_customer_token field. We’ll create a migration for this field then run rake db:migrate to add it to the database.

$ rails g migration add_stripe_to_subscriptions stripe_customer_token:string

We’re ready now to test making a payment. If we go to the new subscription page, enter valid credit card details and click “Subscribe” the token and customer will be created in the background and the form will be submitted. If we visit the payments page for our Stripe account we’ll see the new payment listed for the price of the plan. This is a recurring payment and will be taken every month.

The recurring payment shown in the dashboard.

Handling Errors

The call to Stripe::Customer.create might raise an exception if some of the information passed isn’t valid. This can happen, for example, if a user submits the form without JavaScript enabled in their browser so that the stripe_card_token wasn’t generated. It’s a good idea to use rescue to handle these cases. Stripe will raise an InvalidRequestError in these cases and we can catch these, log them, add a validation error to the page and then show the form again.

/app/models/subscription.rb

def save_with_payment
  if valid?
    customer = Stripe::Customer.create(description:email, ↵
      plan: plan_id, card: stripe_card_token)
    self.stripe_customer_token = customer.id
    save!
  end
rescue Stripe::InvalidRequestError => e
  logger.error "Stripe error while creating customer: #{e.mesage}"
  errors.add :base, "There was a problem with your credit card."
end

We also need to handle errors on form fields unrelated to credit card information, such as the email field on our form, better. Currently if we try to submit a subscription with no email address but with valid credit card details we’ll receive a proper token from Stripe but instead of being redirected to the page that shows a successful login we’ll be shown the form again with an email validation error.

The credit card fields still show even after we�ve entered valid information.

In these cases we should hide the credit card fields as we already have a valid token. We can do this by modifying the view so that it hides the fields if a stripe_card_token exists for the subscription and shows a message instead.

/app/views/subscriptions/new.html.erb

<div class="field">
  <%= f.label :email %>
  <%= f.text_field :email %>
</div>  
<% if @subscription.stripe_card_token %>
  Credit card has been provided
<% else %>
  <div class="field">
    <%= label_tag :card_number, "Credit Card Number " %>
    <%= text_field_tag :card_number, nil, name: nil %>
  </div>
  <div class="field">
    <%= label_tag :card_code, "Security Code on Card (CVV)" %>
    <%= text_field_tag :card_code, nil, name: nil %>
  </div>
  <div class="field">
    <%= label_tag :card_month, "Card Expiration" %>
    <%= select_month nil, {add_month_numbers_true: true}, ↵ 
      {name: nil, id: "card_month"}%>
    <%= select_year nil, {start_year: Date.today.year, ↵
      end_year: Date.today.year+15}, ↵
      {name: nil, id: "card_year"} %>
  </div>
</div>
<% end %>

We’ll also need to update the setupForm function in our subscriptions CoffeeScript file so that it doesn’t try to process the card again when a user submits the form in this case. We can do this by checking to see if the card number field exists on the form. If it doesn’t then it’s been hidden because the credit card information has already been processed. In this case we won’t process it again and we’ll return true so that the form is submitted.

/app/assets/javascripts/subscriptions.js.coffee

setupForm: ->
  $('#new_subscription').submit ->
    $('input[type=submit]').attr('disabled', true)
    if $('#card_number').length
      subscription.processCard()
      false
    else
      true

If we submit the form with valid credit card details but with a missing email address now the credit card fields will be hidden.

The credit card fields are now hidden.

If we fill in the email field and submit the form again our subscription will be successful and the payment will be processed by Stripe.

When we enter a valid email address our subscription is successfully submitted.

WebHooks

One feature of Stripe that we haven’t covered is WebHooks. These allow us to provide a callback URL that Stripe will call when a given event fires. For example if a recurring payment fails we want to be told about this so that we can suspend that user’s account. We can then inform them so that they can update their credit card information. Speaking of which, our application still needs a way for users to update their card information, cancel their subscriptions and so on, but we won’t be covering that here.

Stripe has a number of features we haven’t covered in this episode, but hopefully we’ve given you enough to get started. Stripe is a great way to process credit card payments. Unfortunately it’s unavailable outside the U.S. for now, but international support is promised soon.