technology

Ruby on Rails 5 with Stripe Element Integration

This tutorial shows you how I set up Stripe Element with Ruby on Rails 5 for my project For A Piece, a job board for programmers looking for companies that offer equity. 


Setup

This project is using Ruby 2.6.1 and Rails 5.2.3. This tutorial expects you to have an initial Rails app. If you’re copying&pasting, you might need to update the quotes.

Step 1.

Add gem 'stripe' to your gemfile and bundle install.

Step 2.

Add the code to your application.html.erb. I’ve added this inside the <header></header> section after <%= yield :javascript %>. I’ve added 2 environments as I do not wish to charge a real card during development. You’re going to see if/else in Step 2 and Step 3. This isn’t required and you can choose how to set up your own environment.

<%= yield :javascript %>
<%= yield :head %>
<%= javascript_include_tag 'application', "https://js.stripe.com/v2/", "https://js.stripe.com/v3/", 'data-turbolinks-track': 'reload' %>

<% if Rails.env == 'development' %>
    <%= tag :meta, name: "stripe-public-key", content: Rails.application.credentials.stripe_test[:publishable_key] %>
<% else %>
    <%= tag :meta, name: "stripe-public-key", content: Rails.application.credentials.stripe_production[:publishable_key] %>
<% end %>

Step 3.

Initialize stripe.rb. Create a file config/initializers/stripe.rb 

require "stripe"

if Rails.env == 'development'
  Rails.configuration.stripe = {
    :publishable_key => Rails.application.credentials.stripe_test[:publishable_key],
    :secret_key      => Rails.application.credentials.stripe_test[:secret_key]
  }
  Stripe.api_key = Rails.application.credentials.stripe_test[:secret_key]
else
  Rails.configuration.stripe = {
    :publishable_key => Rails.application.credentials.stripe_production[:publishable_key],
    :secret_key      => Rails.application.credentials.stripe_production[:secret_key]
  }
  Stripe.api_key = Rails.application.credentials.stripe_production[:secret_key]
end

Step 4.

Add Routes, Controller, and View. I chose Purchases, but you can call this any name that makes sense to you. 

In the routes.rb add resources: purchases, only: [:new, :create]

In the purchases_controller.rb add

class PurchasesController < ApplicationController
  def new
  end

  def create
    token = params[:stripeToken]
    if token
      Stripe::Charge.create({
        amount: '100.00',
        currency: "usd",
        description: 'your product description',
        source: token
      })
    else
      flash[:alert] = 'Something went wrong with your payment. Please try again later'
      render 'new'
    end
  end
end

In views/purchases/new.html.erb add

<%= form_tag purchases_path, id: "payment-form" do |form| %>
<div class="group">
<label>
<span>Name</span>
<input id="name" class="field" placeholder="Jane Doe" />
</label>
<label>
<span>Card</span>
<div id="card-element" class="field"></div>
</label>
</div>
<button class='subscription-button'>Place your order</button>
<div class="outcome">
<div id="card-errors" role="alert"></div>
<div class="success">
Success! Your Stripe token is <span class="token"></span>
</div>
</div>
<% end %>

Step 5

The final piece of the puzzle. Adding Javascript to stylize, embed, and submit the form. In the path assets/javascripts/subscriptions.js add

document.addEventListener("turbolinks:load", function() {
  var public_key = document.querySelector("meta[name='stripe-public-key']").content;
  var stripe = Stripe(public_key);
  var elements = stripe.elements();

// Custom styling can be passed to options when creating an Element.
  var style = {
    base: {
      // Add your base input styles here. For example:
      fontSize: '18px',
    }
  };

 // Create an instance of the card Element
  var card = elements.create('card', {style: style});
 // Add an instance of the card Element into the `card-element` <div>
  card.mount('#card-element');
  // card.mount('#example1-card');
 card.addEventListener('change', function(event) {
    var displayError = document.getElementById('card-errors');
    if (event.error) {
      displayError.textContent = event.error.message;
    } else {
      displayError.textContent = '';
    }
  });
  // Create a token or display an error when the form is submitted.
  var form = document.getElementById('payment-form');
  form.addEventListener('submit', function(event) {
    var name = document.getElementById('name').value;
    if (!name) {
      var errorElement = document.getElementById('card-errors');
      errorElement.textContent = "You must enter a name.";
      errorElement.classList.add('visible');
      return;
    } else {
      event.preventDefault();
      var options = {
        name: name,
      };
 stripe.createToken(card, options).then(function(result) {
        if (result.error) {
          // Inform the user if there was an error
          var errorElement = document.getElementById('card-errors');
          errorElement.textContent = result.error.message;
        } else {
          // Send the token to your server
          stripeTokenHandler(result.token);
        }
      });
    }
  });
}); 
 function stripeTokenHandler(token) {
  // Insert the token ID into the form so it gets submitted to the server
  var form = document.getElementById('payment-form');
  var hiddenInput = document.createElement('input');
  hiddenInput.setAttribute('type', 'hidden');
  hiddenInput.setAttribute('name', 'stripeToken');
  hiddenInput.setAttribute('value', token.id);
  form.appendChild(hiddenInput);
 ["brand", "exp_month", "exp_year", "last4"].forEach(function(field) {
    addFieldToForm(form, token, field);
  });
 // Submit the form
  form.submit();
}
 function addFieldToForm(form, token, field) {
  var hiddenInput = document.createElement('input');
  hiddenInput.setAttribute('type', 'hidden');
  hiddenInput.setAttribute('name', "user[card_" + field + "]");
  hiddenInput.setAttribute('value', token.card[field]);
  form.appendChild(hiddenInput);
} 

That’s all folks! Now when you visit purchases#new you will see a form with a name and credit card field that is already styled right on your page. No more ugly modals! 



If you’re looking for a new remote programming job that offers equity come checkout For A Piece, it’s free! 

Cheers!
John Moon

Standard

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s