20

I know there is another question that exists similar to this one but I don't think it was asked/answered very well.

Basically I have a working rails app where users can sign up for my subscription, enter credit card information, etc. That's all working. But I need to handle the situation where a user's card is declined at some point during this recurring subscription.

The types of events they send are here: https://stripe.com/docs/api?lang=ruby#event_types.

I'm having trouble accessing the charge.failed object in my app.

The docs on webhooks are also here: https://stripe.com/docs/webhooks, and any help would be much appreciated.

Zach
  • 1,233
  • 13
  • 19
  • possible duplicate of [Using Stripe webhooks with Rails](http://stackoverflow.com/questions/9371566/using-stripe-webhooks-with-rails) – Hauleth Oct 29 '12 at 06:50

3 Answers3

40

You need to create a controller to basically accept and handle the requests. It's pretty straight forward, although not as straight forward to wrap your mind around initially. Here is an example of my hooks_controller.rb:

class HooksController < ApplicationController
  require 'json'

  Stripe.api_key = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

  def receiver

    data_json = JSON.parse request.body.read

    p data_json['data']['object']['customer']

    if data_json[:type] == "invoice.payment_succeeded"
      make_active(data_event)
    end

    if data_json[:type] == "invoice.payment_failed"
      make_inactive(data_event)
    end
  end

  def make_active(data_event)
    @profile = Profile.find(User.find_by_stripe_customer_token(data['data']['object']['customer']).profile)
    if @profile.payment_received == false
      @profile.payment_received = true
      @profile.save!
    end
  end

  def make_inactive(data_event)
    @profile = Profile.find(User.find_by_stripe_customer_token(data['data']['object']['customer']).profile)
    if @profile.payment_received == true
      @profile.payment_received = false
      @profile.save!
    end
  end
end

The def receiver is the view that you have to point the webhooks to on the stripe interface. The view receives the json, and I'm using it to update the user's profile in the event that a payment fails or succeeds.

rubyplusplus
  • 516
  • 5
  • 2
  • Nice!! I think you're right, it was a bit intimidating initially but once I figured it out it was no big deal. My controller actually looked somewhat like this(although not as pretty). Thanks! – Zach Mar 11 '12 at 04:51
  • 2
    Very helpful! FYI the result of the JSON parse isn't an indifferent hash, so you may want to do event_json = JSON::parse(request.body.read).with_indifferent_access instead. – runako May 10 '12 at 21:12
  • 12
    For best [security] practice, use the event id (`data_json['id']`) to retrieve the Stripe::Event object, and then grab the data from that since it's for sure legit. - As stated on [Stripe's webhooks reference page](https://stripe.com/docs/webhooks). – anxiety Jul 13 '12 at 20:28
  • 1
    You can avoid json parsing using Stripe::Event.construct_from(params) from the stripe gem. – Jo P Nov 11 '15 at 19:36
  • it scares me to see methods on a controller that are not :actions and are not made private – pixelearth Nov 14 '16 at 17:55
10

It is much easier now using the stripe_event gem:

https://github.com/integrallis/stripe_event

Richard Ortega
  • 495
  • 1
  • 6
  • 11
0

This is a less than ideal testing situation…

Stripe needs a way to "force" webhooks for testing purposes. Currently, the shortest subscription you can make is for 1 week (in test mode); it would be much more helpful if you could set it for 1 minute, 1 hour, or even simply cause the callback to occur in realtime, so you can test your API response system.

Local tests are great, but nothing replaces the real-world, live, over the internet, webhooks/callbacks. Having to wait a week (!) seriously slows down projects.

rcd
  • 1,348
  • 1
  • 14
  • 27