Ask A Question

Notifications

You’re not receiving notifications from this thread.

Using Webhooks with Stripe Discussion

Fritz Rodriguez Jr. Fritz Rodriguez Jr.

Hey Chris,

Another great episode!

How would you handle a monthly recurring cost without having the user create an account? For example, a donation site?

Reply

If you're doing recurring donations, you'll need to store a User model of some sort so they can come back in and cancel it. You may not need passwords in that case, a secret token that you log them in with via email would work. You could make it work similar to a password reset token basically so they could manage their monthly donation via email.

Reply

Great episode! Thanks!

Could you recommend some article or gem that could handle some kind of "leaky bucket" algorithm?

I wanna build an app where many different Users will be able to send API Calls to a third party website which would return a link to generated by this site document in pdf.

I am wondering if I should construct an algorithm which would queue up all these requests so they do not fire off at exact same time, or is it something that Rails4 handles? Or maybe its not a problem at all?

Another thing I would like to set up is a queue for request so they would fire for example not more often than f.e. 2 calls per second. And on the other hand, if one call returns f.e. 429 error, an app would try again in f.e. 30 seconds and the other ones which would be in a queue (triggered by another Users) would be put on hold until the errored one would be successful after another or several attempts...

Is there any gem that handles that kind of requests scheduling for API calls?

Would you build a seperate model for API calls and queue them up in the database with timestamps?.. If so, then what is the best way to schedule a controller to reach to this database of queued requests and make an api call in the background?

f.e. Shopify handles that kind of errors on their server site,.. but I'd like to be able to control it from my app as not all of the services handle it... https://docs.shopify.com/ap...

Thanks for great tutorials!!! :)

Reply

You would probably want to do this on the Rack level. What you're basically attempting is a rate limiting / throttling algorithm for the API. This is best done in Rack because it will be faster than going all the way through the Rails stack. Something like this is a good starting point. https://github.com/bendiken...

I'm not sure that it supports the leaky bucket algorithm that shopify does, but it's a good starting point.

Reply

Hi Chris!

Another great episode about something i was just gonna start working with! I have a question about webhooks from Stripe. Is it possible to use webhooks to get update of the status of the subscription? If it's active or not, so that if someone isn't paying our a charge fails that it will update the user's account.

Again thx for a great episode!

Reply

Yep! That's exactly right. You can get notifications of all the subscription events and keep that in sync and send out any emails you need to when they get canceled.

Reply

Awesome episode, Chris. I just loved it. I'm trying to build a simple website for user to listen musics online with subscription model. The idea is simple, users pay monthly subscription and every month, they can listen any new 20 songs. Everything works well as Stripe webhooks fired every month.

The problem is when someone pay YEARLY subscription (20% discounted). How can I deal the same logic as now, Stripe webhook only fire in yearly basis. Would love to hear your thoughts, Chris!

Reply

Yeah, that gets pretty tricky. We had to do that at One Month and it became kind of a complicated piece of work. It was a nightly cron job that calculated the end of the month on the same schedule that Stripe would do monthly. We would look up users expiring and give them access to their next course. You also have to take into account things like extra days in months, leap years, etc. It's really not fun so if you can get by without offering the yearly plan for something like this, I'd probably recommend it.

You could also have a simple 30 day schedule for every yearly user that's a little different than the monthly users. People generally won't notice the difference and it's a lot simpler than making a perfect copy of what Stripe does yourself.

Reply

Hey Chris, quick question...I followed your tutorial for setting up webhooks with stripe using the stripe.rb config file. How would you retrieve the line items for an invoice using the latest stipe API? I have the parent elements retrieval working fine. But the line items are an issue. Here is the code i am using, it's returning nil. CODE: invoice = event.data.object, invoice.lines.data[0].plan AND invoice.lines.data[0].plan.id returns => id method error. Any ideas?

Reply

Have you printed out the result from this line? invoice.lines.data[0].plan

