Dave Nance David Nance lives at the corner of Art & Science, under the shadow of Risk, and always around Curiosity. As a technical copywriter, he showcases easily-digestible documentation for tech companies, like GoDaddy. As a coder, he explores data-driven solutions with Python, Ruby, and Node.

1 Setup

The popularity of Rails has increased significantly in the age of web apps, in part because of its flexibility when it comes to integrations. Oftentimes, apps need to communicate or work with third-party vendors, REST APIs, and libraries that serve to ease the development process. Payment processing is one area where the use of secure, convenient vendor services applies to a wide range of applications that need the ability to process payment information.

It eases the development process to be able to avoid brewing a custom checkout solution, and Stripe has done a wonderful job of being convenient, yet flexible. We will create a simple web app that integrates with Stripe to manage coupons, create and delete subscriptions, processes one-time payments and deposits, logs user payment events, alerts customers of predetermined events via email, and performs daily tasks by way of scheduled tasks.

All of this would be possible, but quite difficult if we were using Rails out of the box. Instead of using vanilla Rails and crafting an entirely new workflow that uses separate and independent features of Stripe, we will use a library that oversees all Stripe functionality from a single perch.

Code: To follow along, be sure to clone the demo app from GitHub

1.1 Prerequisites

In this tutorial, we will explore a simple Rails app that performs some activities within a plausible scenario. The app utilizes the fairly popular open-source Rails gem, Stripe Rails, to communicate securely with a linked Stripe account, and should serve as a template to branch off on more complex data pipelines.

There are a few prerequisites to talk about before we get started. We are running Rails 4.0 in this case, so as long as you use some version of Rails 4 there should not be anything more than minor differences when following along with the working demo that can be run locally along with a Rails server. Additionally, changes can be made interactively through your local Rails console, to diagnose functionality and migrations on the fly.

In addition to the Stripe-Rails gem that we've chosen, there are other gems out there that can help to streamline integrations between your app and Stripe's payments processor, a few of which can be found here:

Stripe Rails specifically handles some of the functionality we're looking for in our application, namely the ability to deal with all of our Stripe configurations from one place, seamlessly managing our coupons with flexible options, and receiving and validating webhooks that listen for events. Additionally, we have the option of using stripe.js for more robust form handling.

We manage the definition of our payment plans and subscriptions, just by running stripe:install after we have a scaffolded root app folder:

$ rails generate stripe:install

1.2 Stripe API Keys

Once you have signed up with a Stripe account, we will need to grab your API key, found in your account settings. Listed, you should find your test secret key, test publishable key, live secret key, and live publishable key. Our Stripe Rails gem has setup configuration folders and files for us so that we can store our API keys in them seamlessly. Navigate to config/environments/development.rb, and place the following code and your API details at the bottom of the file:

config.stripe.secret_key = 'your test secret key' config.stripe.publishable_key = 'your test publishable key' config.stripe.eager_load = ['user']

Specific instructions for production environments are similar, and are referenced in-file from config/environments/production.rb. This command creates the files config/plans.rb, and config/coupons.rb, which we will explore later.

1.3 Gems

As noted earlier, we have chosen the Stripe Rails gem to help with processing. We also included Devise, and Whenever. Devise is an include worth thinking about in production environments for handling rack-based authentication that allows for multiple models to be signed in concurrently. While the scope of this guide is too small to go into more complicated setups, we do connect with the gem with this small bit of code at the top of our user model:

devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable

+ Recoverable allows us to reset a user password and resend instructions for basic login into our app.

allows us to reset a user password and resend instructions for basic login into our app. + Rememberable manages generation and clearing of a token from a saved cookie.

manages generation and clearing of a token from a saved cookie. + Trackable tracks sign in counts, ip addresses, and timestamps user events.

tracks sign in counts, ip addresses, and timestamps user events. + Validatable provides email and password validation.

For testing, we've set our Devise gem to "require: false". Most of what can be found under the hood of the Devise gem will be found in your app's config folder.

The Whenever gem allows us to wrap our app with a cron job so that we can schedule rake tasks to handle some of the daily tasks we are going to setup.

2 Generate the Scaffold

