Integrate Stripe in your Ruby on Rails app

ksushiva

Oksana Ivanchenko

Posted on March 20, 2019

Integrate Stripe in your Ruby on Rails app

Creating a workspace

We will start by creating a Ruby on Rails application

$ rails new tuto
$ cd tuto
$ bundle install
Enter fullscreen mode Exit fullscreen mode

Then, we will generate a controller named Billing with the help of

rails generate

$ rails generate controller Billing

Enter fullscreen mode Exit fullscreen mode

After that, we're going to add route in config/routes.rb :

root 'billing#index', as: :billing 
Enter fullscreen mode Exit fullscreen mode

To finish, we have to create an empty action index in app/controllers/billing_controller.rb:

class BillingController < ApplicationController

  def index
  end

end
Enter fullscreen mode Exit fullscreen mode

In app/views/billing we will create a file index.html.erb where we will add a button that will show the "Stripe Checkout" modal, that will let us link a credit card to a user. For this tutorial I will be using (as always) the bootstrap library. If you want to use it too, don't forget to add it in your app. If you want to learn how to do it, you can read this article. So in app/views/billing/index.html.erb:

<div class="container">
  <div class="row">
    <h1 class="col-md-12 mt-5 text-center">
      Are you ready to pay?
    </h1>
  </div>
  <div class="row">
    <div class="col-md-12 mt-4 text-center">
      <button class="btn btn-primary">Let's go </button>
    </div>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

For now, we will leave our button like this and later we will change it using a Rails syntax.

Creating a User with Devise

Now, we will implement basic authentication with the help of the "devise" gem. We want only connected users to be able to access the billing page. Start by adding the following line in your Gemfile:

gem 'devise'
Enter fullscreen mode Exit fullscreen mode

To setup devise we have to type this in the terminal:

$ bundle install
$ rails g devise:install
Enter fullscreen mode Exit fullscreen mode

After running these two commands, the terminal will display instructions to configure devise. It says that we have to add this line of code in config/environments/development.rb:

config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
Enter fullscreen mode Exit fullscreen mode

The next step is to create a User model:

$ rails g devise User
$ rails db:migrate
Enter fullscreen mode Exit fullscreen mode

That's it. We added authentication with devise. Now we are ready to create our first user. We need to run 'rails s' in terminal and go to the following page: localhost:3000/users/sign_up. After we created the account it will redirect us automatically to the root page at localhost:3000.

We now want that only signed-in users have access to localhost:3000. For this in app/controllers/billing_conroller.rb we need to add:

class BillingController < ApplicationController
  before_action :authenticate_user!

  def index
   @user=current_user.email
  end

end
Enter fullscreen mode Exit fullscreen mode

So before any action in billing controller it will check if user is signed in. Also in the index action I added a global variable @user which takes email of current user. I will use it in app/views/billing/index.html.erb:

<div class="container">
  <div class="row">
    <div class="col-md-12 mt-4 text-center">
      <h1> Welcome  <%= @user %></h1>
    </div>
  </div>
  <div class="row">
    <h2 class="col-md-12 mt-5 text-center">
      Are you ready to pay?
    </h2>
  </div>
  <div class="row">
    <div class="col-md-12 mt-4 text-center">
      <button class="btn btn-primary">Let's go </button>
    </div>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

To check if everything works we will go to localhost:3000 and clear our cookies. After refreshing page you should be automatically redirected to the Sign in page.

Integrating Stripe

Adding the Stripe credentials

Firstly, you have to sign up on Stripe.
Then, you can find the development and production keys for Stripe on your dashboard, under developers => API keys. The keys that you will see now are for production. To see development keys click to 'view testing data'.
alt text

Now we will add the keys to the rails encrypted credentials. To do this, I use the GNU nano editor which is installed by default on MacOS and Linux.

$ EDITOR=nano rails credentials:edit

alt text

Here we add our keys for development and production.

stripe:
  development:
    publishable_key: 'pk_test_...'
    secret_key: 'sk_test_...'
  production:
    publishable_key: 'pk_live_...'
    secret_key: 'sk_live_...'

Enter fullscreen mode Exit fullscreen mode

Installing Stripe

You need also to add the stripe checkout javascript in app/views/layouts/application.html.erb:

<%= javascript_include_tag 'https://checkout.stripe.com/checkout.js' %>
Enter fullscreen mode Exit fullscreen mode

