Stripe Subscriptions in Rails

Share this article

Subscription business model

Choosing the right payment solution for your app could be tricky, especially if you’re running a global marketplace. The prospect of dealing with country-specific regulations is not something I look forward to while getting on a new project. Also, there is a lot of boilerplate that you have to handle to get recurring billing to work. Stripe seems to understand the pain thousands of developers face every day and solves it pretty well. In this article, I will demonstrate how to integrate Stripe into your Rails app. Read on.

Setting Up

Before we begin, you will need a Stripe API key. You can obtain one by signing up with Stripe with your bank details. There are two different set of keys, one for your test and one for production.

Let’s begin by setting up the Stripe client. Add this to your Gemfile:

gem 'stripe', :git => 'https://github.com/stripe/stripe-ruby'

and bundle install.

Once you are done installing your dependencies, you need to setup your API keys. In production, you would store these keys in an environment variable and take it out of the codebase. But for brevity’s sake, I’m going to set them in an initializer. Kindly refrain from throwing pitchforks at me… :)

# config/initializers/stripe.rb
#todo remove the key info from this file and have env variable
#todo recreate new API keys when do that
if Rails.env == 'production'
  Rails.configuration.stripe = {
    :publishable_key => MY_PUBLISHABLE_KEY,
    :secret_key      => MY_SECRET_KEY
  }
else
  Rails.configuration.stripe = {
    :publishable_key => TEST_MY_PUBLISHABLE_KEY,
    :secret_key      => TEST_MY_SECRET_KEY
  }
end

Stripe.api_key = Rails.configuration.stripe[:secret_key]

With that out of the way, let’s setup our payment page.

Creating Your First Charge

Payment forms are ridiculously complex to build. There are tons of scenarios to consider and one misstep can put you out of business. Stripe has a simple solution for this problem. All you have to do is to copy the following snippet into your payment page and Stripe will take care of everything from getting the user’s credit card details to making the payment:

# views/stripe_checkout.haml
= form_tag('/stripe_checkout',{method: :post}) do
  %script{src: "https://checkout.stripe.com/checkout.js",
          class: "stripe-button",
          'data-key'=> "#{Rails.configuration.stripe[:publishable_key]}",
          'data-amount'=> 10.0,
          'data-email' => "customer@example.com",
          'data-currency' => 'USD'}

NOTE: You should use a test card in development mode. You can find more details here.

stripe_form

Fire up this view in a browser and you’ll see a ‘Pay with Stripe’ button. On click, it opens a modal for taking the user’s credit card information and authorizes it. The user’s card has not been charged yet, Stripe has merely authorized the card and said the card is valid. This happens in the background and, once Stripe is done with the necessary checks, it will post to the /stripe_checkout endpoint we’ve specified in the form.

Stripe callback to the controller:

def stripe_checkout
  @amount = 10
  #This will create a charge with stripe for $10
  #Save this charge in your DB for future reference
  charge = Stripe::Charge.create(
                  :amount => @amount * 100,
                  :currency => "usd",
                  :source => params[:stripeToken],
                  :description => "Test Charge"
  )
  flash[:notice] = "Successfully created a charge"
  redirect_to '/subscription'
end

and routes.rb,

post 'stripe_checkout' => 'subscription#stripe_checkout'

When we receive the callback from Stripe, it sends a stripeToken with it. This is a hashed reference to the user’s credit card and has a short lifetime. We can use the Stripe::Charge module to create a charge with this token before it expires. At no point will Stripe send us the card number. This way, we don’t have to worry about the security of user’s credit card data.

Congratulations, you’ve created your first charge in under 10 lines of code.

Setup a Subscription

Stripe makes it really simple to integrate subscriptions into your app. First, you’ll need to create a bunch of plans in your dashboard. Let’s build a simple form which will talk to Stripe and do that for us.

# views/index.html.haml
%h1 Create a new subscription
= form_tag('/subscription',{method: :post}) do
  .form-group
    %label Plan name
    %input{:type => 'text', :name => 'name'}
  .form-group
    %label Plan interval
    = select_tag 'interval', options_for_select(['month', 'year']), multiple: false, :include_blank => true,  class: "form-control"
  .form-group
    %label Plan Value
    %input{:type => 'text', :name => 'amount'}

  %button{:type => "submit"} Submit to Stripe

