#288 Billing with Stripe
Stripe is a full-stack payment solution with very reasonable pricing and is easy to setup. See how to add it to a Rails application here. Currently only available in the US.
- Download:
- source codeProject Files in Zip (101 KB)
- mp4Full Size H.264 Video (45.3 MB)
- m4vSmaller H.264 Video (21.1 MB)
- webmFull Size VP8 Video (22.9 MB)
- ogvFull Size Theora Video (52.7 MB)
Ahhhhhhhhhhhhhhh only US :(
Sign up here
https://stripe.com/help/global
Maybe it will encourage them to speed things along :)
Can't use it here, oh well :(
Just what I was about to use! Always on the ball.
Very nice recommendation! I hadn't seen stripe before and have been grudgingly testing other payment processors. This looks fantastic and very reasonable
I cannot believe how easy Stripe has made ecomm for developers! Thanks for the intro to this killer offering.
Great video on a great service. For those outside the US, know that Stripe is working on it. :)
Stripe on HackerNews
Thanks!
A couple of questions:
Why do you pass the API key into the meta tag and grab it in the javascript instead of just hard coding it into the javascript?
In the "save_with_payment" instance method, why do you reference "self" when assigning the customer id to an attribute of the instance.
In the new action of the subscriptions controller, why do you need "@subscription = plan.subscriptions.build"?
@1: Because this makes it easily configurable from, for example yml file.
@3: plan.subscriptions.build is equivalent of Subscription.new(plan: plan)
Also means you could re-use the same JS file with another project. For example if you build a merchant hosting site where each of your users can create their own merchant site hosted by you, you could change easily who the payment is going to while using the same JS file for everyone.
Some peculiarities of Ruby's parser (nothing to do with Rails). Without
self
, it would think you are trying to assign a localstripe_customer_token
variable instead of using the implicitly definedself.stripe_customer_token=
attribute writer.Hmm... Personally I think I would have added the erb-preprocessor to the coffeescript-file and referenced the constant through an erb block:
@1. I was wondering the same. Values like that are prone to change so it's best to have them in some sort of global variable. I had always used the approach:
Stripe.setPublishableKey('<%= STRIPE_PUBLIC_KEY %>')
I see one advantage to the meta tag version in that you don't have to give your coffeescript file an .erb extension. This can confuse certain text editors. Other than that, I can't figure out why he would have used a meta tag.
meltzerj,
Regarding question 2...
You can see that Ryan adds a stripe_customer_token to the subscription model:
rails g migration add_stripe_to_subscriptions stripe_customer_token:string
self.stripe_customer_token = customer.id
sets that in the current subscription object (taken from the object returned by Stripe::Customer.create() method).Hope that helps!
Regarding question 3...
It has to do with the model associations.
When you say:
@subscription = plan.subscriptions.build
you are really saying something like:
@subscription = Subscription.new(plan_id: id)
You are wanting an object that already has the association (to a particular Plan, in this case) set. This object is passed to the form with the association set -- so it knows how this particular Subscription object relates to a specific Plan object.
see: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Got it. But in the create action, isn't he already setting this association when passing in the params hash into Subscription.new()? Doesn't the params hash contain the plan id since the form has a hidden field for the plan id (f.hidden_field :plan_id)? In other words, is he setting the association twice?
The hidden plan_id field is actually set because of the call to
plan.subscriptions.build
method. Without that call, :plan_id hidden field within the form would be nil.You're using the
build
method to set that value within the form. That way, when you call theSubscription.new
method using the params hash, it will know which plan the subscription is associated with.Ahh ok, now I understand. Thanks!
What I would have done was, in the new action, "@plan_id = params[:plan_id]"
Then in the form: <%= f.hidden_field :plan_id, :value => @plan_id %>
To me it seems like less magic is going on by doing it that way.
Yeah, the
build
method is ActiveRecord magic. Good magic! :)In your code (
@plan_id = params[:plan_id]
), theparams[:plan_id]
is not going to do what you think. The first time the form is shown,params[:plan_id]
is going to be nil -- it doesn't exist in theparams
hash (yet) because it hasn't been set to anything.It's best to use the
build
method. It saves you from these bugs that are hard to track down.Either way, you get the gist of what's going on.
Isn't
params[:plan_id]
passed in through the url when the form is shown?But yea, I get the gist of it.
The
params
hash is used to get data that's been posted from a form. You may have seenparams
being sent back to the browser but that's typically used to re-display a form using data that's already been sent (i.e. when a person has forgotten to input a field).You can probably set the plan_id using the params hash but it's more work and isn't the idiomatic 'Rails Way.'
See: ActionController : Parameters
Which is better to use? Braintree or stripe?
I haven't used either, but based on their prices i would recommend Stripe
Braintree looks way better than say authorize.net, but Stripe has these advantages:
- Developer friendly, less headaches
- No monthly fees
- Very easy setup
Am I correct in thinking that I could bypass the credit card payment altogether by manually setting a value to the hidden token field (using javascript console or similar before submitting)?
That would likely trigger a
Stripe::InvalidRequestError
when trying toStripe::Customer.create( card: stripe_card_token)
with your fakedstripe_card_token
, and thus prevent saving the subscription record.Thanks.
A couple of questions about your implementation of
save_with_subscription
:1) you use
save!
in your implementation. Would it be better to either return the result ofsave
, or change the name of the function tosave_with_subscription!
, in order to better follow conventions?2) I noticed that you don't handle failures to
save
. In the real world, we would need to handle that failure by manually rolling back the subscription creation. Or is there a better way to handle this with Stripe?Good questions.
1) The
save_with_subscription
method is doing avalid?
check and therefore does not raise an exception when validation fails. I am doingsave!
inside thevalid?
check because if the data somehow got invalid between then and now I want to know about it.2) Handling cases where save fails is tricky because the transaction has already been placed. I suppose you could do an automatic void but then there's always the possibility of that not going through. I think this is better to be handled manually. If
save
fails then it will raise an exception and hopefully there is something setup to notify you when an exception occurs so you can handle the situation manually (such as voiding the transaction).How would you test if the exception was successfully raised and errors logged using RSpec?
Thanks in advance!
After, pulling my hair for a few hours, this is how I am testing it:
The tricky part was creating the exception, I had to take a look at this
Mocking a third party API is a very bad practice. However you can replace the mock with stub and raise the exception.
Since jQuery 1.6 you should use
prop()
method to set boolean attributes:$('input[type=submit]').prop('disabled', true)
Or
$('input[type=submit]').disable()
to get the same behavior and 'disabled' css style class added.Thanks for posting this, I had forgotten about the
prop
method.really like the coffee script stuff in this, I learned a lot
I use Braintree, which I love. But it has two problems, first I pay a monthly fee, second I have to pay another monthly fee for each application I write (since the name on the account has to be "prominently displayed on your site", it has to be the application name not a company name, and hence not sharable across applications).
With this, I get the pricing of PayPal but the customer convenience of entering credit cards directly on my site. Way cool. Can hardly wait to set it up.
For those who aren't using CoffeeScript, here is the compiled subscriptions.js file: https://gist.github.com/1307748
Great episode, btw. I'm loving Stripe so far.
Can anyone expand here on why using a before create callback is a bad idea? In looking at the Stripe monospace-rails example app, it handles a user updating their card quite nicely, which wasn't covered in this episode. However, a before_save call is how they're handling it.
Awesome episode, I'm so grateful for this intro into Stripe
Thanks,
if $('#card_number').length
was always returning 1 for me, even if there were no values in the input.
So I used
if $('#card_number').val().length > 0
and that worked.
$('#card_number').length
is returning the size of the jQuery collection -- just selecting an ID, so yes, the length will be 1.$('#card_number').val()
is the value currently which the input field is currently holdingHi there, Ryan says in the video that we should gitignore the stripe api key initializer file so to avoid it being divulge in general development. But when the time comes that we do need to push it to the production server, like Heroku which I use, how do you go about it? do you do something with git add just for that one push?
Unless your code is open source, I wouldn't bother with that. I prefer to store those setting in environment files.
production.rb:
STRIPE_SECREY_KEY = "XXXX"
development.rb:
STRIPE_SECREY_KEY = "YYYY"
initializers/stripe.rb:
Stripe.api_key = STRIPE_SECRET_KEY
Now if your code is open source... yes.
http://stackoverflow.com/questions/6113042/where-to-store-sensitive-data-in-public-rails-app
Generally you use Capistrano to copy it over. For Heroku you would use their config variables: http://devcenter.heroku.com/articles/config-vars
heroku config:add STRIPE_TOKEN=123
@tybro0103 mentioned you only need secure it when you're doing open source code. However I personally don't want my fellow developers to be able to refund payments, cancel my customers, or charge my customers copious amounts. Keep it secret, keep it safe.
Great episode Ryan!
It would be AWESOME if you could go into more depth in stripe, even if it is with the pro subscription. It would be very very useful.
Thanks!
Useless to anyone outside of the US :(
I was very excited about this, but when I contacted Stripe, they said they will let me know when they will be available in Europe (England in my case) but I'm not holding my breath.
Ryan, for this one, I really wish you had included some info on how to test.
I'm curious about the testing aspect too, especially the
save_with_payment
method in the Subscription model.Anyone have an RSpec example for this or a resource for the underlying concept?
I have written an article on integration testing using Capybara and RSpec, check out:Stripe Recurring Billing Part 3
Have you considered doing an episode on the rest of Stripe Integration? Things like:
upgrades, downgrades, cancellations, receiving webhooks, updating cc info, handling failed payments, etc.? I've built several subscription sites with authorize.net and these things are always such a pain. I would love find out if there are any best practices and see how other Rails developers handle such things.
I'm using Stripe to put together a SaaS application. We create a customer (Stripe) and link it to the user (our app). We handle the upgrading, trials, and charging. Just charge expiring customers in our crontab.
Any reason this would not working in production mode? I know the code gets minified. I altered the code for new user but works great in development. I set break points in the deminified javascript and it is as it does not even get called. The new_user id is there in the HTML. This might be too heavy to post here but I feel it is something stupid that I missing. Thanks for any help. Here is a portion of the deminified javascript:
I took Justin's subscriptions.js file above and converted for new user and got the same results. Works fine in development mode but is essentially ignored in production mode. Other js files are working fine. Must be something in the asset pipeline configuration. Anybody else using this in production mode?
Sorry for blowing up the board. I found the problem. One of the Twitter Bootstrap js files was conflicting with it. It is all working now.
How did you get around the Twitter Bootstrap and Stripe issue?
I'm having issues in development mode where it's always returning an error with Invalid Token on the card. It's like it's trying to submit to Stripe with no card information.
I found that in order for the card to process with your example, I had to put the variables in the card around
{ }
and also delimit them with a comma.You can also add another newline between the object declaration and the call to createToken.
Hey guys,
When I remotely load the new form and click on submit button; the javascript is simply ignored and the form is submitted to the controller. This leads to "Invalid token" error as no token was generated. But if I don't load the form via remotely, everything works fine. Any idea what am I doing wrong?
This is how I render the new form:
I noticed this is all done without SSL on the web app. Yes, the JSON calls to Stripe are done via SSL, but that's not obvious to the casual user. Would you recommend that the payment form be served via SSL, even if just to give the user some reassurance?
Will it doesn't say (AFAICS) in the stripe documentation that Stripe will not work in production without it, the docs do recommend having it. (My guess is that it will not work in prod mode without SSL)
https://stripe.com/help/ssl
In pro episode 357 Adding SSL, Ryan suggests going as far as making all sites that collect user information or require login of some sort to use SSL.
Stripe is now in Beta for Canada :)
Hi guys, does anyone have a good resource on creating multiple user plans and subscriptions? I've followed Michael Hartl's tutorial, and am on to my own app, but I don't know where to begin with multiple user roles.
In subscriptions.js.coffee:
You need to add 'false' to prevent the submit button from working and going to the next page.
For details that are not covered in the RailsCast, take a look at the open source example application for a Rails Membership/Subscription/SaaS Site from the RailsApps project. It comes with a tutorial that goes into detail about how to integrate Stripe with Devise, shows how to use Stripe webhooks, and explains the implementation in detail.
Question: In the controller, I can't do
@subscription = Subscription.new(params[:subscription])
because plan_id is not accessible. How did you get around this?
and :stripe_card_token for that matter.
This is an outstanding episode! So concise and helpful.
Just one question... One small JQuery-related thing through me for a loop.
in /app/assets/javascripts/subscriptions.js.coffee, why was the change made to:
$('#new_subscription')[0].submit()
?
What is the [0] for? I have not seen that before.
For information, the app crashes when handling failure scenario provided by stripe. https://stripe.com/docs/testing
If you are receiving an error message similar to
An unexpected error has occurred. We have been notified of the problem.
then this could be due to the fact that you have updated your API. I have a support ticket into Stripe to see what can be done about this. However, currently, I've just bypassed this error using the below coffeescript.
I'm getting the same error when I try it. I just watched the video, and ran through the tutorial, so developer error may be the cause, but if you find a solution before I do...please post.
I just ran through this tutorial two days ago and had it working. But tonight my app started throwing off that same error, which leads me to believe you are correct that it is on the Stripe side. Please let me know if they get back to you on that support ticket.
One source of errors at my end was an assumption that card values (number, expiration, CVC) that passed a token request (returned 200):
return Stripe.createToken(card, order.handleStripeResponse);
would necessarily be a valid card token for:
This is NOT the case. Stripe.createToken does very minimal checking of the card values. From Stripe, all it is doing is making sure the card number is long ENOUGH (not the right length) and passes the Luhn test. Adding extra digits to the card can result in a 200 response, even though the card number is not valid. Stripe.createToken only checks that the expiration date is in the future, not that it is the correct date associated with the card.
It is not UNTIL Customer.create that the card is actually determined to be a real, valid card. Therefore, Customer.create can fail with what Stripe has provided as a valid card token.
Try, for instance, what happens if you use card number:
4242 4242 4242 4242 42
rather than:
4242 4242 4242 4242
Are you seeing the error with all browsers?
I'm getting with Firefox (with subsequent failure to create to new entry--order in my case rather than subscription), Chrome (here the new entry is created...)
And not seeing the error with Safari or IE.
I had not tried it with different browsers. I'm on IE 10.
I am testing on a legacy machine running XP (so IE 8) and not seeing the error. Should probably try on a machine running IE 10.
I cannot track down what I did (probably upgraded the Stripe API on our account), but as I worked back through my Stripe test logs, about 2 weeks ago each execution of:
Stripe.createToken(card, order.handleStripeResponse)
in my coffee script started generating two tokens at the Stripe end, not one. Don't know if it is related because with FireFox (both XP and Mac) the "An unexpected error has occurred. We have been notified of the problem.", does not generate a token log entry (or 2) in the Stripe log, but :
gets to the .submit() line, presumably because of a status ==200.
With Chrome, the error is there, but the token is in the hidden token field on the form and dismissing the error results in a new order being properly created. With FireFox, there doesn't seem to be anything in the hidden token field and order creation fails (but not by redirecting back to the "submit order" page.
Kobaltz,
Where are you putting the
clause in your script below?
After you test for "An unexpected error..." or before?
Did anyone track down this problem? I'm still trying to find a solution.
I am also getting the "An unexpected error has occurred. We have been notified of the problem.". Has anyone found a solution???
I'd suggest you guys just check out the new Stripe docs. They're very clear and have been updated over the past year and a half.
Just an FYI...I finally gave up on solving this problem, and worked through the docs and examples on the stripe site. It's now working fine after I ditched jQuery and used plain old js. I know seems crazy.
Stripe is awesome but they don't accept eChecks, yet. Is there another option that will allow ACH payment processing?
Stripe in Beta in UK.
confirmed - got my invite today! let's play...
I had the same error - "An unexpected error has occurred. We have been notified of the problem."
I got it working following the Stripe tutorial (https://stripe.com/docs/tutorials/forms) which uses https://js.stripe.com/v2/ and has some differences, including the 'data-stripe' attribute.
Tried to add drop-downs for the expiry date and was told by Stripe support "The data-stripe version of Stripe.js doesn't support dropdowns yet (we're working to fix this). In the meantime, feel free to copy the old method from https://gist.github.com/boucher/1750368)"
Does anyone have any examples of using stripe in a nested form. For example I have a nested form that contains an affiliate which has_one subscription. This same affiliate also has a captcha. The problem is that that if i do something like
and there are errors with my affiliate model but not my subscription model, then the subscription gets saved without an affiliate_id, a customer is created by stripe and the same form re-renders populated with the stripe_token and the newly created subscription_id. Once the form is resubmitted and Affiliate.new(params[:affiliate] ) is called in the create action it is raising an error like
Couldn't find Subscription with ID=31 for Affiliate with ID=
since there is no affiliate_id in the subscription row in the DB. It would really be nice to see an example of this inside a nested form. Help!Great lesson, Ryan. I share this because it slipped by me and it took a lot of troubleshooting to catch it. Hope it helps someone else. When you coded setupForm with $('#new_subscription').submit -> you said, "(#new_subscription), that is the default name that Rails will give it...". The Rails default name for my form wasn't working and I actually had to ID tag the whole form.
Ive been getting the following error "You must supply a valid card" i cant seem to find any info on the stripe docs, have any of you been getting this lately?
Has anyone figured out how to implement this for one-time payments as opposed to recurring/subscription?
I would point you here: https://stripe.com/docs/checkout and here https://stripe.com/docs/stripe.js.
I would use the first link and totally forgot about developing the forms and the needed js.
Hi guys,
great tutorial. I setup the subscription part and before integrating the stripe part, I want to add an association to the user. I have a user model with devise and want people to create a user account before being able to make a subscription. The problem is, that I am confused on how to get the user_id into the subscription. Can someone help?
sorry to bother you guys again. I got to the very end with everything working, but now receiving Stripe::InvalidRequestError in SubscriptionsController#create
my error highlighted this.
customer = Stripe::Customer.create(description: email, plan: plan_id, card: stripe_card_token)
This is an awesome tutorial!
Firefox does not seem to clear the credit card number fields before submitting (even though name: nil on each one) as I discovered CC #'s in my logfiles!
So I modified my orders.js.coffee as follows:
typo in asciicast:
#{e.mesage}
->#{e.message}
Thanks for reporting that error, I have corrected it.
Hi Guys. Followed the tutorial above and getting an error with the stripe_token not being passed. Am getting the following error
```jQuery ->
Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'))
subscription.setupForm()
subscription =
setupForm: ->
$('#new_subscription').submit ->
$('input[type=submit]').attr('disabled', true)
subscription.processCard()
false
processCard: ->
card =
{number: $('#card_number').val(),
cvc: $('#card_code').val(),
expMonth: $('#card_month').val(),
expYear: $('#card_year').val()}
Stripe.createToken(card, subscription.handleStripeResponse)
handleStripeResponse: (status, response) ->
if status == 200
$('#subscription_stripe_card_token').val(response.id)
$('#new_subscription')[0].submit()
else
$('#stripe_error').text(response.error.message)
$('input[type=submit]').attr('disabled', false)```
Any help would be greatly appreciated
I have this empty card error as well
Stripe error while creating customer: You passed an empty string for 'card'. We
assume empty values are an attempt to unset a parameter; however 'card' cannot b
e unset. You should remove 'card' from your request or supply a non-empty value
It's fixed just update the stripe version to v2 from v1 in the layout files or wherever you have included it.
entering dummy card number 4111111111111111 and cvv 123 solved the following issue for me:
Stripe error while creating customer: You passed an empty string for 'card'. We
assume empty values are an attempt to unset a parameter; however 'card' cannot b
e unset. You should remove 'card' from your request or supply a non-empty value
if you are getting empty values:
Stripe error while creating customer: You passed an empty string for 'card'. We
assume empty values are an attempt to unset a parameter; however 'card' cannot b
e unset. You should remove 'card' from your request or supply a non-empty value
Drove me crazy!
Mark. I literally almost went nuts. 2+ hours just trying to get the card to push up to stripe. Turbolinks was my issue. Thank you so much.
Stripe now available in the UK. And yes, this is by far the simplest payment system out there. Excellent cast as ever
For anyone who's finding that the coffeescript for this app isn't working, it's probably worth looking at this http://stackoverflow.com/questions/20081255/cannot-get-stripe-js-working-with-my-rails-4-app
Hi,
Just curious to know the difference between:
Stripe.createToken method used in this tutorial and Stripe.card.createToken as presented in the Stripe document.
Thanks
Hi ,
i need to know that can we implement below payment process using strip payment gateway
User1's Credit card--->fund transfer--to admin strip account-->20% commission deducted for admin--->80% of fund transfer to user2's paypal or bank account.
We are trying to implement this in out site what having the issues in implementation.
Can anybody help in this..
Thanks
Hi there,
Trying to get a spinner on the submit button rather than just disabling it. Usually I just do it with:
<%= button_tag "Submit", class: 'mb-xs mt-xs mr-xs btn btn-primary btn-block', data: {disable_with: " Wait..."} %>
But this doesnt seem to work. Any ideas?
Thanks
You can process Stripe payments in the background, checkout Processing Stripe Payments with a Background Worker in Rails 5