Rails + Stripe integration


Stripe?

Let’s check Stripe’s About page:

Stripe is a technology company that builds economic infrastructure for the internet. Businesses of every size—from new startups to public companies—use our software to accept payments and manage their businesses online.

Documentation

Main website:

https://stripe.com/

API:

https://stripe.com/docs/api

Demo:

Demo for ruby integration

Stripe Ruby

https://github.com/stripe/stripe-ruby

The Stripe Ruby library provides convenient access to the Stripe API from applications written in the Ruby language. It includes a pre-defined set of classes for API resources that initialize themselves dynamically from API responses which makes it compatible with a wide range of versions of the Stripe API.

The library also provides other features. For example:

  • Easy configuration path for fast setup and use.

  • Helpers for pagination.

  • Built-in mechanisms for the serialization of parameters according to the expectations of Stripe’s API.

Installation

Let’s start from Gemfile:

gem 'stripe'

Configuration

development.rb / production.rb

config.stripe.secret_key = Rails.application.credentials.stripe[:development][:secret_key]
config.stripe.publishable_key = Rails.application.credentials.stripe[:development][:publishable_key]

Implementation

Let’s modify our routes.rb file first:

  get '/card/new' => 'charges#new', as: :add_payment

And add some logic to the StripeChargesService class:

  class StripeChargesService
    DEFAULT_CURRENCY = 'usd'.freeze
    
    def initialize(params, user, order_id, plan_id)
      @stripe_email = params[:stripeEmail]
      @stripe_token = params[:stripeToken]
      @order = order_id
      @plan_id = plan_id
      @user = user
    end

    def call
      create_charge(find_customer)
    end

    private

    attr_accessor :user, :stripe_email, :stripe_token, :order

    def find_customer
      if user.stripe_token
        retrieve_customer(user.stripe_token)
      else
        create_customer
      end
    end

    def retrieve_customer(stripe_token)
      Stripe::Customer.retrieve(stripe_token) 
    end

    def create_customer
      customer = Stripe::Customer.create(
        email: stripe_email,
        source: stripe_token
      )
      # set stripe token
      user.update(stripe_token: customer.id)
      customer
    end

    def create_charge(customer)
      Stripe::Charge.create(
        customer: customer.id,
        amount: order_amount,
        description: @order,
        currency: DEFAULT_CURRENCY
      )
      # update user's subscription
      user.update(subscription_plan_end_date: Time.zone.now + SubscriptionPlan::LENGTH.days, subscription_plan_id: @plan_id)
    end

    def order_amount
      # order amount in cents
      Order.find_by(id: order).amount * 100
    end
  end

Charges controller implementation:

class ChargesController < ApplicationController
  before_action :authenticate_user!
  rescue_from Stripe::CardError, with: :catch_exception
  before_action :set_subscription_plan
  
  def new
    @order = Order.create(user_id: current_user.id, price: @plan&.price, amount: @plan&.price)
    session[:order] = @order.to_param
  end

  def create
    result = ::StripeChargesService.new(charges_params, current_user, session[:order], @plan&.id).call
    redirect_to cabinet_path, flash: { success: "Thanks for your order" }
  end

  private

  def set_subscription_plan
    @plan = SubscriptionPlan.default
  end

  def charges_params
    params.permit(:stripeEmail, :stripeToken, :order_id)
  end

  def catch_exception(exception)
    flash[:error] = exception.message
  end
end

View for our route: charges/new.html.erb:

<section class="section section--hero contact" id="contact">
  <h1 class='text-center'>Plan purchase page</h1>
  <div class="container center">
    <%= form_tag charges_path do %>
      <article>
        <% if flash[:error].present? %>
        <div id="error_explanation">
          <p><%= flash[:error] %></p>
        </div>
        <% end %>
        <label class="amount">
        <span>Amount: $<%= @order.price %></span>
        </label>
      </article>
      <script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
        data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
        data-description="A month's subscription"
        data-amount="<%= @order.price * 100 %>"
        data-locale="auto"></script>
    <% end %>
  </div>
</section>

Testing

Navigate to the /card/new page in your app:

image

Make payment with test card:

image

Conclusion

That’s it - super simple, just a few files, configuration changes and your payments are ready to go!