Stripe Guest Checkout


  • To allow anyone to buy a product


We will continue developing from where we left at part 9 of the Stripe Recurring Billing series of articles.

Step 1

Let's display the product details with price in the products show page. Create products controller:

rails g controller products show

Step 2

The product show page will have the link to 'Buy Now'. Create app/views/products/show.html.erb

<h1>Rails 4 Quickly</h1>

Price : $27

<%= link_to 'Buy Now', buy_path %>

Step 3

To load the product show page, let's define the route in config/routes.rb.

get 'products/show'

Step 4

Go to http://localhost:3000/products/show to view the product show page. When we click on the 'Buy Now' link it should load the payment form. Create app/views/sales/new.html.erb.

<form action="create" method="POST" id="payment-form">
  <span class="payment-errors"></span>
  <%= hidden_field_tag :authenticity_token, form_authenticity_token -%>

  <div class="form-row">
      <span>Card Number</span>
      <input type="text" size="20" data-stripe="number"/>

  <div class="form-row">
      <span>Expiration (MM/YYYY)</span>
      <input type="text" size="2" data-stripe="exp-month"/>
    <span> / </span>
    <input type="text" size="4" data-stripe="exp-year"/>

  <button type="submit">Submit Payment</button>

Step 5

Let's use a separate layout for checkout. The reason is that during checkout we will hide navigation links and any other distractions. It will also have the javascript related to checkout. Create app/views/layouts/sales.html.erb.

<!DOCTYPE html>
  <title>Buy Now</title>

  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application' %>

  <%= javascript_include_tag '' %>
  <%= tag :meta, :name => "stripe-key", :content => STRIPE_PUBLIC_KEY %>

  <script type="text/javascript">
    // This identifies your website in the createToken call below

    var stripeResponseHandler = function(status, response) {
      var $form = $('#payment-form');

      if (response.error) {
        // Show the errors on the form
        $form.find('button').prop('disabled', false);
      } else {
        // token contains id, last4, and card type
        var token =;
        // Insert the token into the form so it gets submitted to the server
        $form.append($('<input type="hidden" name="stripeToken" />').val(token));
        // and re-submit

    jQuery(function($) {
      $('#payment-form').submit(function(event) {
        var $form = $(this);

        // Disable the submit button to prevent repeated clicks
        $form.find('button').prop('disabled', true);

        Stripe.card.createToken($form, stripeResponseHandler);

        // Prevent the form from submitting with the default action
        return false;


  <%= csrf_meta_tags %>
<body data-no-turbolink>

<%= yield %>


Step 6

When this payment form is submitted to our server, it will go to sales controller, create action. Let's create the sales controller.

rails g controller sales new create

Step 7

The sales controller will load the payment form for new action and charge the customer when the payment form is posted to create action.

class SalesController < ApplicationController
  layout 'sales'

  def new

  def create
      stripe_gateway =
      stripe_gateway.charge(2700, params[:stripeToken])
    rescue Striped::CreditCardDeclined => e
    rescue Exception => e
      redisplay_form("Subscription failed. We have been notified about this problem.")

Step 8

Add a charge method to stripe_gateway.rb.

def charge(amount, stripe_token)
  # amount in cents
    charge = Stripe::Charge.create(amount:      amount, 
                                   currency:    "usd",
                                   card:        stripe_token,
                                   description: "")
  rescue Stripe::CardError => e
    # Since it's a decline, Stripe::CardError will be caught
    body = e.json_body
    err  = body[:error]

    @logger.error "Status is: #{e.http_status}"
    @logger.error "Type is: #{err[:type]}"
    @logger.error "Code is: #{err[:code]}"
    # param is '' in this case
    @logger.error "Param is: #{err[:param]}"
    @logger.error "Message is: #{err[:message]}" 

  rescue Exception => ex
    @logger.error "Purchase failed due to : #{ex.message}"  


Step 9

Add new routes for guest checkout in config/routes.rb.

get 'sales/new' => 'sales#new', as: :buy
post 'sales/create' 

Step 10

Create the integration test in spec/features/guest_checkout_spec.rb.

require 'rails_helper'
require 'spec_helper'

feature 'Guest Checkout' do
  scenario 'Complete purchase of one product', js: true do
    visit products_show_path
    click_link 'Buy Now'

    fill_in "Card Number", with: '4242424242424242' '10', from: "card_month" '2029', from: 'card_year'
    click_button 'Submit Payment'

    expect(page).to have_content('Receipt')

  scenario 'Fails due to credit card declined', js: true do
    visit products_show_path
    click_link 'Buy Now'

    fill_in "Card Number", with: '4000000000000002' '10', from: "card_month" '2029', from: 'card_year'
    click_button 'Submit Payment'

    expect(page).to have_content('Your card was declined.')

You can check the payments has been made successful by looking at the Payments tab in your Stripe dashboard. The complete source code that handles failure cases and has been refactored can be downloaded from using the commit hash 1c04385.


In this article, we implemented guest checkout feature. In the next article we will prompt the user to provide us an email and password at the end of the purchase so that they can view their purchases and use one-click checkout in subsequent purchases.

Related Articles

Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.