and the controller:

# controllers/subscriptions_controller.rb
class SubscriptionController < ApplicationController
  require "stripe"

  def create
    subscription = Stripe::Plan.create(
      :amount => (params[:amount].to_i)*100,
      :interval => params[:interval],
      :name => params[:name],
      :currency => 'usd',
      :trial_plan => null
      :id => SecureRandom.uuid # This ensures that the plan is unique in stripe
    )
    #Save the response to your DB
    flash[:notice] = "Plan successfully created"
    redirect_to '/subscription'
  end
end

Here, we use Stripe’s client to create a plan for us. The parameters include the amount, interval, currency, and name of the plan. Stripe will save the amount in cents, so make sure to multiply the user’s input by 100. If you wish to offer a trial period before actually charging the customer, send a trial_plan along with the request. Remember, these plans cannot be modified, so if you need to edit the amount or trial period, create a new plan.

plans

Once the plan is created in Stripe, it will return a plan object:

{
  interval: "month"
  name: "Pro"
  created: 1429342808
  amount: 1500
  currency: "usd"
  id: fb2488b5-5e83-49b5-9071-781ca04489c4
  object: "plan"
  livemode: false
  interval_count: 1
  trial_period_days: null
  metadata:
  statement_descriptor: null
}

Ideally, you should save this into the database to avoid unecessary roundtrips with Stripe. But for this article, we’re a going to bypass that and fetch the plans directly.

Building a Pricing Page

We can use the plan module in Stripe to retrieve all the plans:

#controllers/subscription_controller.rb
#......
def plans
  @stripe_list = Stripe::Plan.all
  @plans = @stripe_list[:data]
end
#......

and in your views:

.plan-container
  %ul
    - @plans.each do |plan|
      %li.plan
        .header
          =plan[:name]
        .price
          =(plan[:amount]/100)
        = form_tag('/subscription_checkout', {method: :post, plan: plan}) do
          %input{type: "hidden", name: "plan_id", value: plan[:id]}
          %script{src: "https://checkout.stripe.com/checkout.js",
                  class: "stripe-button",
                  'data-key'=> "#{Rails.configuration.stripe[:publishable_key]}",
                  'data-amount'=> (plan[:amount]),
                  'data-email' => "customer@example.com",
                  'data-currency' => plan[:currency],
                  'data-image' => '/assets/sitepoint.png'}

and in the routes:

post 'subscription_checkout' => 'subscription#subscription_checkout'

As we discussed earlier, Stripe does not charge the card at this point. It sends the customer data with the stripeToken to our endpoint.

#controllers/subscription_controller.rb
#......
def plans
  @stripe_list = Stripe::Plan.all
  @plans = @stripe_list[:data]
end

# This is the callback from stripe
def subscription_checkout
  plan_id = params[:plan_id]
  plan = Stripe::Plan.retrieve(plan_id)
  #This should be created on signup.
  customer = Stripe::Customer.create(
          :description => "Customer for test@example.com",
          :source => params[:stripeToken],
          :email => "test@example.com"
        )
  # Save this in your DB and associate with the user;s email
  stripe_subscription = customer.subscriptions.create(:plan => plan.id)

  flash[:notice] = "Successfully created a charge"
  redirect_to '/plans'
end

#......

Stripe subscriptions are usually associated with a customer, so it can charge them recursively. Stripe::Customer is the equivalant of our User model. It’s a normal practice, especially if you run a subscription service, to create a Customer with Stripe right at signup and associate it with the user. But for brevity sake we’re creating a customer at the time of subscription.

Web Hooks

While Stripe will automatically take care of the billing, it happens asyncronously and our app won’t know whether the charge succeded or not. In the case of recursive payments, Stripe interacts with our app through webhooks. Let’s see how to setup web hooks in our Rails app.

First, open the Stripe dashboard and add this endpoint to the webhook tab:

webhook

And add this to your routes.rb:

post 'webhooks' => 'subscriptions#webhooks'

and add this to the controller:

