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!