In this case, the basic premise for our user is for us, the vendor, to allow the user to deposit money from their credit card into our system, and then to use some of that money to purchase some of our goods and services. For our purposes, we can imagine that we sell something like monthly or on-demand written content to businesses, or perhaps we sell licensed digital photo services in the cloud, or subscription-based B2B public relations services; the only limit is our imagination.

Here is a quick overview of the basic functionality we're looking for from our application: a web-ready app that communicates securely with Stripe, authenticates the user credit card, handles any errors with basic response handling, charges the card for a subscription or a one-time payment, determines if the user qualifies for a coupon, uses webhooks to notify the user of a low balance, and alerts them after a prolonged period of inactivity.

We can begin by taking advantage of scaffolding, and then build out some more custom functionality afterwards:

$ rails new rails-stripe $ cd rails-stripe $ rails scaffold payments

With the core scaffolding of our app in place, we can start to build out a form view and then begin building the user model, where we will house most of the capabilities of our user.

In our gemfile, include a few gems we will use a bit later, and then run bundle install:

gem 'devise' gem 'stripe-rails' gem 'whenever', require: false

The scaffold generator has pumped out a generic view called /app/views/payments/new.html.erb that corresponds to the view linked to the payments controller, /app/controllers/payments_controller.rb. We'll place some HTML in there to capture the card details of our user, to be handled on the Stripe end:

<%= form_tag payments_path, id: 'card-details' do %> <div class="payment-errors"></div> <div> <label for="card-number">Card Number:</label> <input id="card-number" type="text" data-stripe="number" /> </div> <div> <label for="card-exp-month">Card Expiry Month:</label> <input id="card-exp-month" type="text" data-stripe="exp_month" /> </div> <div> <label for="card-exp-year">Card Expiry Year:</label> <input id="card-exp-year" type="text" data-stripe="exp_year" /> </div> <div> <label for="card-cvc">CVC:</label> <input id="card-cvc" type="text" data-stripe="cvc" /> </div> <div> <label for="payment-type">Payment Type:</label> <select id="payment-type" name="payment_type"> <option value="deposit">$20 Deposit</option> <option value="subscription">Monthly subscription, $9.97</option> </select> </div> <div> <input type="submit" value="Pay Now" /> </div> <% end %>

We've made it possible for the user to select either a $20 deposit, or begin a subscription, to be charged monthly at the rate of $9.97 per month. The IDs and names come from conventions described in the Stripe Rails gem.

The first line of the template call calls out the id: card-details, which links the form to a javascript file that communicates with our payments controller, either authenticating the Stripe token, or giving a payment error. On form submit, our card-details tag fires payments.js, found at /app/assets/javascripts/payments.js:

$(function() { var stripeResponseHandler, $cardForm; $cardForm = $('#card-details'); stripeResponseHandler = function(status, response) { if (response.error) { $cardForm.find('.payment-errors').text(response.error.message); $cardForm.find('input[type=submit]').prop('disabled', false); } else { var token = response.id; $cardForm.append($('<input type="hidden" name="stripe_token" />').val(token)); $cardForm.get(0).submit(); } }; $cardForm.submit(function() { Stripe.card.createToken(this, stripeResponseHandler); return false; }); });

The form is doing just a few things. We're using jQuery to select the card-details id from the form and setting it equal to the $cardForm variable. Then, our flow finds the form class, .payment-errors, and issues a message response if it is not empty. If validation proceeds past this point, we save response.id in the variable token. This token is then transferred in a hidden field with the name stripe_token, and handed to Stripe, who then continues to try and change the card.

3 The Payments Controller

Once the HTTP post request is made through the form submit, and Stripe has authenticated the token, the payments controller app/controllers/payments_controller.rb speaks with our user model via the do_deposit_transaction method, invoked on the current_user object, like so:

class PaymentsController < ApplicationController def new end def create if stripe_token = params[:stripe_token] if current_user.do_deposit_transaction(params[:payment_type], stripe_token) flash[:notice] = 'Card charged successfully' else flash[:alert] = 'Some error happened while charging you, please double check your card details' end else flash[:alert] = 'You did not submit the form correctly' end redirect_to new_payment_path end end

