A Recurring Billing System with Stripe

Posted by: Brian Sam-Bodden on 06/11/2012

These notes are meant to answer some basic questions about using Stripe for billing, and also to answer some questions about implementation which are not covered in the Stripe documentation and forums. This is not a comprehensive overview by any means. If you find errors, please let me know and I will fix them. The examples will use the stripe-ruby gem (v1.7.0). A list of basic operations can be found in Stripe's documentation.

Stripe recommends downloading the gem over SSL from the mirror at https://code.stripe.com.

sudo gem install --source https://code.stripe.com stripe

Here is a list of resources which I have found useful:

Creating Subscription Plans

Plans can be created through the Stripe Dashboard or the API. Since plans will not change often, it’s easy to create them through the Dashboard and retrieve them from the application via the API.

Stripe Dashboard - New Plan

Stripe::Plan.create(
  :amount => 999,
  :interval => 'month',
  :name => 'My Basic Plan',
  :currency => 'usd',
  :id => 'basic'
)

Create Token with Stripe.js

Stripe provides a javascript library which sends encrypted credit card and cardholder data to Stripe’s servers from the browser. Card info is sent (along with the accounts public key) directly to Stripe and is never handled by host application.

// https://gist.github.com/1750368
Stripe.setPublishableKey('pk_YOUR_PUBLISHABLE_KEY');

Stripe.createToken({
  number: $('.card-number').val(),
  cvc: $('.card-cvc').val(),
  exp_month: $('.card-expiry-month').val(),
  exp_year: $('.card-expiry-year').val()
}, stripeResponseHandler);

The response includes the ID of the created card token if successful, and relevant error messages otherwise. The token represents a valid credit card stored by Stripe’s servers. Card tokens do not represent charges of any kind.

Token Object

A The host server can retrieve information about the card by retrieving the token referenced by the token ID. The response will contain basic card details including the last four digits of the card, instead of the entire number.

Stripe::Token.retrieve("tok_00000000000000")

Create Customer

The card token ID can then be submitted to the host server. Card tokens can be used to create a one-time charge or to create a customer with a subscription. The relationship between a customer and a plan is described by a subscription. Subscriptions can be updated or canceled through the customer object.

Stripe::Customer.create(
  :description => "Customer for jim@example.com",
  :plan => "basic",
  :card => "tok_00000000000000" # obtained with Stripe.js
)

Many applications will offer a free plan. Stripe will allow plans to have an amount of 0 (zero). Customers can be subscribed to a free plan without providing credit card information. Subscribing free users to a free plan could make the upgrade process simpler, since a customer record will already exist. Providing a card token is optional when creating a customer with a subscription to a free account.

Stripe::Customer.create(
  :description => "Customer for jim@example.com",
  :plan => "my_free_plan_id"
)

Updating Customer Subscription

It’s likely that a host application will allow users to change to a different plan. In this case, the relevant customer object can be retrieved and updated. Updates can optionally be prorated.

c = Stripe::Customer.retrieve("cus_00000000000000")
c.update_subscription(:plan => "enterprise", :prorate => true)

Customers can only be updated from a free plan to a paid plan if a valid card token is provided with the request.

c = Stripe::Customer.retrieve("cus_00000000000000")
c.update_subscription(:plan => "pro", :card => "tok_00000000000000" ,:prorate => true)

Customers can be updated from a paid plan to a free plan without providing a card token. In this case Stripe will still have the user’s active card on file; so if the user switches back to a paid plan, it is optional to include a card token with the update request.

At this time, Stripe does not offer a way to remove an active card from a customer which is subscribed to a free plan.

Cancel Customer Subscription

When a user deletes their account or their customer record is no longer needed, the customer object can be retrieved and deleted.

c = Stripe::Customer.retrieve("cus_00000000000000")
c.delete

Webhook Authentication

It is critical to properly authenticate webhooks. There are two methods of authentication recommended by Stripe. The first (and simplest) method is to retrieve the event using the id parameter of the webhook request.

# in a Rails controller
def my_webhook_action
  # this will raise InvalidRequestError (status 404) if the event does not exist
  event = Stripe::Event.retrieve(params[:id])
  # safe to use the returned event object
  # ...
rescue Stripe::InvalidRequestError => e
  logger.error "Unable to authenticate webhook request: #{e.message}"
  # 401 Unauthorized response
end

The second method for authenticating webhooks is to use HTTP basic authentication. When setting the webhook url in the Stripe Dashboard, provide a username and password. See Railscasts episode 82 for details on implementing HTTP basic authentication in a Rails app.

Webhook url

Testing

If you are implementing your system in Ruby, I highly recommend the VCR gem. For a good overview of VCR and it capabilities, watch Railscasts episode 291 *.

It is important not to commit your secret api key to the repo. Use the filter_sensitive_data configuration option to redact your private key from the recordings.

VCR.configure do |c|
  # ...
  c.filter_sensitive_data('[secret_key]') { Stripe.api_key }
end

Appendix

* Please note that some of the configuration syntax has changed in newer versions of the VCR gem:

# VCR.config do |c| # outdated
VCR.configure do |c| # new
  # ...
  # c.stub_with :fakeweb # outdated
  c.hook_into :fakeweb # new
end


About Brian Sam-Bodden

Brian Sam-Bodden

Brian Sam-Bodden is an author, instructor, speaker and hacker that has spent over fifteen years crafting software systems. He holds dual bachelor degrees from Ohio Wesleyan University in computer science and physics and heads Integrallis http://www.integrallis.com. He is a frequent speaker at user groups and conferences nationally and abroad. Brian is the author of "Beginning POJOs: Spring, Hibernate, JBoss and Tapestry", co-author of the "Enterprise Java Development on a Budget: Leveraging Java Open Source Technologies" and a contributor to O'reilly's "97 Things Every Project Manager Should Know".

More About Brian »

NFJS, the Magazine

May Issue Now Available
  • On the road to learning

    by Raju Gandhi
  • Refactoring to Modularity

    by Kirk Knoernschild
  • RESTful Groovy

    by Kenneth Kousen
  • Getting Started with D3.js

    by Brian Sletten
Learn More »