Oksana Ivanchenko
Posted on March 20, 2019
Creating a workspace
We will start by creating a Ruby on Rails application
$ rails new tuto
$ cd tuto
$ bundle install
Then, we will generate a controller named Billing with the help of
rails generate
$ rails generate controller Billing
After that, we're going to add route in config/routes.rb :
root 'billing#index', as: :billing
To finish, we have to create an empty action index in app/controllers/billing_controller.rb:
class BillingController < ApplicationController
def index
end
end
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>
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'
To setup devise we have to type this in the terminal:
$ bundle install
$ rails g devise:install
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 }
The next step is to create a User model:
$ rails g devise User
$ rails db:migrate
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
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>
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'.
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
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_...'
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' %>
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]
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]
Finally, we will add the stripe gem in our Gemfile
gem 'stripe-rails'
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`
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
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();
});
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 %>
After that in terminal we run 'rails s' and after clicking on our button we should have something like this:
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
In config/routes.rb we are creating a new route:
post "/card" => "billing#create_card", as: :create_payment_method
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
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 %>
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
In app/controllers/billing_controller.rb:
def success
end
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>
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.
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
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
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 %>
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
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!
Posted on March 20, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.