Payola

Stripe payments for your Rails app

Payola v1.2: Now with Subscriptions

17 Nov 2014

Today is release day for Payola v1.2.0 and the big watch word is subscriptions. So now that they’re here, how do you use subscriptions with Payola? It’s easy:

  1. Install the gem
  2. Configure a model and a controller
  3. Set up a form
  4. Profit!

Here’s the steps that Payola goes through when your customer clicks your Subscribe button:

  1. Contact Stripe to get a token from the credit card information
  2. POST your form asynchronously (see below for an example controller)
  3. Creates the Stripe::Subscription record asynchronously
  4. On the client, waits for subscription processing to finish before redirecting to the URL specified by SubscriptionPlan#redirect_path (defaulting to /).

Let’s go through how you setup Payola subscriptions in more detail:

1. Installation

Add payola-payments to your Gemfile:

gem 'payola-payments', '>= 1.2.4'

Now run bundler and install the migrations:

$ bundle install
$ rails g payola:install
$ rake db:migrate

Payola assumes you have your Stripe keys in environment variables named STRIPE_SECRET_KEY and STRIPE_PUBLISHABLE_KEY. Make sure to set those up or configure them in Payola’s initializer.

2. Model

Payola tracks everything about a subscription for you, but you have to tell it about your plans. For that, create a Plan model and include the appropriate Payola module:

$ rails g model Plan \
    stripe_id:string \
    name:string \
    amount:integer \
    interval:string
$ rake db:migrate

Now open up app/models/plan.rb and add the concern:

class Plan < ActiveRecord::Base
  include Payola::Plan
end

At this point you should be able to open up a console and add a plan:

$ rails console
irb(main):001:0> Plan.create(name: 'Test Plan', stripe_id: 'test-plan', amount: 100, interval: 'month', interval_count: 1)

This will create a Plan object as well as create a plan within Stripe.

3. Form

Payola currently only supports custom forms for subscriptions but it makes it as easy as possible to do. Let’s create a simple controller first at app/controllers/subscriptions_controller.rb:

class SubscriptionsController < ApplicationController
  # bring in the `render_payola_status` helper.
  include Payola::StatusBehavior

  def new
    @plan = Plan.first
  end

  def create
    # do any required setup here, including finding or creating the owner object
    owner = current_user # this is just an example for Devise

    # set your plan in the params hash
    params[:plan] = SubscriptionPlan.find_by(id: params[:plan_id])

    # call Payola::CreateSubscription
    subscription = Payola::CreateSubscription.call(params, owner)

    # Render the status json that Payola's javascript expects
    render_payola_status(subscription)
  end
end

This is what our form is going to look like:

Here’s what the view looks like, using Bootstrap 3 for layout:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.payment/1.2.1/jquery.payment.min.js"></script>
<div class="row">
  <div class="col-xs-8 col-xs-offset-2">
    <div class="well">
    <%= form_tag('/subscriptions',
      role: 'form',
      class: 'payola-onestep-subscription-form',
      'data-payola-base-path' => '/payola',
      'data-payola-error-selector' => '.payola-error',
      'data-payola-plan-type' => @plan.plan_class,
      'data-payola-plan-id' => @plan.id) do %>
      <div class="form-group">
        <label>Email Address</label>
        <input type="email"
               name="email"
               data-payola="email"
               placeholder="you@example.com"
               class="form-control"></input>
      </div>
      <div class="form-group">
        <label>Card number</label>
        <input type="text"
               size="20"
               data-stripe="number"
               class="card-number form-control"
               placeholder="**** **** **** ****"></input>
      </div>
      <div class="row">
        <div class="col-md-6">
          <div class="form-group">
            <label>Exp</label>
            <input type="text"
                   size="8"
                   class="exp-date form-control"
                   placeholder="MM / YY"></input>
            <input type="hidden" data-stripe="exp_month"></input>
            <input type="hidden" data-stripe="exp_year"></input>
          </div>
        </div>
        <div class="col-md-6">
          <div class="form-group">
            <label>CVC</label>
            <input type="text"
                   size="4"
                   data-stripe="cvc"
                   class="form-control"
                   placeholder="***"></input>
          </div>
        </div>
      </div>
      <div class="text-center">
        <input type="submit" value="Subscribe" class="btn btn-info btn-lg"></input>
      </div>
      <div class="alert alert-warning payola-error" style="display: none"></div>
      <% end %>
    </div>
  </div>
</div>

Payola’s subscription form behavior is triggered by your form having the class payola-onestep-subscription-form. After that, you just mark up the data destined for Stripe with data-stripe attributes.

This particular form has a few additional niceties provided by the jquery.payment library from Stripe. Here’s the javascript for the form, in app/assets/javascripts/form.js:

$(function() {
  $('.exp-date').payment('formatCardExpiry');
  $('input[data-stripe="number"]').payment('formatCardNumber');
  $('input[data-stripe="cvc"]').payment('formatCardCVC');

  $('.exp-date').on('keyup', function() {
      var e = $('.exp-date').first();
      var out = $.payment.cardExpiryVal(e.val());
      $('input[data-stripe="exp_month"]').val(out.month);
      $('input[data-stripe="exp_year"]').val(out.year);
  });

  $('.card-number').on('keyup', function() {
    var e = $('.card-number').first();
    var type = $.payment.cardType(e.val());
    var img = "card.png";
    switch(type) {
        case "visa":
          img = "visa.png";
          break;
        case "mastercard":
          img = "mastercard.png";
          break;
        case "discover":
          img = "discover.png";
          break;
        case "amex":
          img = "amex.png";
          break;
    }
    e.css('background-image', 'url(/images/' + img + ')');
  });

});

This does three separate things. First, it sets up formatters on the form’s expiration date, card, and cvc number fields. Next, the JS sets up another event handler that uses jquery.payment’s cardExpiryVal function to parse the card expiration date into a month and a year, and sets the hidden fields to that value.

Finally, it sets up an event handler that changes the credit card icon based on what type of card the customer is entering. The particular images that I’m using are from a very nice set of flat icons off of Creative Market. Shopify put out a free set a while back as well.

In order to make the icon actually show up in the right place, you need to add this small CSS snippet:

.card-number {
  background-image: url(/images/card.png);
  background-repeat: no-repeat;
  background-size: 30px;
  background-position: right 10px center;
}

(card.png is the default green card image.)

Ok, so now we have a form, but what exactly happens here? Payola Subscriptions works like this:

  1. When the customer hits the submit button, Payola intercepts that and sends the fields tagged with data-stripe to Stripe to get a card token.
  2. When Stripe returns a token, Payola POSTs that along with the email address field to /subscriptions, which creates a Payola::Subscription using the service class Payola::CreateSubscription. This automatically launches a background job that attempts to create a Stripe::Customer. The controller then returns a JSON status hash back to Payola’s javascript handler.
  3. The javascript then polls every 500ms, waiting for the subscription to finish. When it’s done, the user’s browser will be redirected to whatever URL the redirect_path on Plan returns, or if it’s not implemented /. Payola will set flash[:notice] appropriately.

4. Profit!

At this point you have a functional subscription system. Payola provides all sorts of hooks and notifications that you can use to trigger additional application-specific behavior, which you can read all about in the README.


P.S.: Payola Pro is an add-on to Payola that provides priority email support, pre-built integrations with several 3rd party services, Stripe Connect marketplace support, along with a lawyer-friendly commercial license. You can read all about it at payola.io/pro, and all of the modules have documentation on the Payola wiki.

P.P.S.: I want to thank Jeremy Green who has been instrumental in driving this forward. Subscriptions would probably be another month away without Jeremy’s help.