# controllers/subscription_controller.rb
# Method responsbile for handling stripe webhooks
# reference https://stripe.com/docs/webhooks
def webhook
  begin
    event_json = JSON.parse(request.body.read)
    event_object = event_json['data']['object']
    #refer event types here https://stripe.com/docs/api#event_types
    case event_json['type']
      when 'invoice.payment_succeeded'
        handle_success_invoice event_object
      when 'invoice.payment_failed'
        handle_failure_invoice event_object
      when 'charge.failed'
        handle_failure_charge event_object
      when 'customer.subscription.deleted'
      when 'customer.subscription.updated'
    end
  rescue Exception => ex
    render :json => {:status => 422, :error => "Webhook call failed"}
    return
  end
  render :json => {:status => 200}
end

Stripe is now ready to broadcast events to our Rails app. The above code receives the Stripe event and orchestrates it to the right methods. You can see each specific event type here.

NOTE: In production, you probably want to mask this endpoint with a random hash or it might be easy for a third party to mimic Stripe’s events to get unauthorized access to your app.

Wrapping Up

Stripe is a wonderful tool. It saves developer time and helps businesses focus on their core competence. Feel free to add our Stripe expereinces in the comments.

All the code snippets used in this article is available on Github.

Frequently Asked Questions (FAQs) about Stripe Subscriptions with Rails

How Can I Integrate Stripe Subscriptions with Rails for Recurring Payments?

Integrating Stripe subscriptions with Rails for recurring payments involves several steps. First, you need to set up Stripe and create a new Rails application. Then, you need to configure Stripe in your Rails application by adding the Stripe gem to your Gemfile and setting up your Stripe API keys. After that, you need to create a subscription model and controller, and set up routes for your subscription actions. Finally, you need to create views for your subscription actions and handle Stripe webhook events.

What Are the Benefits of Using Stripe Subscriptions with Rails?

Using Stripe subscriptions with Rails offers several benefits. First, it allows you to easily manage recurring payments for your online business. Second, it provides a secure and reliable payment processing solution. Third, it offers a flexible and customizable subscription model that can be tailored to your business needs. Fourth, it integrates seamlessly with Rails, making it easy to implement in your application.

How Can I Handle Stripe Webhook Events in Rails?

Handling Stripe webhook events in Rails involves creating a webhook endpoint in your Rails application that Stripe can send events to. You can then use the Stripe event data to update your application state accordingly. For example, if a subscription is cancelled, you can update your subscription model to reflect this change.

How Can I Test Stripe Subscriptions in Rails?

You can test Stripe subscriptions in Rails by using Stripe’s test mode and test cards. This allows you to simulate different subscription scenarios and ensure that your application handles them correctly. You can also use Rails testing tools like RSpec and Capybara to write automated tests for your subscription functionality.

How Can I Customize Stripe Subscriptions in Rails?

You can customize Stripe subscriptions in Rails by modifying your subscription model and views. For example, you can add additional fields to your subscription model to store custom subscription data. You can also customize your subscription views to provide a unique user experience.

How Can I Handle Subscription Renewals with Stripe and Rails?

Subscription renewals with Stripe and Rails are handled automatically by Stripe. When a subscription is due for renewal, Stripe will attempt to charge the customer’s card. If the charge is successful, the subscription will be renewed. If the charge fails, Stripe will send a webhook event to your application, which you can handle to update your subscription model and notify the customer.

How Can I Manage Multiple Subscriptions with Stripe and Rails?

You can manage multiple subscriptions with Stripe and Rails by creating multiple subscription models in your Rails application. Each subscription model can represent a different subscription plan. You can then use Stripe’s API to create and manage subscriptions for each plan.

How Can I Handle Failed Payments with Stripe and Rails?

You can handle failed payments with Stripe and Rails by listening for the invoice.payment_failed webhook event. When this event is received, you can update your subscription model to reflect the failed payment and notify the customer.

How Can I Offer Free Trials with Stripe Subscriptions in Rails?

You can offer free trials with Stripe subscriptions in Rails by setting the trial_period_days attribute when creating a subscription. This will start the subscription immediately, but the customer won’t be charged until the trial period is over.

How Can I Cancel Stripe Subscriptions in Rails?

You can cancel Stripe subscriptions in Rails by calling the cancel method on a subscription object. This will cancel the subscription at the end of the current billing period. If you want to cancel the subscription immediately, you can use the cancel_now method.

Hola! I'm a Fullstack developer and a strong advocate of Mobile first design. I'm running a digital children's startup for kids and I lead the engineering efforts there. In my free time I ramble about technology, and consult startups.

GlennG
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week