Making Users Pay – Building a Site with Stripe and Devise

I took the One Month Rails tutorial and I’m continuing to build out the site. I wanted to add a credit card charge for a SaaS model business and I’d heard great things about Stripe, so I used them. The One Month Rails tutorial basically builds a Pinterest clone and uses Devise for a User model and authentication. Here is an example from the OMR creator.

Stripe has great documentation and there are tutorials out there, but nothing was perfect for Devise + Stripe, and I’m also working with Rails 4 and Ruby 2, to make things a little more interested.

I started by building a single credit card charge setup from the Stripe tutorial. This works well and is fairly straightforward. The one tricky piece for me was pushing through the ENV variables. It was fairly straightforward to do this manually when you start the rails server (below), but getting the ENV variables in there automatically at setup was harder.

PUBLISHABLE_KEY=pk_foo SECRET_KEY=sk_bar rails s

The answer was to get the ENV variables into my ~/.bash_profile which took some yak shaving, but here is the best place to start.

So far things have been easy, but we just followed the Stripe tutorial, there is no User involved yet. For simplicity, maybe user experience, I want:

  • The User to enter their credit card at signup,
  • A single payment plan (for now),
  • Every User to give a credit card.

So if the person is a User and can login, then they have paid with a credit card. Stripe has really good subscriptions built in, and again, their documentation is really good, but the documentation isn’t the complete picture and it takes some thought to bring in the Devise setup.

I found an interesting tutorial that outlined how I wanted things to work, although simply copying and pasting into my app wasn’t going to work and the tutorial is incomplete. It helped me understand a few of the processes better and seemed to have the right approach.

The approach is to add the credit card forms to the signup page. When the user presses submit, Stripe Javascript grabs the credit card information and submits it to Stripe (my app never sees it) and returns a token which is entered into a hidden field in the form. The JS then submits the form to create a User. Finally we run an after_create callback on the User model to charge the card and subscribe the User (and Stripe Customer) to the subscription plan.

I found myself continuously referencing the Stripe tutorials along with the guide from Clogeny.
Stripe = and
Clogeny =

Following the Clogeny process with some corrections and different ideas…

Step 1: Stripe Gem- Installing the gem should be done already if you’ve built the single charge from the Stripe guide.

Step 2: ENV variables  – These should already be done from the single-charge test. You also want to follow best practices for ENV variable and not put the in code that might end up on github. Also pay attention to the different variable names in this guide. It looks like the Stripe.api_key (Clogeny) is related to the Stripe secret key, but the initializer is different in the Clogeny guide. I stuck with the initializer code from the Stripe guide.

Step 3: Include the JS in your layouts file. I needed to change the syntax to get mine to work. Here is the code that worked for me.


<%= javascript_include_tag ";, type: 'text/javascript' %>
view raw gistfile1.txt hosted with ❤ by GitHub

Step 4: Add the Stripe publishable_key to the application page. Mine looks much different from the Clogeny guide.

app/views/layouts/application.html.erb (same page as above)

<%= javascript_tag "Stripe.publishableKey = '#{Rails.configuration.stripe[:publishable_key]}';", type: 'text/javascript' %>
view raw gistfile1.txt hosted with ❤ by GitHub

I’m not sure how much the order matters, but you can see my code here At this point I would load the signup page and see if the publishable_key shows up when you Inspect the page elements.

Step 5: New User Form – Now we are getting somewhere. We want to add the hidden field and the credit card form to our signup page. I already had the User model form built so I tried to match the styles as much as possible.


#At the top of the form
<%= f.hidden_field :stripe_card_token %>
#right above the submit button
<% if @user.stripe_card_token.present? %>
Credit Card is on File
<% else %>
<div class='form-group'>
<%= label_tag :card_number, "Credit Card Number" %>
<%= text_field_tag :card_number, nil, name: nil, class: 'form-control' %>
<div class='form-group'>
<%= label_tag :card_code, "Security Code on Card (CVV)" %>
<%= text_field_tag :card_code, nil, name: nil %>
<%= label_tag :card_month, "Card Expiration" %>
<%= select_month nil, {add_month_numbers: true}, {name: nil, id: "card_month"} %>
<%= select_year nil, {start_year:, end_year:}, {name: nil, id: "card_year"} %>
<% end %>
view raw gistfile1.txt hosted with ❤ by GitHub

Step 6: JS for CC form – This had me confused for a week or so. We are adding Javascript to handle the form that we just created. To review, when the user clicks submit the JS will pause the form, take the credit card information and submit it to Stripe. Stripe will reply with the credit card token, fill it to the hidden field in the form and then submit the form.

I created a file for this app/assets/javascript/

jQuery ->
user =
setupForm: ->
$('#new_user').submit ->
$('input[type=submit]').attr('disabled', true)
if $('#card_number').length
processCard: ->
card =
number: $('#card_number').val()
cvc: $('#card_code').val()
expMonth: $('#card_month').val()
expYear: $('#card_year').val()
Stripe.createToken(card, user.handleStripeResponse)
handleStripeResponse: (status, response) ->
if status == 200
$('input[type=submit]').attr('disabled', false)
view raw gistfile1.txt hosted with ❤ by GitHub