Let's look closer at what the do_deposit_transaction method does as we start to build out the user model.

4 The user model

Now it's time to create some of the functionality of our user, and the first method we make is the deposit transaction. We want to take type, amount and stripe_token parameters to define our deposit activity:

def do_deposit_transaction(type, stripe_token) amount = Transaction.amount_for_type(type) coupon = UserCoupon.coupon_for_amount(amount) card = save_credit_card(stripe_token) if deposited = deposit(amount, card) subscribe if type == 'subscription' create_coupon(coupon) if coupon deposited end end

All this method is doing is letting us know if the user is initiating a monthly subscription or a one-time deposit, and whether a coupon might be applicable. This method calls another function, deposit, a few lines down.

The deposit method uses the Stripe-Rails gem to create a charge on the card with amount, currency, customer, card, and description parameters:

def deposit(amount, card) customer = stripe_customer Stripe::Charge.create( amount: amount, currency: 'usd', customer: customer.id, card: card.id, description: "Charge for #{email}" ) customer.account_balance += amount customer.save rescue => e false end

Upon deposit, we take a look at the current customer balance, customer.account_balance, and accrue it with the deposited amount, so that the customer has an updated balance. Then we save the updated details back to Stripe.

4.1 Coupons and plans

You may have noticed the method create_coupon being called by the do_deposit_transaction function. This method returns true if the customer has already been issued a coupon, and it is of the variety 'off3'.

Coupon delineations are described in the file /app/models/user_coupon.rb model. That model gives a $2-off coupon to monthly subscriptions, and a $3-off coupon to a one-time deposit. The UserCoupon class within this file references the coupons that we have created in config/stripe/coupons.rb. We have defined our coupon methods like so:

Stripe.coupon :off3 do |coupon| coupon.duration = 'once' coupon.amount_off = 300 coupon.currency = 'usd' # coupon.reedem_by = (Time.now + 30.days).utc coupon.max_redemptions = 10 end Stripe.coupon :off2 do |coupon| coupon.duration = 'once' coupon.amount_off = 200 coupon.currency = 'usd' # coupon.reedem_by = (Time.now + 30.days).utc coupon.max_redemptions = 10 end

For coupon variation, other valid methods of the coupon object are described within the coupons.rb file for our convenience.

Because the unique customer has been pulled from the stripe_customer method, we'll know that this one-time coupon has been applied to the user the next time they make a deposit.

def stripe_customer Stripe::Customer.retrieve(stripe_customer_id) end

After the coupons and plans have been setup, we must run a rake to prepare them for syncing on the Stripe.com end:

$ rake stripe:prepare

Note that running this command does not affect plans and coupons that are already setup, rather, it ignores those that exist, and recognizes new items as they are added. To delete a plan from Stripe.com, keep in mind that you will have to manually delete them from your Stripe account.

On more complex workflows, you might configure the default coupon settings to have expiry dates, or to increase buying urgency from a customer newsletter perhaps.

That takes care of most of the basic user functionality in terms of making an HTTP get request through a form, authenticating the Stripe token using some jQuery and a payments controller, making a charge to the user's card, and saving that data on the Stripe end to update the user account. We demand more from our application, and our payment processing could use a bit more interactivity to keep our service front-of-mind with the user, and make for a customer-friendly experience.

5 Webhooks/Callbacks

Webhooks allow us to set certain endpoints to listen for events. In this app, we want to listen for both user inactivity and a low user account balance. If either of these events occur, we'll notify our user that there are some actions that they can take to spend more money with us, or spend with us more frequently - both desired actions.

5.1 The Notifications Class

To setup notifications, we can build a Notifications class that can be found at /app/mailers/notifications.rb. In it, we'll craft simple email messages to send to users when they qualify for certain conditions:

class Notifications < ActionMailer::Base default from: 'from@example.com' def low_balance(user) @message = 'Your balance is lower than $10' mail to: user.email end def inactive_user(user) @message = "You've been inactive for a month" mail to: user.email end end

5.2 The Low-Balance & Inactive User Callbacks