Also in config/enviroments/development.rb we add:

config.stripe.secret_key = Rails.application.credentials.stripe[:development][:secret_key]
config.stripe.publishable_key = Rails.application.credentials.stripe[:development][:publishable_key]
Enter fullscreen mode Exit fullscreen mode

And in config/enviroments/production.rb:

config.stripe.secret_key = Rails.application.credentials.stripe[:production][:secret_key]
config.stripe.publishable_key = Rails.application.credentials.stripe[:production][:publishable_key]
Enter fullscreen mode Exit fullscreen mode

Finally, we will add the stripe gem in our Gemfile

gem 'stripe-rails'
Enter fullscreen mode Exit fullscreen mode

And run 'bundle install' in terminal.

Setting up the route

Now we are returning to config/routes.rb and add to the file:

get '/card/new' => 'billing#new_card', as: :add_payment_method`
Enter fullscreen mode Exit fullscreen mode

Linking a card to a user

In app/controllers/billing_controller.rb we are creating new action:

def new_card
    respond_to do |format|
      format.js
    end
  end
Enter fullscreen mode Exit fullscreen mode

It sends us to new_card.js.erb which we will create in app/views/billing. So in app/views/billing/new_card.js.erb :

var handler = StripeCheckout.configure({
    key: '<%= Rails.application.credentials.stripe[Rails.env.to_sym][:publishable_key] %>',
    //get a publishable key that we put in editor depending on environment: production or development
    locale: 'auto',
    //handle translation
    name: "upload.express",
    description: "Add your credit card information",
    email: "<%= current_user.email %>",
    panelLabel: "Add payment method",
    allowRememberMe: false,
    token: function (token) {
        var form = document.getElementById('billing-create-payment-method');
        //we will create element with this id in the next step
        var hiddenInput = document.createElement('input');
        hiddenInput.setAttribute('type', 'hidden');
        hiddenInput.setAttribute('name', 'stripeToken');
        hiddenInput.setAttribute('value', token.id);
        //creating an <input type="hidden" name="stripeToken" value="<id>"/>. We will need this information in the next steps to link a user to his card 
        form.appendChild(hiddenInput);
        //adding this input when we use a form.
        form.submit();
    }
});

handler.open();

window.addEventListener('popstate', function() {
    handler.close();
});
Enter fullscreen mode Exit fullscreen mode

Then we change our button in app/views/billing/index.html.erb into a form with id "billing-create-payment-method":

<%= form_tag id: "billing-create-payment-method" do  %>
        <%= link_to "Let's go", add_payment_method_path, remote: true, class: "btn btn-primary" %>
      <% end %>
Enter fullscreen mode Exit fullscreen mode

After that in terminal we run 'rails s' and after clicking on our button we should have something like this:
alt text

Almost there! Now we need to associate the current user to the given card. To do this we have to add a new column stripe_id of type string in the User model by running this in terminal:

$ rails g migration AddStripeIdToUsers stripe_id:string
$ rails db:migrate
Enter fullscreen mode Exit fullscreen mode

In config/routes.rb we are creating a new route:

post "/card" => "billing#create_card", as: :create_payment_method
Enter fullscreen mode Exit fullscreen mode

Now in controller in app/controllers/billing_controller.rb we are creating action create_card:

def create_card 
    respond_to do |format|
      if current_user.stripe_id.nil?
        customer = Stripe::Customer.create({"email": current_user.email}) 
        #here we are creating a stripe customer with the help of the Stripe library and pass as parameter email. 
        current_user.update(:stripe_id => customer.id)
        #we are updating current_user and giving to it stripe_id which is equal to id of customer on Stripe
      end

      card_token = params[:stripeToken]
      #it's the stripeToken that we added in the hidden input
      if card_token.nil?
        format.html { redirect_to billing_path, error: "Oops"}
      end
      #checking if a card was giving.

      customer = Stripe::Customer.new current_user.stripe_id
      customer.source = card_token
      #we're attaching the card to the stripe customer
      customer.save

      format.html { redirect_to success_path }
    end
  end
Enter fullscreen mode Exit fullscreen mode

We are changing a bit our app/views/billing/index.html.erb in order to call an action create_card when we submit our form.

<%= form_tag create_payment_method_path, id: "billing-create-payment-method" do  %>
        <%= link_to "Let's go", add_payment_method_path, remote: true, class: "btn btn-primary" %>
      <% end %>
Enter fullscreen mode Exit fullscreen mode

Quickly, we are going to add the success page. Create a route, empty action and view.

In config/routes.rb:

get '/success' => 'billing#success', as: :success
Enter fullscreen mode Exit fullscreen mode

In app/controllers/billing_controller.rb:

def success
end
Enter fullscreen mode Exit fullscreen mode

In app/views/billing/success.html.erb:

<div class="container">
  <div class="row">
    <div class="col-md-12 mt-4 text-center">
      <h2>You successfully linked a credit card to your account</h2>
    </div>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Now run in terminal 'rails s'. Click on 'Let's go' on the website. For testing purposes you can use the card 4242 4242 4242 4242 with random CVC code and date. After submitting we should be redirected to our success page. And if we go on Stripe in Customers (don't forget to switch to test data) we should see an email and card info of our user.
alt text

Now we need make customers to actually pay after registering a card. It means that we have to subscribe our customer.

Subscription

First, we need to create a product. The easiest ways to do it is through the Stripe dashboard. Then, we will attach 2 pricing plans to the product. For example in our product upload.express you can pay 7€ per month, or you can subscribe yearly for 60€. We need to go to our Stripe dashboard => Billing => Products and click on the button "New". Create your product with 2 pricing plan. I suggest you to choose explicit definition like "monthly" and "yearly" for your pricing plan nickname. If you add a yearly plan, don't forget to set the interval on 'yearly'. To add a pricing plan, you need to click on the product you just created and then click on the button "add a pricing plan".

Now we want to fetch the pricing plans we created in our application. We will be doing it on our page "success". So in app/controllers/billing_controller.rb in success:

def success
    @plans = Stripe::Plan.list.data
  end
Enter fullscreen mode Exit fullscreen mode

Now we need to subscribe our user to the plan that he will choose. So we need to create a new action which will link our customer to the plan and create a subscription.

In config/root.rb:

post '/subscription' => 'billing#subscribe', as: :subscribe
Enter fullscreen mode Exit fullscreen mode

In app/views/billing/success.html.erb we add:

<div class="row">
  <div class="col-md-12 mt-3 text-center">
    <h3>Now you need to choose your plan</h3>
  </div>
</div>
<!-- creating a form -->
<%=form_tag subscribe_path, method: :post do %>
<div class="row">
  <div class="col-md-4"></div>
  <div class="col-md-4 text-center">
    <div class="form-group">
      <select class="form-control" name="plan_id">
        <% @plans.each do |plan| %>
        <option value="<%= plan.id %>"><%= plan.amount/100 %>/€ <%= plan.nickname %></option>
        <!-- we pass id chosen by custmer as a value  to use it for subscription -->
        <%end%>
      </select>
    </div>
  </div>
  <div class="col-md-4"></div>
</div>
<div class="row">
  <div class="col-md-12 mt-2 text-center">
    <%= submit_tag 'Save changes', class: "btn btn-primary" %>
  </div>
</div>
<% end %>
Enter fullscreen mode Exit fullscreen mode

In app/controllers/billnig_controller.rb:

def subscribe
      if current_user.stripe_id.nil?
        redirect_to success_path, :flash => {:error => 'Firstly you need to enter your card'}
        return
      end
      #if there is no card

      customer = Stripe::Customer.new current_user.stripe_id
      #we define our customer

      subscriptions = Stripe::Subscription.list(customer: customer.id)
      subscriptions.each do |subscription|
        subscription.delete
      end
      #we delete all subscription that the customer has. We do this because we don't want that our customer to have multiple subscriptions

      plan_id = params[:plan_id]
      subscription = Stripe::Subscription.create({
                                                     customer: customer,
                                                     items: [{plan: plan_id}], })
   #we are creating a new subscription with the plan_id we took from our form

      subscription.save
      redirect_to success_path
    end
  end
Enter fullscreen mode Exit fullscreen mode

It's ready. Now we will check our app. Choose a pricing plan, submitting it.
To check everything worked, go to Stripe, Dashboard => Billing => Subscriptions. Our subscription should be here. If you change your plan and take a look at Stripe, the subscription should change as well.

That's it. You integrated Stripe on your Rails app. Congratulations!!!

P.S. It's my first technical article, I hope it was understandable and you enjoyed it!

💖 💪 🙅 🚩
ksushiva
Oksana Ivanchenko

Posted on March 20, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related