Step 7: Controllers – It was a little tricky figuring out Controller actions, once the form is submitted. Devise doesn’t create a User Controller, so this takes a little work to figure things out. We need to add a stripe_card_token to the User Model, but we can’t just list that as attr_accessible. Rails 4 introduces strong parameters and setting up attributes on the Devise User is a little different. Here is the documentation.

This code in app/controllers/application_controller.rb will let the app add or edit parameters on the User Model during signup and edit actions.

class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
#This allows the attributes to be accessible at sign up. I had to add email and password after adding token.
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :stripe_card_token, :email, :password) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:name, :stripe_card_token) }
view raw gistfile1.txt hosted with ❤ by GitHub

Step 8: User Model – Before the user is created Stripe is grabbing the credit card information, supplying a token and we attach that token to the User Model as the User is created. We still need to subscribe the User to the subscription plan and charge the card. Stripe has great documentation on this I’d recommend creating the plan on the dashboard, as it is so easy.

My code matches Stripe’s example pretty closely, but there are two differences. First, I don’t have the API key in my model, but it still works. Second, for the :plan attribute, Stripe wants the ID number, not the plan name. I think their documentation is incorrect in that respect.


class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :pins
validates :name, presence: true
after_create :create_a_customer
def create_a_customer
token = self.stripe_card_token
customer = Stripe::Customer.create(
:card => token,
:plan => 120,
:email =>
view raw gistfile1.txt hosted with ❤ by GitHub

Once this is set up you should be able to test out the signup process and see the charges and Customers being added on Stripe.

Step 9: Error Messages – After putting through a number of successful transactions and watching the fake money pile up on Stripe, I decided to test out a bad credit card number and I didn’t see what I expected. My normal debugging methods didn’t work because the problem was in the JS and nothing appeared on the server logs. A friend helped me make this work.

Here is the code in app/assets/javascripts/ – specifically what is coming after the ‘else’ – if it’s a bad credit card.

handleStripeResponse: (status, response) ->
if status == 200
$('input[type=submit]').attr('disabled', false)
view raw gistfile1.txt hosted with ❤ by GitHub

This says that if Stripe does not return a 200 status (good credit card), then we should find the div with class=stripe_error and display it. It will look for this div on the User registration page.


<div id='stripe_error' class="alert alert-info" style='display:none'>
view raw gistfile1.txt hosted with ❤ by GitHub

There is some tricky stuff, specifically ‘style=’display:none” in the div, and canceling that in the JS, but if you copy what I have it should work well.

This is a lot of information, and I hope it helps. There is still more (like getting this to run on Heroku), but this is a good start. If you’re building something cool and this helps, please link to it in the comments. Here is the code for my app so far.

If you made it this far, please give me an upvote on HN

This entry was posted in rails. Bookmark the permalink.

15 Responses to Making Users Pay – Building a Site with Stripe and Devise

  1. Pingback: Making Users Pay. Stripe + Devise in a Rails 4 App | Enjoying The Moment

  2. Matthew says:

    Hi – this is great stuff except I keep on getting the
    error undefined method `stripe_card_token’ for #
    when i try to access my form – any idea why?

    • mikesabat says:

      Hi Matthew,

      Sorry that it’s taken me so long to reply. It’s really hard to help without a lot more information. Have you made stripe_card_token a field on your User model? Have you allowed this field to be added – attr_accessible?

    • Ching Casanova Tapia says:

      Did you figure this out ? I’m running into the same error, even after adding stripe_card_token to Devise permitted params in the application controller.

      • Hey, if you’re still trying to figure this out… You need to run a migration to add the stripe_card_token to the users table.

        $ rails g migration add_stripe_card_token_to_users stripe_card_token:string
        $ rake db:migrate

        PS. Mike, thank you, this was a BIG help 🙂

  3. Just wanted to thank you for writing this Stripe tutorial. It really saved me a lot of time today.

  4. Preston says:

    Thanks for the tutorial! I was definitely getting frustrated attempting to integrate Stripe with Devise. Your tutorial helped me get past that and back into a happy place!

  5. preeminentproductions says:

    Awesome! Thank you for this. How are things going for you, as far as learning Rails goes? I’m beginning to learn Rails, with a web app idea in mind. Finding it a bit hard some days, but not going to give up. I’ll definitely bookmark this post.

  6. David says:

    This is a great tutorial — thank you for putting it together!

    I was really struggling to get Devise and Stripe to play well together, and this tutorial is the only one that actually helped me.

    Thanks again. –David

  7. Nayib Abdala says:

    Thanks a lot Mike, really helpful.

    I went though the process and then when I fulfil the form Im getting this :

    Invalid card object: must be a dictionary or string. See API docs at

    Extracted source (around line #20):

    token = self.stripe_card_token

    customer = Stripe::Customer.create(
    :card => token,
    :plan => 120,
    :email =>

    any idea why this is happening ?


  8. Danny says:


    Thanks for the great tutorial, as others have said its saved me a lot of time and no doubt a lot of head scratching today. Everything is working perfectly for me.

    All the best

  9. 0MNPB10 Zamuar Bosmir says:


Leave a Reply

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

You are commenting using your 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