When you’re working with online payments there’s little room for error, especially when your application has a large number of active users, which is certainly the case with our current project. We decided to use Stripe as a payment gateway, as it has proved its reputation many times and is thus, very developer-friendly.  Recurring payments, such as subscriptions for example, can be a very useful feature and in this blog, I’m going to explain how to integrate subscriptions with Stripe into your Rails application, as well as one way you can test them locally using ngork.

Stripe concepts

Before I demonstrate how to integrate the subscription system, let’s introduce the important Stripe concepts that we’ll be using. Namely, these are customers, products, and subscriptions.  All of these are very well documented and you may learn more about them in Stripe’s API reference, but in a nutshell: The Plan object determines the currency, price, and the billing cycle.

The Customer object allows you to bind payments to a certain user and track them and we can bind a Customer to a Plan via the Subscription object. In plain terms, the Customers subscribe to a plan. It is important to understand that Stripe Subscriptions and Charges are two different things. Charges are used to process single payments and may or may not be tied to a Product whereas Subscriptions, are used for charging fees on a scheduled basis (weekly, monthly etc.) and MUST be connected to a Plan. Also, it is noteworthy that subscriptions don’t have any information about how much the customer is going to pay, it simply represents the relationship between a Customer and a Plan.

To give this blog some context, let’s say we’re building an online learning platform, where users can register and access online courses. In addition, let’s say that basic course components, such as video lectures, are free for all users, but users that subscribe, gain access to more cool features.

First and foremost, we need to create the premium plans. This can be done in two ways, one is using the Stripe API, and the other, which I’m going to do in this example, is going to your Stripe account’s dashboard, select “Subscriptions” from the left-hand menu, then “Products”, click the “New+” button and fill in the forms that will determine the pricing plan you want to use.

Basic examples

You’ll almost always want to store information regarding subscriptions in your database, so by generating Subscription and Transaction models, we can keep track of all of the important things. Therefore, the Subscription belongs to the User, and each time a Subscription payment is made, a corresponding Transaction record is created.

We’ll also generate a Subscriptions controller with, for now, two actions: new and create.

require 'subscribe_user_to_premium'
require 'subscriptions_toolkit'

class SubscriptionsController < ApplicationController
  def new

    #Always store your API key in environment variables
    Stripe.api_key = ENV["stripe_api_key"]
    @plan = Stripe::Plan.retrieve(SubscriptionsToolkit::PREMIUM_PLAN_ID)
  end

  def create
    response = SubscribeUserToPremium.new.call(current_user, subscription_params, SubscriptionsToolkit::PREMIUM_PLAN_ID)

    if response.success?
      flash[:notice] = 'You have successfully subscribed to our premium plan!'
    else
      flash[:alert] = 'Ooops, something went wrong!'
    end

    redirect_to root_path
  end

  private

    def subscription_params
      params[:source] = params[:stripeToken]
      params[:email] = params[:stripeEmail]

      params.permit(:source, :email)
    end

end

In the new action, we retrieve the plan object via Stripe API. The parameter that we used in the retrieve function, SubscriptionsToolkit::PREMIUM_PLAN_ID, is a string constant located inside the SubsciptionsToolkit module, and its value is the plan id, in our case ‘eschool-premium’.

Additionally, notice the SubcribeUserToPremium class, here we used the service object pattern and moved all the subscription logic into this class and therefore, made our controller slim and more readable, which we will discuss, later on.

The subscription form (the view for SubscriptionsController’s ‘new’ action)  looks like this. As ugly and basic as it gets, it will serve its purpose for this demonstration.

<%= form_tag subscriptions_path do %>
  <article>
    <label class="amount">
      <span>Amount: <%= @plan.amount %></span>
    </label>
  </article>

  <script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
    data-key="pk_test_ZuzIlGdmjAUUjNP74ugAOKTB"
    data-amount="<%= @plan.amount %>"
    data-email="<%= current_user.email %>"
    data-locale="auto">
  </script>
<% end %>

Let’s get back to our service object, the inside of our SubscribeUserToPremium looks like this:

require 'stripe'

class SubscribeUserToPremium

  def call(user, subscription_params, plan_id)

    begin

      #Always store your API key in environment variables
      Stripe.api_key = ENV["stripe_api_key"]

      customer = Stripe::Customer.create(subscription_params)
      stripe_subscription = customer.subscriptions.create({plan: plan_id})

      ServiceResponse.new(stripe_subscription)

    rescue Exception => e
      ServiceResponse.new(nil, false, 'Something went wrong!')
    end

  end
end

Moving this logic to a ServiceObject, also enables us to reuse in places other than our SubscriptionsController, such as an API endpoint that can be used by mobile apps.