I'm wondering if it isn't returning a hash, but some other type (like maybe the plan is just the string ID instead and it doesn't auto-include the nested plan attributes). I don't know off the top of my head, but I'd recommend checking that out and seeing what you get.

Reply

Chris, since I'm running Koudoku the setup is a bit different and I'm running into an issue. I posted a question on SO: http://stackoverflow.com/qu... Any suggestion you could provide would be hugely helpful!

Thanks!

Reply

I think Matt's answer is correct. Basically just need to reverse your way back to the code that creates the Subscription object (in your database) and attaches that to the user. Maybe it's not saving?

Reply

Hey @excid3:disqus when I'm at minute 21 ish where you call event.data.object I don't see "source". I believe I have a bug but am not getting any errors. Aditionally, the object is saving all of the data as nill (EX: card_brand: nil, card_exp_month: nil).

Here's a screen shot of what I get when I call event.data.object and another screen shot of what happens when I call RecordCharges.new.call(event).

Any suggestions?

Reply

Hi @Alex Kehaya. I don't know if you've already solved your problem, but it appears you are grabbing the wrong event. You are grabbing a subscribe event not a charge event, indicated by the fact that the event.data.object response says "Stripe::Subscription" and the id starts with "sub_". A charge event id starts with "ch_". Go back to "Events & webhooks" in your Stripe dashboard and find an event that has the phrase "was charged". That should be a charge event. When you click on it, in the "Event Data" section the id should start with "ch_". Use the event id that appears under "Event Details" at the top.
And don't worry, I did the same thing. :-)
Cheers,
Tim

Reply

I'm using `customer.subscription.updated` event to get notification when a recurring charge occurs, so that I can update user table for relevant attribute.

But this event will also trigger when customer manually changes/renew their plan from admin-panel before billing period ends; in this case too I'll update user table fields (similarly as above) but here, to update these attributes I've defined the code separately in my subscriptions controller.

There will be duplication of code and app will try to update user table fields, first based on controller code and then based on webhook event code. How to avoid this?

Reply

One issue with the current set up that I noticed was that if you delete the subscription on the Stripe dashboard -- it is still displayed on the site end. I believe this is caused from not having a webhook? Any advice on how I could build this feature in so they stay in sync.

Reply

Yep. You can implement the subscription webhook that gets triggered on delete (or cancel, don't remember the exact name) and just have it remove it from the user on your end. You'd build it same as the other one, but just remove the subscription ID from the user.

Reply

Hey Chris, awesome episode. Quick question though, how would you handle error handling for webhooks? You kinda glossed over it for a minute but am wondering on making an app production ready, would you suggest just swallowing the error or it doesn't matter because of precautions like database constraints?

Reply

It typically depends on what you're doing with the webhooks and what the potential errors are. Maybe a user deleted their account and so you no longer have a matching record for the stripe customer ID, in that case you might let the webhook silently fail. In other cases, you probably want to send yourself an alert email if something goes wrong that's out of the ordinary so you can fix it. I usually leave that for exception_notification or a tool like Airbrake/AppSignal/Sentry to log and handle sending the notification for me.

In general you never want webhooks to fail, so either send yourself an alert so you can fix that case when something went wrong or build in the logic to allow certain cases to skip gracefully and you should be fine.

Reply

A great (free) service to tunnel your localhost is ngrok https://ngrok.com/download :
- download
- unzip
- put in `/usr/local/bin`
- start via `ngrok http 3000` (after you started `rails s`)
- Check output to see where website is available

Reply

Hi Chris,

Awesome episode, helped me add Stripe subscription with minimal work. Thanks!

Do you think we will see a follow up for webhooks, I am trying to catch failed charges and send the users to update the card. Similar to what you have in gorails.com. Any help would be appreciated.

Reply

You can just listen to the charge.failed event and send an email accordingly if you want. Even better, you can use a service like Profitwell to handle the failed payments (and soon to expire cards) for you and let them optimize the email text and things. That's what I use and it's generally better at recovering failed payments than your own stuff because they can learn and improve over a vast set of businesses rather than just your single one.

Reply

Awesome! But how do you implement the extra "account" attribute for managed accounts as seen here: https://stripe.com/docs/con...

Reply

Thanks so much for putting this series together. Very helpful. I wanted to ask about the stripe_id and being unique in the chargers model. As I follow, from the previous episodes, the stripe_id is the 'Stripe Customer Id' Every user(subscribed) has only 1 stripe_id. In this episode you set users has_many charges and charge belongs_to a user. By making strip_id unique and not allowing duplicates, won't it fail to store all the charges for the user? So if its a monthly subscription, after 6 months, a user should have 6 charge records on the table with each record having the same user_id and stripe_id? Sorry I am missing something. Still new to me. ;)

Reply

Actually, stripe_id on the User model references a Stripe::Customer's ID and stripe_id on a Charge references a Stripe::Charge's ID. That's a great point to clarify!

We're just storing references to the different objects in Stripe so we can load up those objects whether they're a Customer, a Charge, or a Subscription, etc.

Reply
I am trying to implement this episode (exactly as shown) in my test app using Rails 5.2 on Cloud 9 with no luck at all.  The Stripe parameters (charge details) arrive safely into our app but then rejected because of "Can't verify CSRF token authenticity" error as shown in the console.

I have tried all the solutions found on the web including adding:

- skip_before_action :verify_authenticity_token
- protect_from_forgery with: :null_session
- skip_before_action :verify_authenticity_token, only: [:webhook]

to stripe_controller 

and I also tried adding 
- protect_from_forgery unless: -> { request.format.json? } 

to application controller.

Please can someone help as the episode seemed to be great but is not working for me.
Reply

Hi,

Are you sure that the endpoint in stripe contains /webhooks/stripe at the end of the URL?

This fixed it for me.

Reply
Hey Chris. I'm running into some interesting issues with having the StripeEvent.configure code in config/initializers/stripe.rb - mainly, I have to restart my server if I change any service object code - And I'm getting a  
ArgumentError (A copy of StripeEvents::InvoicePaymentFailed has been removed from the module tree but is still active!) if I hit the webhook a 2nd time. This may be due to the fact that I'm calling a service object in a separate file.

I'm documenting my findings here: https://github.com/integrallis/stripe_event/issues/108 - I'll report back once I sort it out.

If you have any ideas or comments in the mean time, I'm all ears!
Reply
What is required to add a signature header and endpoint secret? Stripe now requires them as per their v2.0.0 to verify requests with a StripeEvent.signing_secret
Reply
All you do is grab the signing secret from your Stripe account and set that variable like you would do with the other keys. Link to how to use it is here: https://github.com/integrallis/stripe_event#authenticating-webhooks-with-signatures

I'll do a quick episode on this shortly!
Reply

Sorry Chris I forgot to write back to you! I got it working at like 3:30AM lol Stripe docs made it seem that you needed the event payload, the Stripe-Signature header, and the endpoint’s secret to perform the verification, which was what I was writing here about.

I was able to simply add the signing_secret to config/secrets.yml, and in config/initializers/stripe.rb. My first deploy to heroku only added the signing secret to stripe.rb and was throwing an issue.

config/initializers/stripe.rb
 Rails.configuration.stripe = {
:stripe_publishable_key => ENV['STRIPE_PUBLISHABLE_KEY'],
:stripe_secret_key => ENV['STRIPE_SECRET_KEY']
:stripe_secret_key => ENV['STRIPE_SECRET_KEY'],
:stripe_signing_secret => ENV['STRIPE_SIGNING_SECRET']
}

class RecordCharges
def call(event)
....

Reply

Hey Chris,

I'm trying to figure out how to fix up my code and move the RecordCharges class out of my initializer and into a PlainOldRubyObject™ located in app/services/webhook_handlers/stripe_webhook_handler.rb but am running into an issue that because the StripeEvent is called in an initializer (config/initializers/stripe.rb), the rest of the app isn't loaded so it is not able to call the class. Any chance you could help me out?

Thanks,

Rich

Reply

It should automatically load that file as long as you named it correctly. class WebhookHandlers::StripeWebhookHandler; end

Reply

Thanks for getting back to me! I am still struggling with this :/

Edit: Got it working but it feels dirty...

Here's what I have:

/config/intializers/stripe.rb

...
StripeEvent.configure do |events|
  events.subscribe 'charge.succeeded', WebhookHandlers::StripeWebhookHandler.new
end

/app/services/webhook_handlers/stripe_webhook_handler.rb

module WebhookHandlers
  module StripeWebhookHandler
    class RecordCharges
      def call(event)
        ...
      end
    end
  end
end

Any chance you could point me in the right direction on wheather or not this is clean?

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 82,329+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    Screencast tutorials to help you learn Ruby on Rails, Javascript, Hotwire, Turbo, Stimulus.js, PostgreSQL, MySQL, Ubuntu, and more.

    © 2024 GoRails, LLC. All rights reserved.