We are linking our 'low_balance' and 'inactive_user' methods of our Notifications class to the /app/views/notifications/low_balance.html.erb and /app/views/notifications/inactive_user.html.erb views via the @message name instance. Here is what our low_balance view might look like in its simplest form:

<span>Low balance</span> <p> <%= @message %> </p>

5.3 Using the Whenever Gem for Scheduling

We can then invoke an instance of this class from config/schedule.rb. This file is actually created from the Whenever gem. The Whenever gem is used for the management of cron jobs within your applications. By installing the gem in our gemfile and running bundle install, we only have to take a few more steps for the gem to create our schedule.rb file. Navigate to the root of your app from the command line, and type:

$ wheneverize .

This creates a wrapper for a cron job processor that allows us to schedule tasks and run scripts, such as the email notifications we are setting up here. Conveniently, there are in-file instructions for configuring your cron tasks. The easiest way to do this is to write a rake task and place it in /lib/tasks/app.rake, which we will explore in a moment. After browsing the Whenever documentation on scheduling, your schedule.rb file might look like this:

every 1.day do rake 'app:notify_inactive_users' end

What we are doing is performing a daily rake that collects all inactive users, as defined by our app.rake file. If they are considered inactive, we send them a message through our mailer. We could have added a bit more functionality by setting up a destination for logging, so that it is easier to diagnose any errors with the cron job. Perhaps it would make sense to define the environment, and create an error log for error feedback. At the top of schedule.rb, you could accomplish that like this:

set :environment, "development" set :output, {:error => "log/cron_errors.log", :standard => "log/cron_log.log"}

We'll take a look at app.rake a bit closer:

namespace :app do desc 'Send an email for users who have been inactive for a month' task notify_inactive_users: :environment do users = User.where('last_sign_in_at <= ?', Time.now - 30.days).each do |user| puts "Notifying #{user.email}" # TODO: Notified users should be flaged so that they aren't notified for another month Notifications.inactive_user(user).deliver end puts "Notified #{users.count} user(s)" end end

Our rake file selects users who have a last_sign_in_at of earlier than 30 days ago or more. If those conditions are met, then the inactive_user method is called on the Notifications class, and hopefully our user realizes the error of their ways, and starts using our services again. If the message falls on deaf ears, it could be an issue for us the way our app.rake is setup.

You may notice that as it stands now, each inactive user would be notified each day that the rake is performed. A daily reminder of inactivity might qualify as spammy to our user, and they might cut us off for good when they get back from their sixty-day vacation in the Venezualan jungles, to the tune of thirty emails from us. Luckily, this is a development app that uses Stripe sandbox payments by default. As always, test your Rails application and notifications in the development environment first, preferably using a kit like Capybara to hash out different scenarios.

6 Syncing With Stripe.com

Once the app is running, we should check the Stripe logs to make sure that HTTP requests are being handled successfully, and that information is traveling as we might expect. One easy way to do this is to login to Stripe to check the logs. Through the main Stripe interface, click "Logs" and find the test events and transactions that you have made with the application. Here is what one might look like:

{ amount: "2000" currency: "usd" customer: cus_4wLvd3NhEu6v9P card: "card_14mV4bAosYZryyBZ77EnFioE" description: "Charge for james.bond@localhost.com" }

The same can be done with "Events & Webhooks". Peruse the logs and make sure that your app is properly communicating with the callbacks that we set up. If your webhook is not responding to events, make sure that your test webhook url is configured using your computer's ip, and that your test is set on the "Live" tab; otherwise it won't work. Your webhook url format will likely be a JSON endpoint like so:

http://your-ip-here:8080/stripe/events.json

7 Takeaways

That concludes the setup of our Rails Stripe app. This app accomplishes what we set out to do, which is to streamline the process of integrating Stripe with our web app, so that we can process payments and simplify development. We used the Stripe Rails gem to manage our Stripe integrations, setup a basic Rails scaffold, created a Stripe.com account, and built in functionality for dealing with various transactions, including subscriptions and deposits/one-time payments. We issued coupons for eligible users, and setup interactive webhooks to notify our users of special scenarios.

Make sure to clone the working demo, play around with it, and come up with your own custom functionality.