ServiceResponse is a very basic class, that helps with the neat implementation of the SO pattern.

class ServiceResponse
  attr_reader :success, :result, :errors

  def initialize(result = nil, success = true, errors = [])
    @result = result
    @success = success
    @errors = errors
  end

  def success?
    @success
  end

end

And there you have it, it’s as simple as that. The code I’ve shown so far will enable subscriptions to be created on Stripe, but earlier, I said that you’d almost always want to keep important information in your database. How can we do that?

That’s where we come to Stripe webhooks. Whenever an event (such as new subscription created, new subscription payment made, subscription canceled etc.) occurs, Stripe can notify a specific endpoint that we can configure. Let’s create a new controller and an endpoint for this purpose.

Rails g controller StripeWebhook

Add this to our routes.rb

 resources :stripe_webhook, path: '/', only: [] do

    collection do

      post :stripe

    end

  end

And the controller itself looks like this:

require 'json'
require 'create_subscripton_record'
require 'activate_subscription'
require 'create_transaction_record'
require 'send_payment_succeeded_mail'
require 'send_payment_failed_mail'

class StripeWebhookController < ApplicationController
  def stripe

    response = JSON.parse(request.body.read)
    data_object = response["object"]

    case response["type"]
    when "customer.subscription.created"
      create_subscripton = CreateSubscriptionRecord.new.call(data_object)
      return head :ok if create_subscripton.success?
    when "invoice.payment_succeeded"

      subscription = Subscription.find_by(stripe_id: data_object["subscription"])

      ActivateSubscription.new.call(subscription) unless subscription.active?

      create_transaction = CreateTransactionRecord.new.call(data_object)

      send_mail_notification = SendPaymentSucceededMail.new.call(data_object)

      return head :ok if create_transaction.success? && send_mail_notification.success?
    when "invoice.payment_failed"

      subscription = Subscription.find_by(stripe_id: data_object["subscription"])
      send_mail_notification = SendPaymentFailedMail.new.call(subscription)

      return head :ok if send_mail_notification.success?
    end
  end
end

As you can see, we first parse the response that we get from Stripe, and after that, we handle it according to its type. You can see that we use several new service objects here. The CreateSubscriptionRecord and CreateTransactionRecords services to create a corresponding records in our database, the SendPaymetSucceeded and SendPaymentFailedMail services that invoke the Application mailer and notify our user of his transactions and the ActivateSubscription service that updates a particular SubscriptionRecord to active status. (We did this because in our implementation when we first create the subscription record, we set it’s active status to false).

The endpoint has been configured, but we need to register it on Stripe now. Go to Stripe Dashboard -> Webhooks -> +Add Endpoint. Url to be called should be in the form of yourapplication_base_url/stripe . Also check ‘Select events to send’ and then check “customer.subscription.created”, “invoice.payment_succeeded” and “invoice.payment_failed” because we need only those 3 in this example.

How to test subscriptions on localhost using ngrok

It is always important to do a local check before you commit something, but how can we test our localhost app if it’s supposed to communicate with Stripe? It’s simple. All we need is to expose the port on which our app is running locally so that Stripe can access it, and that’s where Ngrok comes in hand. Go to their website and download the .zip file. Extract the file and copy the ngrok executable to the root of your rails project and add it to the .gitignore file. Now, go to the terminal and navigate to the root of your application (where we’ve moved the ngrok executable to) and type this: ./ngrok http 3000

The output should look like this

Now that we’ve exposed our app, we will repeat the endpoint configuration step explained earlier and set the forwarding address (in this case > http://304b8be1.ngrok.io) as yourapplication_base_url.

So our endpoint, in this case, will be http://304b8be1.ngrok.io/stripe

Important: Each time you run .ngrok http 3000, a different forwarding address will be generated!

There you have it, now feel free to play around with this because that is the best way to learn how something works.

Now you are all set to go 🙂

Decorator pattern
Software DevelopmentTech Bites
January 23, 2023

Decorator pattern

Design patterns are typical solutions to common problems in software design. Each pattern is like a blueprint that you can customize to solve a particular design problem in your code. A decorator pattern allows users to add new functionality to an existing object without altering its structure. This design pattern comes under…
JPA annotations in Hibernate
Software DevelopmentTech Bites
January 5, 2023

JPA annotations in Hibernate

Suppose you have wondered how we interact with relational databases without (directly) using SQL queries. In that case, the answer usually lies in Object-Relational Mapping (ORM), which will do the job of converting Java objects and statements to database tables and related queries. Hibernate comes into this story as the…

Leave a Reply