GithubHelp home page GithubHelp logo

integrallis / stripe_event Goto Github PK

View Code? Open in Web Editor NEW
843.0 21.0 104.0 470 KB

Stripe webhook integration for Rails applications.

Home Page: https://rubygems.org/gems/stripe_event

License: MIT License

Ruby 92.61% HTML 7.39%

stripe_event's Introduction

StripeEvent

Build Status Gem Version Code Climate Gem Downloads

StripeEvent is built on the ActiveSupport::Notifications API. Incoming webhook requests are authenticated with the webhook signature. Define subscribers to handle specific event types. Subscribers can be a block or an object that responds to #call.

Install

# Gemfile
gem 'stripe_event'
# config/routes.rb
mount StripeEvent::Engine, at: '/my-chosen-path' # provide a custom path

Usage

# config/initializers/stripe.rb
Stripe.api_key             = ENV['STRIPE_SECRET_KEY']     # e.g. sk_live_...
StripeEvent.signing_secret = ENV['STRIPE_SIGNING_SECRET'] # e.g. whsec_...

StripeEvent.configure do |events|
  events.subscribe 'charge.failed' do |event|
    # Define subscriber behavior based on the event object
    event.class       #=> Stripe::Event
    event.type        #=> "charge.failed"
    event.data.object #=> #<Stripe::Charge:0x3fcb34c115f8>
  end

  events.all do |event|
    # Handle all event types - logging, etc.
  end
end

Subscriber objects that respond to #call

class CustomerCreated
  def call(event)
    # Event handling
  end
end

class BillingEventLogger
  def initialize(logger)
    @logger = logger
  end

  def call(event)
    @logger.info "BILLING:#{event.type}:#{event.id}"
  end
end
StripeEvent.configure do |events|
  events.all BillingEventLogger.new(Rails.logger)
  events.subscribe 'customer.created', CustomerCreated.new
end

Subscribing to a namespace of event types

StripeEvent.subscribe 'customer.card.' do |event|
  # Will be triggered for any customer.card.* events
end

Securing your webhook endpoint

Authenticating webhooks with signatures

Stripe will cryptographically sign webhook payloads with a signature that is included in a special header sent with the request. Verifying this signature lets your application properly authenticate the request originated from Stripe. As of v2.0.0, StripeEvent now mandates that this feature be used. Please set the signing_secret configuration value:

StripeEvent.signing_secret = Rails.application.secrets.stripe_signing_secret

Please refer to Stripe's documentation for more details: https://stripe.com/docs/webhooks#signatures

Support for multiple signing secrets

Sometimes, you'll have multiple Stripe webhook subscriptions pointing at your application each with a different signing secret. For example, you might have both a main Account webhook and a webhook for a Connect application point at the same endpoint. It's possible to configure an array of signing secrets using the signing_secrets configuration option. The first one that successfully matches for each incoming webhook will be used to verify your incoming events.

StripeEvent.signing_secrets = [
  Rails.application.secrets.stripe_account_signing_secret,
  Rails.application.secrets.stripe_connect_signing_secret,
]

(NOTE: signing_secret= and signing_secrets= are just aliases for one another)

Configuration

If you have built an application that has multiple Stripe accounts--say, each of your customers has their own--you may want to define your own way of retrieving events from Stripe (e.g. perhaps you want to use the account parameter from the top level to detect the customer for the event, then grab their specific API key). You can do this:

class EventFilter
  def call(event)
    event.api_key = lookup_api_key(event.account)
    event
  end

  def lookup_api_key(account_id)
    Account.find_by!(stripe_account_id: account_id).api_key
  rescue ActiveRecord::RecordNotFound
    # whoops something went wrong - error handling
  end
end

StripeEvent.event_filter = EventFilter.new

If you'd like to ignore particular webhook events (perhaps to ignore test webhooks in production, or to ignore webhooks for a non-paying customer), you can do so by returning nil in your custom event_filter. For example:

StripeEvent.event_filter = lambda do |event|
  return nil if Rails.env.production? && !event.livemode
  event
end
StripeEvent.event_filter = lambda do |event|
  account = Account.find_by!(stripe_account_id: event.account)
  return nil if account.delinquent?
  event
end

Note: Older versions of Stripe used event.user_id to reference the Connect Account ID.

Without Rails

StripeEvent can be used outside of Rails applications as well. Here is a basic Sinatra implementation:

require 'json'
require 'sinatra'
require 'stripe_event'

Stripe.api_key = ENV['STRIPE_SECRET_KEY']

StripeEvent.subscribe 'charge.failed' do |event|
  # Look ma, no Rails!
end

post '/_billing_events' do
  data = JSON.parse(request.body.read, symbolize_names: true)
  StripeEvent.instrument(data)
  200
end

Testing

Handling webhooks is a critical piece of modern billing systems. Verifying the behavior of StripeEvent subscribers can be done fairly easily by stubbing out the HTTP signature header used to authenticate the webhook request. Tools like Webmock and VCR work well. RequestBin is great for collecting the payloads. For exploratory phases of development, UltraHook and other tools can forward webhook requests directly to localhost. You can check out test-hooks, an example Rails application to see how to test StripeEvent subscribers with RSpec request specs and Webmock. A quick look:

# spec/requests/billing_events_spec.rb
require 'rails_helper'

describe "Billing Events" do
  def bypass_event_signature(payload)
    event = Stripe::Event.construct_from(JSON.parse(payload, symbolize_names: true))
    expect(Stripe::Webhook).to receive(:construct_event).and_return(event)
  end

  describe "customer.created" do
    let(:payload) { file_fixture('evt_customer_created.json').read }
    before(:each) { bypass_event_signature(payload) }

    it "is successful" do
      post '/_billing_events', params: payload
      expect(response).to have_http_status(:success)
      # Additional expectations...
    end
  end
end

Maintainers

Special thanks to all the contributors.

Versioning

Semantic Versioning 2.0 as defined at http://semver.org.

License

MIT License. Copyright 2012-2015 Integrallis Software.

stripe_event's People

Contributors

abepark01 avatar adamlamar avatar beccadax avatar beetlehope avatar bousquet avatar danhodos avatar danielricecodes avatar dclausen avatar excid3 avatar gaffneyc avatar gingray avatar invisiblefunnel avatar iseth avatar jakegavin avatar joshk avatar krasnoukhov avatar ktheory avatar markmurphy avatar mattgoldman avatar mcolyer avatar mjc-gh avatar oboxodo avatar qrush avatar rmm5t avatar roberteles avatar schinery avatar taise avatar thinkclay avatar tylerpearson avatar zorbash avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stripe_event's Issues

Completed 401 Unauthorized

I created a basic Rails app with stripe_event to test out the integration. I followed the steps in the instructions and deployed my app to Heroku, but I get a 401 error when I trigger a Webhooks test from Stripe. Here is what is in the logs

2012-09-29T01:42:58+00:00 app[web.1]:   Parameters: {"type"=>"plan.created", "livemode"=>false, "object"=>"event", "created"=>1326853478, "id"=>"evt_00000000000000", "data"=>{"object"=>{"interval"=>"month", "amount"=>2999, "livemode"=>false, "currency"=>"usd", "object"=>"plan", "name"=>"Month to Month", "interval_count"=>1, "trial_period_days"=>3, "id"=>"monthtomonth_00000000000000"}}, "webhook"=>{"type"=>"plan.created", "livemode"=>false, "object"=>"event", "created"=>1326853478, "id"=>"evt_00000000000000", "data"=>{"object"=>{"interval"=>"month", "amount"=>2999, "livemode"=>false, "currency"=>"usd", "object"=>"plan", "name"=>"Month to Month", "interval_count"=>1, "trial_period_days"=>3, "id"=>"monthtomonth_00000000000000"}}}}
2012-09-29T01:42:58+00:00 app[web.1]: 
2012-09-29T01:42:58+00:00 app[web.1]: 
2012-09-29T01:42:58+00:00 app[web.1]: Started POST "/event-from-stripe" for 50.18.4.244 at 2012-09-29 01:42:58 +0000
2012-09-29T01:42:58+00:00 app[web.1]: Processing by StripeEvent::WebhookController#event as XML
2012-09-29T01:42:59+00:00 app[web.1]: Completed 401 Unauthorized in 545ms (ActiveRecord: 0.0ms)
2012-09-29T01:42:59+00:00 heroku[router]: POST myapp.herokuapp.com/event-from-stripe dyno=web.1 queue=0 wait=0ms service=638ms status=401 bytes=1

I set the STRIPE_API_KEY config in Heroku using:

heroku config:add STRIPE_API_KEY=[my secret API key for test mode]

I double checked the end point, and I printed the Stripe.api_key when I start to application to make sure it is set properly.

It seems strange that the log says it is processing the event as XML, even though it is receiving JSON.

Any ideas on what is going on here?

Thanks in advance!

-javid

Concurrency issues

How about concurrency? Your readme suggest something like

StripeEvent.configure do |events|
  events.subscribe 'customer.created', CustomerCreated.new
end

How will that work under high load? Is it guaranteed to be thread safe? If not I suggest you update the readme with this information. If that is supposed to be thread safe then lets talk.

NoMethodError: private method `singleton_class' called for StripeEvent:Module

I just cannot get this module to work. I get the following error in 1.9.2 and 1.9.3. I know this actually occurs in Rails::Engine, but I can't find anything whatsoever on Google, so I'm trying to figure out if it's just completely broken and no one's talking about it, or if I'm doing something wrong.

We've seen similar errors in RSpec, which we had to monkey patch to get working. But it's harder to monkey patch stuff like this as order of routes, initializers, etc. is important.

NoMethodError: private method `singleton_class' called for StripeEvent:Module
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/railties-3.2.9/lib/rails/engine.rb:388:in `isolate_namespace'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/stripe_event-0.4.0/lib/stripe_event/engine.rb:3:in `<class:Engine>'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/stripe_event-0.4.0/lib/stripe_event/engine.rb:2:in `<module:StripeEvent>'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/stripe_event-0.4.0/lib/stripe_event/engine.rb:1:in `<top (required)>'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/activesupport-3.2.9/lib/active_support/dependencies.rb:251:in `require'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/activesupport-3.2.9/lib/active_support/dependencies.rb:251:in `block in require'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/activesupport-3.2.9/lib/active_support/dependencies.rb:236:in `load_dependency'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/activesupport-3.2.9/lib/active_support/dependencies.rb:251:in `require'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/stripe_event-0.4.0/lib/stripe_event.rb:2:in `<top (required)>'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/bundler-1.0.18/lib/bundler/runtime.rb:68:in `require'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/bundler-1.0.18/lib/bundler/runtime.rb:68:in `block (2 levels) in require'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/bundler-1.0.18/lib/bundler/runtime.rb:66:in `each'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/bundler-1.0.18/lib/bundler/runtime.rb:66:in `block in require'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/bundler-1.0.18/lib/bundler/runtime.rb:55:in `each'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/bundler-1.0.18/lib/bundler/runtime.rb:55:in `require'
~/.rvm/gems/ruby-1.9.2-p290@coalmine/gems/bundler-1.0.18/lib/bundler.rb:120:in `require'
~/Desktop/Coalmine/coalmine/config/application.rb:8:in `<top (required)>'
<internal:lib/rubygems/custom_require>:29:in `require'
<internal:lib/rubygems/custom_require>:29:in `require'
~/Desktop/Coalmine/coalmine/config/environment.rb:2:in `<top (required)>'
<internal:lib/rubygems/custom_require>:29:in `require'
<internal:lib/rubygems/custom_require>:29:in `require'
~/Desktop/Coalmine/coalmine/config.ru:3:in `block in <main>'
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/builder.rb:4:in `instance_eval'
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/builder.rb:4:in `initialize'
~/Desktop/Coalmine/coalmine/config.ru:1:in `new'
~/Desktop/Coalmine/coalmine/config.ru:1:in `<main>'
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:50:in `eval'
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:50:in `load_config'
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:43:in `initialize'
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:13:in `new'
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:13:in `run'
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/bin/nack_worker:4:in `<main>'

StripeEvent.registration is now StripeEvent.setup

Not sure where to post this, but was StripeEvent.registration changed to StripEvent.setup in the docs (I just realized I had to update my code because of this).

Is the registration method deprecated and just use setup now?

Richard

account.application.deauthorized 401 error with 1.2.0

I'm trying to test out the account.application.deauthorized event. This has been recently fixed up for the 1.2.0 release so I have made sure I am on that.

I get an error :
Completed 401 Unauthorized in 2212ms (ActiveRecord: 0.0ms)

when i unauthorize my application from the stripe control panel

I am registering my events like this:

StripeEvent.configure do |events|

events.subscribe 'account.application.deauthorized', AccountApplicationDeauthorized.new(Rails.logger)

  events.all do |event|
    logger.info "Stripe Event please: #{event}"
  end
end

and my handler looks like this:

class AccountApplicationDeauthorized

  def initialize(logger)
    @logger = logger
  end

  def call(event)
    @logger.info "deauth:#{event.type}:#{event.id}"

    p = Provider.where(:stripe_user_id => event.user_id).first

    if p
      p.access_token = ""
      p.save
    end
  end

end

What am I doing wrong?

Thanks for any guidance,

Will

ActionController::RoutingError (No route matches [GET] ""):

In a rails 4.1.6 (ruby 2.1.2) app using stripe_event 1.3.0, I'm getting

Started GET "/stripe" for 127.0.0.1 at 2014-10-13 11:30:55 -0700
ActiveRecord::SchemaMigration Load (0.3ms) SELECT "schema_migrations".* FROM "schema_migrations"

ActionController::RoutingError (No route matches [GET] ""):
actionpack (4.1.6) lib/action_dispatch/middleware/debug_exceptions.rb:21:in call' actionpack (4.1.6) lib/action_dispatch/middleware/show_exceptions.rb:30:incall'
. . .
whenever I load http://localhost:3000/stripe

In rake routes, I see stripe_event_path has blank for HTTP verb.

This seems similar to #13. Any idea how to fix?

Suppressing error in webhooks controller makes testing more difficult

In my case, I couldn't figure out why the spec was failing (I hadn't stubbed auth correctly) and had to manually call StripeEvent.instrument in my test.

I'm not super familiar with Engines--is there way to propagate the error and let the client decide how to handle it? And/or add a logging statement?

Thanks for the great work!

Edit: referring to https://github.com/integrallis/stripe_event/blob/master/app/controllers/stripe_event/webhook_controller.rb#L14

401 Unauthorized

I'm getting 401 Unauthorized codes even when I create test payments myself (as opposed to using the test webhook in Account Settings, which I know causes the 401 issue).

I've doublechecked to make sure my API key is correct and that I'm using the Stripe dash in the correct mode for the provided API key. I can run Stripe::Event.all in console and see the events.

I've copied the body of a charge.succeeded event from Stripe and sent it to my listening URL as a POST request myself as well, just to see if I could get a different result, but it still returned 401.

Code is below. Maybe I'm just missing something obvious here? Thanks for your help!

In routes:

mount StripeEvent::Engine => "/receipt_shipper"

In the Stripe config file:

Stripe.api_key = <my secret key>

StripeEvent.setup do
    subscribe 'charge.succeeded' do |event|
        charge_id = event.data.object.id
        # make sure a receipt does not already exist with that charge
        unless Receipt.where(:charge_id => charge_id).count > 0
            stripe_cust_id = event.data.customer
            user = User.where(:stripe_id => stripe_cust_id).first
            # make a receipt in the database
            r = Receipt.new
            r.user_id = user.id
            r.charge_id = charge_id
            r.last_four = event.data.card.last4
            r.amount = event.data.amount
            r.save!
        end
    end
end

Disable retrieval?

Is there any way to disable the retrieval of events when in tests/development?
Some events are not easy to trigger from the Stripe interface like "customer.subscription.trial_will_end" using actual test data...

Feature Request: Disable verifying event with stripe

I'm currently integrating stripe_event, and want to test my code in the subscribe blocks. I'm using localhost for development and therefore can't set a webhook to my URL.

Thoughts on adding a testing option to disable the checking with stripe? Then I could just look at the event in the stripe interface and post it to localhost myself.

Thanks,
Jake

protect from forgery

This is not an issue or a problem, however I want to get some advice.
With stripe-event, Should I apply null-session for protect_from_forgery.

I've just heard as a default, stripe-webhook events can't be heard without turning off the CSRF protection.
Should I do something like below, or protect_from_forgery with: :exception following the default setting?

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: :null_session
end

I just only listen to Stripe Webhook.

Question about config/initializers/stripe.rb

I'm a fairly new rails dev so maybe the answer to question is obvious to some so please bear with me.

I love the idea of how stripe_event works. Having a lot (practically all) of the legwork done for stripe events will sure save me a lot of time. Can someone help me understand though the decision to put the subscribe calls inside of an initializer?

I personally intend to send e-mails and update records based on stripe callbacks. Creating, updating, and destroying models seems like something better suited for a controller.

It works don't get me wrong. I guess I'm just wondering if performing these kind of actions in a initializer would fall into rails best practices?

does not support connected account webhooks

Stripe has recently introduced the functionality that Platform accounts ("master" accounts) can retrieve events/receive webhooks pertaining to events taking place on it's connected accounts (managed or traditional connected accounts).

The primary difference in these events, is that they contain a user_id property, which needs to be included in the Stripe::Event.retrieve call [1]. If the receiver doesn't include this user_id in the event retrieval, it will result in a not-found, and thus a 401 back to stripe.

The proper way to retrieve an event for a connected account would look like this:

Stripe::Event.retrieve(params[:id], stripe_account: params[:user_id])

So, the retrieve code in this library [2] can probably be updated to some variation of this:

self.event_retriever = lambda { |params| params[:user_id].nil? ? Stripe::Event.retrieve(params[:id]) : Stripe::Event.retrieve(params[:id], stripe_account: params[:user_id]) }

I am happy to submit/start a PR but I'll likely need help with the impact on your test suite.

[1] https://stripe.com/docs/connect/authentication#webhooks
[2] https://github.com/dockwa/stripe_event/blob/master/lib/stripe_event.rb#L71

return 500 explicitly

Within a webhook, I sometimes need to expand the object for stripe (Request to Stripe for some values based on the request.)

At this point, if request fails for time out error, I'd love to return 500 explicitly.

  def retrieve_charge_metadata_of(invoice_metadata)
    return Stripe::Charge.retrieve(id: invoice_metadata.charge)
  rescue *EXTERNAL_CARD_ERRORS => e
    ExternalCardErrorHandler.new(e).handle
    head 500
  end

How can I achieve this with stripe-event?
Or doe this automatically return 500 if something goes wrong? (Stripe::StripeError?)

Rspec undefined method 'post'

I am having a problem running rspec with this gem. My route is:
Cap::Application.routes.draw do

mount StripeEvent::Engine => '/stripe_events'

I am attempting to run this test:

describe 'customer.created' do
before do
stub_event 'evt_customer_created'
end

it 'is successful' do
  post '/stripe_events', id: 'evt_customer_created'
  expect(response.code).to eq '200'
  # Additional expectations...
end

end

And I have this in my initializer:
StripeEvent.configure do |events|

Clearly you'll be doing something meaningful here...

but we're logging for explanatory purposes.

Watch the logs while your tests run:

$ tail -f log/test.log

events.subscribe 'customer.created' do |event|
Rails.logger.info '*************************************************'
Rails.logger.info event
Rails.logger.info '**
***********************************************'
end

events.all BillingEventLogger.new(Rails.logger)
end

and I get this result:
Failure/Error: post '/stripe_events', id: 'evt_customer_created'
NoMethodError:
undefined method `post' for #RSpec::ExampleGroups::BillingEvents::CustomerCreated:0x0000010cb4f868

I can put anything after the post and get the same result. What am I missing.

Don French

New gem version?

It would be ๐Ÿ’ฃ to have f6e97b6 released as 1.5.1 for those of us using Rails 5.

Thanks for your consideration!

โค๏ธ

Need to be able to differentiate between Stripe Connect customers and regular customers inside Stripe Events

params[:user_id] can be accessed easily inside an event_retriever, but not so easily inside of the subscribers. I have use cases where I'd like to send dispute emails for my own customers to my admin email, and send disputes for my Stripe Connect users' customers to the Stripe Connect user's email address for them to handle. This can't be differentiated inside of the charge.dispute.created namespace.

Would it be possible to make line 28 in lib/stripe_event.rb configurable (see below), similar to the configurable event_retriever?

https://github.com/integrallis/stripe_event/blob/master/lib/stripe_event.rb#L28

Two possibilities:

  1. Allow extra parameters into the subscribe block, more than just the event?
  2. Maybe just replace event[:type] with something configurable, so that if left default, it would pass event[:type], but could be overridden to send something else (which would allow for further namespacing of subscribers)?

ArgumentError - wrong number of arguments (2 for 5):

I'm not doing anything fancy yet, and in fact I've removed all my event subscriptions to confirm this still happens. I've tried it with the latest version of the gem as well as master, and also tried it turning off the event_retriever.

Any event I receive, for instance the attached log is from creating a plan from the test console, results in the above error. attached is the rails event and corresponding stack trace. I'm using Rails 3.2.13 and a handful of non-controvertial gems.

Any ideas?

Started POST "/api/stripe" for 127.0.0.1 at 2013-06-16 15:10:29 -0400
Processing by StripeEvent::WebhookController#event as XML
Parameters: {"id"=>"evt_21pCFyxFerySHp", "created"=>1371409822, "livemode"=>false, "type"=>"plan.created", "data"=>{"object"=>{"interval"=>"month", "name"=>"1", "amount"=>999, "currency"=>"usd", "id"=>"1", "object"=>"plan", "livemode"=>false, "interval_count"=>1, "trial_period_days"=>30}}, "object"=>"event", "pending_webhooks"=>1, "request"=>"iar_21pChQ3qdCyUBS", "webhook"=>{"id"=>"evt_21pCFyxFerySHp", "created"=>1371409822, "livemode"=>false, "type"=>"plan.created", "data"=>{"object"=>{"interval"=>"month", "name"=>"1", "amount"=>999, "currency"=>"usd", "id"=>"1", "object"=>"plan", "livemode"=>false, "interval_count"=>1, "trial_period_days"=>30}}, "object"=>"event", "pending_webhooks"=>1, "request"=>"iar_21pChQ3qdCyUBS"}}
Completed 500 Internal Server Error in 1ms

ArgumentError - wrong number of arguments (2 for 5):
(gem) meta_request-0.2.5/lib/meta_request/railtie.rb:19:in block (2 levels) in <class:Railtie>' (gem) activesupport-3.2.13/lib/active_support/notifications/fanout.rb:47:inpublish'
(gem) activesupport-3.2.13/lib/active_support/notifications/fanout.rb:25:in block in publish' (gem) activesupport-3.2.13/lib/active_support/notifications/fanout.rb:25:inpublish'
(gem) activesupport-3.2.13/lib/active_support/notifications.rb:118:in publish' /Users/dbock/clients/codesherpas/tracking_papers/vendor/bundle/ruby/1.9.1/bundler/gems/stripe_event-590f4ca55acb/lib/stripe_event.rb:19:inpublish'
/Users/dbock/clients/codesherpas/tracking_papers/vendor/bundle/ruby/1.9.1/bundler/gems/stripe_event-590f4ca55acb/lib/stripe_event.rb:15:in instrument' /Users/dbock/clients/codesherpas/tracking_papers/vendor/bundle/ruby/1.9.1/bundler/gems/stripe_event-590f4ca55acb/app/controllers/stripe_event/webhook_controller.rb:4:inevent'
(gem) actionpack-3.2.13/lib/action_controller/metal/implicit_render.rb:4:in send_action' (gem) actionpack-3.2.13/lib/abstract_controller/base.rb:167:inprocess_action'
(gem) actionpack-3.2.13/lib/action_controller/metal/rendering.rb:10:in process_action' (gem) actionpack-3.2.13/lib/abstract_controller/callbacks.rb:18:inblock in process_action'
(gem) activesupport-3.2.13/lib/active_support/callbacks.rb:403:in _run__3447592120788238416__process_action__138177565787492261__callbacks' (gem) activesupport-3.2.13/lib/active_support/callbacks.rb:405:in__run_callback'
(gem) activesupport-3.2.13/lib/active_support/callbacks.rb:385:in _run_process_action_callbacks' (gem) activesupport-3.2.13/lib/active_support/callbacks.rb:81:inrun_callbacks'
(gem) actionpack-3.2.13/lib/abstract_controller/callbacks.rb:17:in process_action' (gem) actionpack-3.2.13/lib/action_controller/metal/rescue.rb:29:inprocess_action'
(gem) actionpack-3.2.13/lib/action_controller/metal/instrumentation.rb:30:in block in process_action' (gem) activesupport-3.2.13/lib/active_support/notifications.rb:123:inblock in instrument'
(gem) activesupport-3.2.13/lib/active_support/notifications/instrumenter.rb:20:in instrument' (gem) activesupport-3.2.13/lib/active_support/notifications.rb:123:ininstrument'
(gem) actionpack-3.2.13/lib/action_controller/metal/instrumentation.rb:29:in process_action' (gem) actionpack-3.2.13/lib/action_controller/metal/params_wrapper.rb:207:inprocess_action'
(gem) activerecord-3.2.13/lib/active_record/railties/controller_runtime.rb:18:in process_action' (gem) actionpack-3.2.13/lib/abstract_controller/base.rb:121:inprocess'
(gem) actionpack-3.2.13/lib/abstract_controller/rendering.rb:45:in process' (gem) actionpack-3.2.13/lib/action_controller/metal.rb:203:indispatch'
(gem) actionpack-3.2.13/lib/action_controller/metal/rack_delegation.rb:14:in dispatch' (gem) actionpack-3.2.13/lib/action_controller/metal.rb:246:inblock in action'
(gem) actionpack-3.2.13/lib/action_dispatch/routing/route_set.rb:73:in dispatch' (gem) actionpack-3.2.13/lib/action_dispatch/routing/route_set.rb:36:incall'
(gem) journey-1.0.4/lib/journey/router.rb:68:in block in call' (gem) journey-1.0.4/lib/journey/router.rb:56:incall'
(gem) actionpack-3.2.13/lib/action_dispatch/routing/route_set.rb:612:in call' (gem) railties-3.2.13/lib/rails/engine.rb:479:incall'
(gem) railties-3.2.13/lib/rails/railtie/configurable.rb:30:in method_missing' (gem) journey-1.0.4/lib/journey/router.rb:68:inblock in call'
(gem) journey-1.0.4/lib/journey/router.rb:56:in call' (gem) actionpack-3.2.13/lib/action_dispatch/routing/route_set.rb:612:incall'
(gem) meta_request-0.2.5/lib/meta_request/middlewares/app_request_handler.rb:11:in call' (gem) rack-contrib-1.1.0/lib/rack/contrib/response_headers.rb:17:incall'
(gem) meta_request-0.2.5/lib/meta_request/middlewares/headers.rb:16:in call' (gem) meta_request-0.2.5/lib/meta_request/middlewares/meta_request_handler.rb:13:incall'
(gem) warden-1.2.1/lib/warden/manager.rb:35:in block in call' (gem) warden-1.2.1/lib/warden/manager.rb:34:incall'
(gem) actionpack-3.2.13/lib/action_dispatch/middleware/best_standards_support.rb:17:in call' (gem) rack-1.4.5/lib/rack/etag.rb:23:incall'
(gem) rack-1.4.5/lib/rack/conditionalget.rb:35:in call' (gem) actionpack-3.2.13/lib/action_dispatch/middleware/head.rb:14:incall'
(gem) actionpack-3.2.13/lib/action_dispatch/middleware/params_parser.rb:21:in call' (gem) actionpack-3.2.13/lib/action_dispatch/middleware/flash.rb:242:incall'
(gem) rack-1.4.5/lib/rack/session/abstract/id.rb:210:in context' (gem) rack-1.4.5/lib/rack/session/abstract/id.rb:205:incall'
(gem) actionpack-3.2.13/lib/action_dispatch/middleware/cookies.rb:341:in call' (gem) activerecord-3.2.13/lib/active_record/query_cache.rb:64:incall'
(gem) activerecord-3.2.13/lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in call' (gem) actionpack-3.2.13/lib/action_dispatch/middleware/callbacks.rb:28:inblock in call'
(gem) activesupport-3.2.13/lib/active_support/callbacks.rb:405:in _run__1631969465490691919__call__3364289358493390362__callbacks' (gem) activesupport-3.2.13/lib/active_support/callbacks.rb:405:in__run_callback'
(gem) activesupport-3.2.13/lib/active_support/callbacks.rb:385:in _run_call_callbacks' (gem) activesupport-3.2.13/lib/active_support/callbacks.rb:81:inrun_callbacks'
(gem) actionpack-3.2.13/lib/action_dispatch/middleware/callbacks.rb:27:in call' (gem) actionpack-3.2.13/lib/action_dispatch/middleware/reloader.rb:65:incall'
(gem) actionpack-3.2.13/lib/action_dispatch/middleware/remote_ip.rb:31:in call' (gem) better_errors-0.7.2/lib/better_errors/middleware.rb:84:inprotected_app_call'
(gem) better_errors-0.7.2/lib/better_errors/middleware.rb:79:in better_errors_call' (gem) better_errors-0.7.2/lib/better_errors/middleware.rb:56:incall'
(gem) actionpack-3.2.13/lib/action_dispatch/middleware/debug_exceptions.rb:16:in call' (gem) actionpack-3.2.13/lib/action_dispatch/middleware/show_exceptions.rb:56:incall'
(gem) railties-3.2.13/lib/rails/rack/logger.rb:32:in call_app' (gem) railties-3.2.13/lib/rails/rack/logger.rb:16:inblock in call'
(gem) activesupport-3.2.13/lib/active_support/tagged_logging.rb:22:in tagged' (gem) railties-3.2.13/lib/rails/rack/logger.rb:16:incall'
(gem) actionpack-3.2.13/lib/action_dispatch/middleware/request_id.rb:22:in call' (gem) rack-1.4.5/lib/rack/methodoverride.rb:21:incall'
(gem) rack-1.4.5/lib/rack/runtime.rb:17:in call' (gem) activesupport-3.2.13/lib/active_support/cache/strategy/local_cache.rb:72:incall'
(gem) rack-1.4.5/lib/rack/lock.rb:15:in call' (gem) actionpack-3.2.13/lib/action_dispatch/middleware/static.rb:63:incall'
(gem) railties-3.2.13/lib/rails/engine.rb:479:in call' (gem) railties-3.2.13/lib/rails/application.rb:223:incall'
(gem) rack-1.4.5/lib/rack/content_length.rb:14:in call' (gem) railties-3.2.13/lib/rails/rack/log_tailer.rb:17:incall'
(gem) rack-1.4.5/lib/rack/handler/webrick.rb:59:in service' /Users/dbock/.rbenv/versions/1.9.2-p320/lib/ruby/1.9.1/webrick/httpserver.rb:111:inservice'
/Users/dbock/.rbenv/versions/1.9.2-p320/lib/ruby/1.9.1/webrick/httpserver.rb:70:in run' /Users/dbock/.rbenv/versions/1.9.2-p320/lib/ruby/1.9.1/webrick/server.rb:183:inblock in start_thread'

Thanks

Hi,

Just wanted to say thanks for making this library and making it available. It let me cut out a bunch of code for working with Stripe webhooks. I contacted Stripe and told them I'd wished they'd linked to this from their docs as I would have found it earlier and it would have saved me a bunch of time.

Cheers,
David

nil class error for test and live events

I noticed from your implementation that test webhooks sent from the "test webhooks" button in the account settings panel on stripe will fail. They send an id of "evt_00000000000000" which will throw a 404 error when tried to retrieve as you are doing in your application_controller.rb on line 7: @event = Stripe::Event.retrieve(params[:id])

Now, I figured that I'd be fine for live transactions. I still end up in the same boat here:

Started POST "/stripe-comm-hook" for 50.18.189.119 at 2012-08-13 00:22:05 -0400
Processing by StripeEvent::WebhookController#event as XML
Parameters: {"object"=>"event", "type"=>"customer.updated", "created"=>1344828074, "pending_webhooks"=>1, "data"=>"object"=>{"id"=>"cus_08bwsfKEsvPJBm", "discount"=>nil, "description"=>"Baran, Christopherr", account_balance"=>0, "livemode"=>false, "active_card"=>nil, "object"=>"customer", "email"=>"[email protected]", created"=>1344375666, "subscription"=>{"customer"=>"cus_08bwsfKEsvPJBm", "start"=>1344789456, "ended_at"=>nil, trial_end"=>1347381456, "current_period_start"=>1344789456, "object"=>"subscription", "canceled_at"=>nil, current_period_end"=>1347381456, "status"=>"trialing", "trial_start"=>1344789456, "cancel_at_period_end"=>false, "plan"=>"id"=>"10", "currency"=>"usd", "trial_period_days"=>30, "livemode"=>false, "object"=>"plan", "amount"=>2000, name"=>"Pipe Fitters - Full Phone List", "interval"=>"month"}}, "delinquent"=>false}, "previous_attributes"=>"description"=>"Baran, Christopher"}}, "livemode"=>false, "id"=>"evt_0AZYkJQPKaMmhR", webhook"=>{"object"=>"event", "type"=>"customer.updated", "created"=>1344828074, "pending_webhooks"=>1, "data"=>"object"=>{"id"=>"cus_08bwsfKEsvPJBm", "discount"=>nil, "description"=>"Baran, Christopherr", account_balance"=>0, "livemode"=>false, "active_card"=>nil, "object"=>"customer", "email"=>"[email protected]", created"=>1344375666, "subscription"=>{"customer"=>"cus_08bwsfKEsvPJBm", "start"=>1344789456, "ended_at"=>nil, trial_end"=>1347381456, "current_period_start"=>1344789456, "object"=>"subscription", "canceled_at"=>nil, current_period_end"=>1347381456, "status"=>"trialing", "trial_start"=>1344789456, "cancel_at_period_end"=>false, "plan"=>"id"=>"10", "currency"=>"usd", "trial_period_days"=>30, "livemode"=>false, "object"=>"plan", "amount"=>2000, name"=>"Pipe Fitters - Full Phone List", "interval"=>"month"}}, "delinquent"=>false}, "previous_attributes"=>"description"=>"Baran, Christopher"}}, "livemode"=>false, "id"=>"evt_0AZYkJQPKaMmhR", controller"=>"stripe_event/webhook", "action"=>"event"}}
WARNING: Can't verify CSRF token authenticity
Completed 500 Internal Server Error in 31ms

NoMethodError (undefined method type' for nil:NilClass): stripe_event (0.3.0) lib/stripe_event.rb:12:inpublish'
stripe_event (0.3.0) app/controllers/stripe_event/webhook_controller.rb:4:in event' actionpack (3.2.5) lib/action_controller/metal/implicit_render.rb:4:insend_action'
actionpack (3.2.5) lib/abstract_controller/base.rb:167:in process_action' actionpack (3.2.5) lib/action_controller/metal/rendering.rb:10:inprocess_action'
actionpack (3.2.5) lib/abstract_controller/callbacks.rb:18:in block in process_action' activesupport (3.2.5) lib/active_support/callbacks.rb:414:in_run__377245576__process_action__943494040__callbacks'
activesupport (3.2.5) lib/active_support/callbacks.rb:405:in __run_callback' activesupport (3.2.5) lib/active_support/callbacks.rb:385:in_run_process_action_callbacks'
activesupport (3.2.5) lib/active_support/callbacks.rb:81:in run_callbacks' actionpack (3.2.5) lib/abstract_controller/callbacks.rb:17:inprocess_action'
actionpack (3.2.5) lib/action_controller/metal/rescue.rb:29:in process_action' actionpack (3.2.5) lib/action_controller/metal/instrumentation.rb:30:inblock in process_action'
activesupport (3.2.5) lib/active_support/notifications.rb:123:in block in instrument' activesupport (3.2.5) lib/active_support/notifications/instrumenter.rb:20:ininstrument'
activesupport (3.2.5) lib/active_support/notifications.rb:123:in instrument' actionpack (3.2.5) lib/action_controller/metal/instrumentation.rb:29:inprocess_action'
actionpack (3.2.5) lib/action_controller/metal/params_wrapper.rb:206:in process_action' activerecord (3.2.5) lib/active_record/railties/controller_runtime.rb:18:inprocess_action'
actionpack (3.2.5) lib/abstract_controller/base.rb:121:in process' actionpack (3.2.5) lib/abstract_controller/rendering.rb:45:inprocess'
rack-mini-profiler (0.1.7) lib/mini_profiler/profiling_methods.rb:62:in block in profile_method' actionpack (3.2.5) lib/action_controller/metal.rb:203:indispatch'
actionpack (3.2.5) lib/action_controller/metal/rack_delegation.rb:14:in dispatch' actionpack (3.2.5) lib/action_controller/metal.rb:246:inblock in action'
actionpack (3.2.5) lib/action_dispatch/routing/route_set.rb:73:in call' actionpack (3.2.5) lib/action_dispatch/routing/route_set.rb:73:indispatch'
actionpack (3.2.5) lib/action_dispatch/routing/route_set.rb:36:in call' journey (1.0.4) lib/journey/router.rb:68:inblock in call'
journey (1.0.4) lib/journey/router.rb:56:in each' journey (1.0.4) lib/journey/router.rb:56:incall'
actionpack (3.2.5) lib/action_dispatch/routing/route_set.rb:600:in call' railties (3.2.5) lib/rails/engine.rb:479:incall'
railties (3.2.5) lib/rails/railtie/configurable.rb:30:in method_missing' journey (1.0.4) lib/journey/router.rb:68:inblock in call'
journey (1.0.4) lib/journey/router.rb:56:in each' journey (1.0.4) lib/journey/router.rb:56:incall'
actionpack (3.2.5) lib/action_dispatch/routing/route_set.rb:600:in call' omniauth (1.1.0) lib/omniauth/strategy.rb:177:incall!'
omniauth (1.1.0) lib/omniauth/strategy.rb:157:in call' omniauth (1.1.0) lib/omniauth/strategy.rb:177:incall!'
omniauth (1.1.0) lib/omniauth/strategy.rb:157:in call' omniauth (1.1.0) lib/omniauth/strategy.rb:177:incall!'
omniauth (1.1.0) lib/omniauth/strategy.rb:157:in call' omniauth (1.1.0) lib/omniauth/strategy.rb:177:incall!'
omniauth (1.1.0) lib/omniauth/strategy.rb:157:in call' omniauth (1.1.0) lib/omniauth/strategy.rb:177:incall!'
omniauth (1.1.0) lib/omniauth/strategy.rb:157:in call' omniauth (1.1.0) lib/omniauth/strategy.rb:177:incall!'
omniauth (1.1.0) lib/omniauth/strategy.rb:157:in call' sass (3.1.19) lib/sass/plugin/rack.rb:54:incall'
warden (1.1.1) lib/warden/manager.rb:35:in block in call' warden (1.1.1) lib/warden/manager.rb:34:incatch'
warden (1.1.1) lib/warden/manager.rb:34:in call' actionpack (3.2.5) lib/action_dispatch/middleware/best_standards_support.rb:17:incall'
rack (1.4.1) lib/rack/etag.rb:23:in call' rack (1.4.1) lib/rack/conditionalget.rb:35:incall'
actionpack (3.2.5) lib/action_dispatch/middleware/head.rb:14:in call' actionpack (3.2.5) lib/action_dispatch/middleware/params_parser.rb:21:incall'
actionpack (3.2.5) lib/action_dispatch/middleware/flash.rb:238:in call' rack (1.4.1) lib/rack/session/abstract/id.rb:205:incontext'
rack (1.4.1) lib/rack/session/abstract/id.rb:200:in call' actionpack (3.2.5) lib/action_dispatch/middleware/cookies.rb:338:incall'
activerecord (3.2.5) lib/active_record/query_cache.rb:64:in call' activerecord (3.2.5) lib/active_record/connection_adapters/abstract/connection_pool.rb:473:incall'
actionpack (3.2.5) lib/action_dispatch/middleware/callbacks.rb:28:in block in call' activesupport (3.2.5) lib/active_support/callbacks.rb:405:in_run__599555566__call__907623258__callbacks'
activesupport (3.2.5) lib/active_support/callbacks.rb:405:in __run_callback' activesupport (3.2.5) lib/active_support/callbacks.rb:385:in_run_call_callbacks'
activesupport (3.2.5) lib/active_support/callbacks.rb:81:in run_callbacks' actionpack (3.2.5) lib/action_dispatch/middleware/callbacks.rb:27:incall'
actionpack (3.2.5) lib/action_dispatch/middleware/reloader.rb:65:in call' actionpack (3.2.5) lib/action_dispatch/middleware/remote_ip.rb:31:incall'
actionpack (3.2.5) lib/action_dispatch/middleware/debug_exceptions.rb:16:in call' actionpack (3.2.5) lib/action_dispatch/middleware/show_exceptions.rb:56:incall'
railties (3.2.5) lib/rails/rack/logger.rb:26:in call_app' railties (3.2.5) lib/rails/rack/logger.rb:16:incall'
actionpack (3.2.5) lib/action_dispatch/middleware/request_id.rb:22:in call' rack (1.4.1) lib/rack/methodoverride.rb:21:incall'
rack (1.4.1) lib/rack/runtime.rb:17:in call' activesupport (3.2.5) lib/active_support/cache/strategy/local_cache.rb:72:incall'
rack (1.4.1) lib/rack/lock.rb:15:in call' actionpack (3.2.5) lib/action_dispatch/middleware/static.rb:62:incall'
rack-mini-profiler (0.1.7) lib/mini_profiler/profiler.rb:233:in call' railties (3.2.5) lib/rails/engine.rb:479:incall'
railties (3.2.5) lib/rails/application.rb:220:in call' rack (1.4.1) lib/rack/content_length.rb:14:incall'
railties (3.2.5) lib/rails/rack/log_tailer.rb:17:in call' rack (1.4.1) lib/rack/handler/webrick.rb:59:inservice'
c:/codeshopruby/lib/ruby/1.9.1/webrick/httpserver.rb:138:in service' c:/codeshopruby/lib/ruby/1.9.1/webrick/httpserver.rb:94:inrun'
c:/codeshopruby/lib/ruby/1.9.1/webrick/server.rb:191:in `block in start_thread'

Rendered c:/codeshopruby/lib/ruby/gems/1.9.1/gems/actionpack-3.2.5/lib/action_dispatch/middleware/templates/rescues/_trace.erb (3.0ms)
Rendered c:/codeshopruby/lib/ruby/gems/1.9.1/gems/actionpack-3.2.5/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (2.0ms)
Rendered c:/codeshopruby/lib/ruby/gems/1.9.1/gems/actionpack-3.2.5/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (26.0ms)

I can retrieve the event from my console just fine:
irb(main):007:0> Stripe::Event.retrieve("evt_0AZYkJQPKaMmhR").type
=> "customer.updated"

Do you have any ideas what I'm doing wrong?

Also, I understand that manually retrieving the event from the webhook increases security, but it the process this breaks the test webhooks and it also requires another request. Why didn't you just use the data payload in the hook itself?

Stripe 1.2x

will there be support for stripe 1.2x gems?

Undefined Method 'delete' for Stripe::Event

Hi,

I'm just trying to get stripe_event setup to handle webhooks and I keep getting this error:

NoMethodError - undefined method `delete' for #<Stripe::Event:0x007fce9c5e7258>:

I have a feeling this might be caused by our version of the Stripe gem -- 1.10.2 -- but I'm still seeing it even after an upgrade. Any Ideas on what could be causing this? Thanks!

P.S. Here's the full trace:

Started POST "/stripe-webhooks" for 127.0.0.1 at 2014-11-10 11:13:28 -0800
Processing by StripeEvent::WebhookController#event as XML
Parameters: {"id"=>"evt_57cr8EODRQpDv5", "created"=>1415646803, "livemode"=>false, "type"=>"customer.deleted", "data"=>{"object"=>{"object"=>"customer", "created"=>1415646499, "id"=>"cus_57clcweULFyvT0", "livemode"=>false, "description"=>"sucka", "email"=>"[email protected]", "delinquent"=>false, "metadata"=>{}, "subscriptions"=>{"object"=>"list", "total_count"=>0, "has_more"=>false, "url"=>"/v1/customers/cus_57clcweULFyvT0/subscriptions", "data"=>nil, "count"=>0}, "discount"=>nil, "account_balance"=>0, "currency"=>nil, "cards"=>{"object"=>"list", "total_count"=>0, "has_more"=>false, "url"=>"/v1/customers/cus_57clcweULFyvT0/cards", "data"=>nil, "count"=>0}, "default_card"=>nil, "subscription"=>nil}}, "object"=>"event", "pending_webhooks"=>3, "request"=>"iar_57crcVpaBp5zTN", "api_version"=>"2013-08-13", "webhook"=>{"id"=>"evt_57cr8EODRQpDv5", "created"=>1415646803, "livemode"=>false, "type"=>"customer.deleted", "data"=>{"object"=>{"object"=>"customer", "created"=>1415646499, "id"=>"cus_57clcweULFyvT0", "livemode"=>false, "description"=>"sucka", "email"=>"[email protected]", "delinquent"=>false, "metadata"=>{}, "subscriptions"=>{"object"=>"list", "total_count"=>0, "has_more"=>false, "url"=>"/v1/customers/cus_57clcweULFyvT0/subscriptions", "data"=>nil, "count"=>0}, "discount"=>nil, "account_balance"=>0, "currency"=>nil, "cards"=>{"object"=>"list", "total_count"=>0, "has_more"=>false, "url"=>"/v1/customers/cus_57clcweULFyvT0/cards", "data"=>nil, "count"=>0}, "default_card"=>nil, "subscription"=>nil}}, "object"=>"event", "pending_webhooks"=>3, "request"=>"iar_57crcVpaBp5zTN", "api_version"=>"2013-08-13"}}
Completed 500 Internal Server Error in 223.1ms
** [Airbrake] Notice was not sent due to configuration:
Environment Monitored? false
API key set? true

NoMethodError - undefined method delete' for #<Stripe::Event:0x007fce9c5e7258>: () Users/dixon/.rvm/gems/ruby-2.0.0-p576@adstage/bundler/gems/stripe-ruby-637d5899f614/lib/stripe/stripe_object.rb:173:inmethod_missing'
meta_request (0.2.5) lib/meta_request/event.rb:11:in initialize' meta_request (0.2.5) lib/meta_request/railtie.rb:19:inblock (2 levels) in class:Railtie'
activesupport (3.2.17) lib/active_support/notifications/fanout.rb:47:in publish' activesupport (3.2.17) lib/active_support/notifications/fanout.rb:25:inblock in publish'
activesupport (3.2.17) lib/active_support/notifications/fanout.rb:25:in publish' activesupport (3.2.17) lib/active_support/notifications/instrumenter.rb:25:ininstrument'
activesupport (3.2.17) lib/active_support/notifications.rb:123:in instrument' stripe_event (1.4.0) lib/stripe_event.rb:28:ininstrument'
stripe_event (1.4.0) app/controllers/stripe_event/webhook_controller.rb:4:in event' actionpack (3.2.17) lib/action_controller/metal/implicit_render.rb:4:insend_action'
actionpack (3.2.17) lib/abstract_controller/base.rb:167:in process_action' actionpack (3.2.17) lib/action_controller/metal/rendering.rb:10:inprocess_action'
actionpack (3.2.17) lib/abstract_controller/callbacks.rb:18:in block in process_action' activesupport (3.2.17) lib/active_support/callbacks.rb:436:in_run__4114123393918112015__process_action__3072714001403839556__callbacks'
activesupport (3.2.17) lib/active_support/callbacks.rb:405:in __run_callback' activesupport (3.2.17) lib/active_support/callbacks.rb:385:in_run_process_action_callbacks'
activesupport (3.2.17) lib/active_support/callbacks.rb:81:in run_callbacks' actionpack (3.2.17) lib/abstract_controller/callbacks.rb:17:inprocess_action'
actionpack (3.2.17) lib/action_controller/metal/rescue.rb:29:in process_action' actionpack (3.2.17) lib/action_controller/metal/instrumentation.rb:30:inblock in process_action'
activesupport (3.2.17) lib/active_support/notifications.rb:123:in block in instrument' activesupport (3.2.17) lib/active_support/notifications/instrumenter.rb:20:ininstrument'
activesupport (3.2.17) lib/active_support/notifications.rb:123:in instrument' actionpack (3.2.17) lib/action_controller/metal/instrumentation.rb:29:inprocess_action'
actionpack (3.2.17) lib/action_controller/metal/params_wrapper.rb:207:in process_action' activerecord (3.2.17) lib/active_record/railties/controller_runtime.rb:18:inprocess_action'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/rails3/action_controller.rb:32:in block in process_action' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/controller_instrumentation.rb:365:inperform_action_with_newrelic_trace'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/rails3/action_controller.rb:31:in process_action' actionpack (3.2.17) lib/abstract_controller/base.rb:121:inprocess'
actionpack (3.2.17) lib/abstract_controller/rendering.rb:45:in process' actionpack (3.2.17) lib/action_controller/metal.rb:203:indispatch'
actionpack (3.2.17) lib/action_controller/metal/rack_delegation.rb:14:in dispatch' actionpack (3.2.17) lib/action_controller/metal.rb:246:inblock in action'
actionpack (3.2.17) lib/action_dispatch/routing/route_set.rb:73:in dispatch' actionpack (3.2.17) lib/action_dispatch/routing/route_set.rb:36:incall'
journey (1.0.4) lib/journey/router.rb:68:in block in call' journey (1.0.4) lib/journey/router.rb:56:incall'
actionpack (3.2.17) lib/action_dispatch/routing/route_set.rb:608:in call' railties (3.2.17) lib/rails/engine.rb:484:incall'
railties (3.2.17) lib/rails/railtie/configurable.rb:30:in method_missing' journey (1.0.4) lib/journey/router.rb:68:inblock in call'
journey (1.0.4) lib/journey/router.rb:56:in call' actionpack (3.2.17) lib/action_dispatch/routing/route_set.rb:608:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' omniauth (1.1.4) lib/omniauth/strategy.rb:184:incall!'
omniauth (1.1.4) lib/omniauth/strategy.rb:164:in call' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
omniauth (1.1.4) lib/omniauth/strategy.rb:184:in call!' omniauth (1.1.4) lib/omniauth/strategy.rb:164:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' omniauth (1.1.4) lib/omniauth/strategy.rb:184:incall!'
omniauth (1.1.4) lib/omniauth/strategy.rb:164:in call' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
omniauth (1.1.4) lib/omniauth/builder.rb:49:in call' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
meta_request (0.2.5) lib/meta_request/middlewares/app_request_handler.rb:11:in call' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
rack-contrib (1.1.0) lib/rack/contrib/response_headers.rb:17:in call' meta_request (0.2.5) lib/meta_request/middlewares/headers.rb:16:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' meta_request (0.2.5) lib/meta_request/middlewares/meta_request_handler.rb:13:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' () Users/dixon/.rvm/gems/ruby-2.0.0-p576@adstage/bundler/gems/hirefire-resource-80e8dc4170a9/lib/hirefire/middleware.rb:32:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' rack-timeout (0.0.3) lib/rack/timeout.rb:16:inblock in call'
/Users/dixon/.rvm/rubies/ruby-2.0.0-p576/lib/ruby/2.0.0/timeout.rb:66:in timeout' rack-timeout (0.0.3) lib/rack/timeout.rb:16:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' newrelic_rpm (3.9.5.251) lib/new_relic/rack/error_collector.rb:50:intraced_call'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:55:in call' newrelic_rpm (3.9.5.251) lib/new_relic/rack/agent_hooks.rb:26:intraced_call'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:55:in call' newrelic_rpm (3.9.5.251) lib/new_relic/rack/browser_monitoring.rb:23:intraced_call'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:55:in call' newrelic_rpm (3.9.5.251) lib/new_relic/rack/developer_mode.rb:48:intraced_call'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:55:in call' rack-raw-upload (1.1.1) lib/rack/raw_upload.rb:72:inconvert_and_pass_on'
rack-raw-upload (1.1.1) lib/rack/raw_upload.rb:18:in call' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
warden (1.2.3) lib/warden/manager.rb:35:in block in call' warden (1.2.3) lib/warden/manager.rb:34:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' actionpack (3.2.17) lib/action_dispatch/middleware/best_standards_support.rb:17:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' rack (1.4.5) lib/rack/etag.rb:23:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' rack (1.4.5) lib/rack/conditionalget.rb:35:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' actionpack (3.2.17) lib/action_dispatch/middleware/head.rb:14:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' actionpack (3.2.17) lib/action_dispatch/middleware/params_parser.rb:21:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' actionpack (3.2.17) lib/action_dispatch/middleware/flash.rb:242:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' rack (1.4.5) lib/rack/session/abstract/id.rb:210:incontext'
rack (1.4.5) lib/rack/session/abstract/id.rb:205:in call' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
actionpack (3.2.17) lib/action_dispatch/middleware/cookies.rb:341:in call' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
activerecord (3.2.17) lib/active_record/query_cache.rb:64:in call' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
activerecord (3.2.17) lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in call' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
actionpack (3.2.17) lib/action_dispatch/middleware/callbacks.rb:28:in block in call' activesupport (3.2.17) lib/active_support/callbacks.rb:405:in_run__4007266311264310826__call__2598003488275731220__callbacks'
activesupport (3.2.17) lib/active_support/callbacks.rb:405:in __run_callback' activesupport (3.2.17) lib/active_support/callbacks.rb:385:in_run_call_callbacks'
activesupport (3.2.17) lib/active_support/callbacks.rb:81:in run_callbacks' actionpack (3.2.17) lib/action_dispatch/middleware/callbacks.rb:27:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' actionpack (3.2.17) lib/action_dispatch/middleware/reloader.rb:65:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' actionpack (3.2.17) lib/action_dispatch/middleware/remote_ip.rb:31:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' airbrake (4.1.0) lib/airbrake/rails/middleware.rb:13:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' better_errors (1.1.0) lib/better_errors/middleware.rb:84:inprotected_app_call'
better_errors (1.1.0) lib/better_errors/middleware.rb:79:in better_errors_call' better_errors (1.1.0) lib/better_errors/middleware.rb:56:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' actionpack (3.2.17) lib/action_dispatch/middleware/debug_exceptions.rb:16:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' actionpack (3.2.17) lib/action_dispatch/middleware/show_exceptions.rb:56:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' railties (3.2.17) lib/rails/rack/logger.rb:32:incall_app'
railties (3.2.17) lib/rails/rack/logger.rb:16:in block in call' activesupport (3.2.17) lib/active_support/tagged_logging.rb:22:intagged'
railties (3.2.17) lib/rails/rack/logger.rb:16:in call' config/initializers/quiet_assets.rb:7:incall_with_quiet_assets'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' actionpack (3.2.17) lib/action_dispatch/middleware/request_id.rb:22:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' rack (1.4.5) lib/rack/methodoverride.rb:21:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' rack (1.4.5) lib/rack/runtime.rb:17:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' activesupport (3.2.17) lib/active_support/cache/strategy/local_cache.rb:72:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' rack (1.4.5) lib/rack/lock.rb:15:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' actionpack (3.2.17) lib/action_dispatch/middleware/static.rb:63:incall'
newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:in call' airbrake (4.1.0) lib/airbrake/user_informer.rb:16:in_call'
airbrake (4.1.0) lib/airbrake/user_informer.rb:12:in call' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
railties (3.2.17) lib/rails/engine.rb:484:in call' railties (3.2.17) lib/rails/application.rb:231:incall'
railties (3.2.17) lib/rails/railtie/configurable.rb:30:in method_missing' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
rack (1.4.5) lib/rack/deflater.rb:13:in call' newrelic_rpm (3.9.5.251) lib/new_relic/agent/instrumentation/middleware_tracing.rb:57:incall'
rack (1.4.5) lib/rack/content_length.rb:14:in call' railties (3.2.17) lib/rails/rack/log_tailer.rb:17:incall'
unicorn (4.6.3) lib/unicorn/http_server.rb:552:in process_client' unicorn (4.6.3) lib/unicorn/http_server.rb:632:inworker_loop'
unicorn (4.6.3) lib/unicorn/http_server.rb:500:in spawn_missing_workers' unicorn (4.6.3) lib/unicorn/http_server.rb:142:instart'
unicorn-rails (1.1.0) lib/unicorn/rails.rb:24:in run' rack (1.4.5) lib/rack/server.rb:268:instart'
railties (3.2.17) lib/rails/commands/server.rb:70:in start' zeus (0.15.2) lib/zeus/rails.rb:160:inserver'
zeus (0.15.2) lib/zeus.rb:148:in block in command' zeus (0.15.2) lib/zeus.rb:135:incommand'
zeus (0.15.2) lib/zeus.rb:50:in go' -e:1:in

'

RSpec running within CircleCI: No route matches [POST]

I've been writing mock tests with RSpec. The problem is the end point I specified doesn't seem to accept the POST request within CircleCI container. (The test works fine locally.)

 1) stripe_invoice_created_webhook responds 404 to invoice_created webhook with invalid endpoint
     Failure/Error: post '/stripe-hogehoge', event.as_json

     ActionController::RoutingError:
       No route matches [POST] "/stripe-hogehoge"

In config/routes.rb

Rails.application.routes.draw do

  # Handle Stripe Webhooks
  mount StripeEvent::Engine, at:'/stripe-events'

I use

  • ruby 2.3.0
  • rails 4.2.5

rspec / capybara?

I'm curious - is there a way to test the webhooks in my app? I basically copied your stripe_event_spec and used the fixtures, which "works" but feels terrible. Is there a DRY'er way?

Thanks,

Matt

Account application deauthorized

I have an app with multiple stripe accounts. If a user revokes access to the app, it sends the 'account.application.deauthorized' event. The event retriever no longer has access to verify the event since access was revoked.

I'd rather not bypass event verification. Any ideas?

Catch Stripe::StripeError in StripeEvent.instrument

Hi,

I recently ran into an issue where my webhook was mistakenly raising Stripe::StripeError. My webhook implementation calls Stripe::InvoiceItem.create as part of metered billing, and it didn't occur to me at first that stripe_event was swallowing the exception.

What about catching Stripe::StripeError around event_retriever.call(params) and letting any exceptions in the user block float to the top?

Also, we can't use the Test Webhooks section in Stripe because of the evt_00000000000000 issue. Would you consider a flag that disables verification for testing purposes? Or should we just write our own event_retriever implementation?

I don't mind helping with a pull request - I just want to be sure these features are desirable.

Stripe Dashboard Event Testing

I really needed to test a webhook from the UI. So i devised the following to add to an initializer.

if Rails.env.development?
  StripeEvent.event_retriever = lambda do |params|
    Stripe::Event.construct_from(params)
  end
end

This might be worth adding to the documentation, it basically allows us to bypass the fetch retriever in development mode.

Filtering for only live events

From the stripe webhook documentation(https://stripe.com/docs/webhooks)

With application webhooks, it's important to note that while only test webhooks will be sent to development applications, both live and test webhooks will be sent to production applications. We recommend that you check the livemode of the webhook before processing events.

However I am having a lot of trouble filtering out testmode webhooks when using stripe_event. I am using the following event_retriever

class EventRetriever
  def call(params)
    secret_key = Shop.find_by_stripe_id!(params[:user_id]).stripe_access_token
    event = Stripe::Event.retrieve(params[:id], secret_key)

    # add user id for shop selection
    event.user_id = params[:user_id]
    event
  end
end

Does anyone have any ideas on how to filter for only livemode webhooks using stripe_event for a stripe_connect application?

Thank You

Stripe Events not in order

I noticed that sometimes the stripe events are not in order. For example, invoice.created maybe come at a later date after invoice.payment_succeeded and charge.succeeded.

I read that the only solution is to reject the webhooks and stripe will send them at a later date. How can do it with stripe_event?

Rails 5 before_filter deprecation

Rails is moving away from *_filter controller callbacks to *_action. Unfortunately *_action was only added in rails 4, but stripe_event currently supports rails >= 3.1.

I'd be willing to make a PR to change things but wasn't sure if backwards compatibility was required.

Using Background job

First off, thanks for the nice library.
I've been using stripe_event for test phase, and works pretty well.

Let's assume that

  • handling little complex logic within in webhook hanlder
  • listening many webhook requests

In this case, I feel I need to consider to using background job.

Best practices in stripe doc

If your webhook script performs complex logic, or makes network calls, it's possible the script >would timeout before Stripe sees its complete execution. For that reason, you may want to have >your webhook endpoint immediately acknowledge receipt by returning a 2xx HTTP status code, >and then perform the rest of its duties.

Here is my code,
I've just wondered which part I should bundle and enqueue?

StripeEvent.event_retriever = lambda do |params|
  return nil if StripeWebhook.exists?(stripe_id: params[:id])
  StripeWebhook.create!(stripe_id: params[:id])

  return Stripe::Event.construct_from(params.deep_symbolize_keys) if Rails.env.test?

  return Stripe::Event.retrieve(params[:id])
end

StripeEvent.configure do |events|
  events.subscribe 'invoice.created', InvoiceCreated.new
  events.subscribe 'invoice.payment_succeeded', InvoicePaymentSucceeded.new
 ...
end

Returning proper error responses back to Stripe webhook calls

Hi Guys,

I think it's good to add in the README for sending proper error response back to Stripe Webhook calls by raising Stripe::StripeError, since your Webhook controller implementation already has a rescue that returns a :unauthorized status. Example:-

subscribe 'invoice.payment_succeeded' do |event|
  subscription = Subscription.find_by_stripe_invoice_id(event.data.object.id)
  if subscription
    subscription.succeed!
  else
    raise Stripe::StripeError
  end
end

Quite often that Stripe sends over a post call to my application where, for some reason, records are not found and responds with a 500 error. Only after a 2nd retry from Stripe that it goes through.

It's also stated in Stripe that they will retry if the response they get is other than 200 OK - https://stripe.com/docs/webhooks

Let me know what you guys think.

Stripe Connect

With Stripe Connect you have to configure a separate endpoint (in the Stripe Dashboard Webhook Settings) to receive notifications related to your connected stripe accounts. Stripe Connect events themselves don't look any different than regular Account events and as such makes them indistinguishable from each other. I'd like to see some examples of how this library might be used to handle both Account events (webhook events on your own account) and Connect events (webhook events on connected accounts).

Replay attack prevention

Stripe's webhooks guide says:

We also advise you to guard against replay-attacks by recording which events you receive, and never processing events twice.

Since I'm using webhooks to enable and disable access to accounts, it seems wise to include this kind of protection. Is there some reason this isn't worth doing? Does StripeEvent have a built-in mechanism to do this?

If I need to do it myself, I suppose I can use an event retriever proc to check that the event ID isn't in a list of used IDs, but recording to that list is a different story. Is there a similar hook that runs after all subscribers have seen the event, so I can ensure that I don't record events where the handler raised an error?

Can't get it to work

I set up my app the same way as the README and the webhooks don't work.

This is in my config/initializers/stripe.rb

class CustomerCreated
  def call(event)
    u = User.find(1)
    u.first_name = "bear"
    u.save!

    puts "whoohoo!"
  end
end

StripeEvent.configure do |events|
  events.subscribe 'customer.created', CustomerCreated.new

  events.all do |event|
    # Handle all event types - logging, etc.
  end
end

this is in my routes.rb

mount StripeEvent::Engine => '/stripe-webhooks'

Is there something else I have to do? Do I need to create a controller?
I am using http://www.ultrahook.com/ if it matters

401 Unauthorized when testing with rspec

Hi,
I am trying to test the webhooks through rspec. I make the following request:

post "/stripe", :id => "evt_customer_subscription_deleted"

And the response returns a 401 unauthorized, event though the stripe initialize is listening to this event and I have had this successfully deployed for quite a while.

Update subscribe DSL to support user_id if webhook was issued by connected Stripe account

Hello. First of all thanks for a nice gem.

My use case is the following: I allow users to connect their Stripe accounts to my application using Stripe Connect feature.

When a webhook is issued by a connected Stripe account, Stripe include user_id param. You already describe this case here - https://github.com/integrallis/stripe_event#configuration. To retrieve the Event object I followed your guide and added a custom retriever.

Then I cannot use your DSL to subscribe to events, because I have no access to the original params map and don't know which user the event has been issued for. I have to process it manually. Something like:

initializer:

StripeEvent.event_retriever = lambda do |params|
  Providers::StripeProvider.retrieve_event(params)
end

StripeProvider.rb

def process_event(params)
  user = User.where(stripe_account_id: params[:user_id]).first
  event = Stripe::Event.retrieve(params[:id], user.stripe_access_token)
  handle_event(user, event)
end

def handle_event(user, event)
  case event.type          
  when 'customer.created'
    user.handle_customer_created(event.data.object)
  when 'customer.updated'
    user.handle_customer_updated(event.data.object)
  when 'customer.deleted'
    user.handle_customer_deleted(event.data.object)
  end  
end

What I really miss is the ability to use your DSL like that:

StripeEvent.configure do |events|
  events.subscribe 'customer.created' do |event, params|
    user = User.where(stripe_account_id: params[:user_id]).first
    user.handle_customer_created(event.data.object)
  end  
end

Parameter params in this DSL should be optional. You will save compatibility with current version and make it super flexible for those users that need access to the user_id.

Thanks for considering this enhancement.
Konstantin.

Can we get a "test mode" option that skips the 'retrieve' check.

From the README:

This button sends an example event to your webhook urls, including an id of evt_00000000000000. To confirm that Stripe sent the webhook, StripeEvent attempts to retrieve the event details from Stripe using the given id. In this case the event does not exist and StripeEvent responds with 401 Unauthorized. Instead of using the 'Test Webhooks' button, trigger webhooks by using the Stripe API or Dashboard to create test payments, customers, etc.

Makes sense - except this is completely impractical for many kinds of events. (How can I trigger a customer.subscription.trial_will_end event from the dashboard, short of creating a plan with a 1-day trial, creating a new customer on that plan, then waiting 24 hours for it to expire? I'd like to able to trigger test webhooks more than once a day!)

I've been able to work around this in my tests by replacing this line with the following:

    event = Stripe::Event.construct_from(params)

Of course, this is a terrible idea in a production environment, but it makes my life MUCH easier when testing (and I don't care about validating that the stripe event is 'real' in my tests).

How about I add a config option to StripeEvent called "test_mode", a boolean that's false by default? When it's false, nothing changes from the current implementation of the gem. When it's true, the gem skips the authorization checks using my line above.

(Potentially it could also raise a warning or an error if StripeEvent is in test mode while the Rails env is in production)

What do you think? I'm happy to code this up myself, I just wanted to make sure it would actually get accepted before I start coding.

Rails 4 Compatibility

I'm running into some issues when testing stripe_event with Rails 4 Beta 1.

I'm happy to look into this, but I thought I'd ask to see if I could get an idea of where to start looking.

After removing the Rails 3.1 requirement in the gemspec, all posts to the mount point get a 404 not found error:

Started POST "/stripe" for 127.0.0.1 at 2013-03-04 03:38:55 -0800

ActionController::RoutingError (No route matches [POST] "/stripe"):
  vendor/bundle/gems/actionpack-4.0.0.beta1/lib/action_dispatch/middleware/debug_exceptions.rb:21:in `call'
  vendor/bundle/gems/actionpack-4.0.0.beta1/lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  vendor/bundle/gems/railties-4.0.0.beta1/lib/rails/rack/logger.rb:38:in `call_app'
  vendor/bundle/gems/railties-4.0.0.beta1/lib/rails/rack/logger.rb:21:in `block in call'
  vendor/bundle/gems/activesupport-4.0.0.beta1/lib/active_support/tagged_logging.rb:67:in `block in tagged'
  vendor/bundle/gems/activesupport-4.0.0.beta1/lib/active_support/tagged_logging.rb:25:in `tagged'
  vendor/bundle/gems/activesupport-4.0.0.beta1/lib/active_support/tagged_logging.rb:67:in `tagged'
  vendor/bundle/gems/railties-4.0.0.beta1/lib/rails/rack/logger.rb:21:in `call'
  vendor/bundle/gems/quiet_assets-1.0.2/lib/quiet_assets.rb:18:in `call_with_quiet_assets'
  vendor/bundle/gems/actionpack-4.0.0.beta1/lib/action_dispatch/middleware/request_id.rb:21:in `call'
  vendor/bundle/gems/rack-1.5.2/lib/rack/methodoverride.rb:21:in `call'
  vendor/bundle/gems/rack-1.5.2/lib/rack/runtime.rb:17:in `call'
  vendor/bundle/gems/activesupport-4.0.0.beta1/lib/active_support/cache/strategy/local_cache.rb:72:in `call'
  vendor/bundle/gems/rack-1.5.2/lib/rack/lock.rb:17:in `call'
  vendor/bundle/gems/actionpack-4.0.0.beta1/lib/action_dispatch/middleware/static.rb:64:in `call'
  vendor/bundle/gems/railties-4.0.0.beta1/lib/rails/engine.rb:510:in `call'
  vendor/bundle/gems/railties-4.0.0.beta1/lib/rails/application.rb:96:in `call'
  vendor/bundle/gems/railties-4.0.0.beta1/lib/rails/railtie/configurable.rb:30:in `method_missing'
  /Users/ben/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:147:in `handle'
  /Users/ben/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:99:in `rescue in block (2 levels) in start'
  /Users/ben/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:96:in `block (2 levels) in start'
  /Users/ben/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:86:in `each'
  /Users/ben/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:86:in `block in start'
  /Users/ben/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:66:in `loop'
  /Users/ben/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:66:in `start'
  /Users/ben/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:13:in `run'
  /Users/ben/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/bin/nack_worker:4:in `<main>'


  Rendered vendor/bundle/gems/actionpack-4.0.0.beta1/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.9ms)
  Rendered vendor/bundle/gems/actionpack-4.0.0.beta1/lib/action_dispatch/middleware/templates/routes/_route.html.erb (8.7ms)
  Rendered vendor/bundle/gems/actionpack-4.0.0.beta1/lib/action_dispatch/middleware/templates/routes/_route.html.erb (0.2ms)
  Rendered vendor/bundle/gems/actionpack-4.0.0.beta1/lib/action_dispatch/middleware/templates/routes/_table.html.erb (4.5ms)
  Rendered vendor/bundle/gems/actionpack-4.0.0.beta1/lib/action_dispatch/middleware/templates/rescues/routing_error.erb within rescues/layout (85.9ms)

account.updated event results in 401 status code

Hi,

I'm trying to test the account.updated webhook, but my subscriber objects are not called and the status is 401.

15:50:32 log.1    | Started POST "/stripe/webhooks" for 54.187.216.72 at 2016-01-12 15:50:32 -0800
15:50:32 log.1    | Cannot render console from 54.187.216.72! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
15:50:32 log.1    | Value for params[:data][:object][:verification][:fields_needed] was set to nil, because it was one of [], [null] or [null, null, ...]. Go to http://guides.rubyonrails.org/security.html#unsafe-query-generation for more information.
15:50:32 log.1    | Processing by StripeEvent::WebhookController#event as XML
15:50:32 log.1    |   Parameters: {"id"=>"evt_17Sbu8ALH9ylSV0DbMbDtR0r", "object"=>"event", "api_version"=>"2015-10-16", "created"=>1452642624, "data"=>{"object"=>{"id"=>"acct_17SVByALH9ylSV0D", "object"=>"account", "business_logo"=>nil, "business_name"=>"ABC Store", "business_url"=>nil, "charges_enabled"=>true, "country"=>"US", "currencies_supported"=>["usd", "aed", "afn", "all", "amd", "ang", "aoa", "ars", "aud", "awg", "azn", "bam", "bbd", "bdt", "bgn", "bif", "bmd", "bnd", "bob", "brl", "bsd", "bwp", "bzd", "cad", "cdf", "chf", "clp", "cny", "cop", "crc", "cve", "czk", "djf", "dkk", "dop", "dzd", "egp", "etb", "eur", "fjd", "fkp", "gbp", "gel", "gip", "gmd", "gnf", "gtq", "gyd", "hkd", "hnl", "hrk", "htg", "huf", "idr", "ils", "inr", "isk", "jmd", "jpy", "kes", "kgs", "khr", "kmf", "krw", "kyd", "kzt", "lak", "lbp", "lkr", "lrd", "lsl", "ltl", "mad", "mdl", "mga", "mkd", "mnt", "mop", "mro", "mur", "mvr", "mwk", "mxn", "myr", "mzn", "nad", "ngn", "nio", "nok", "npr", "nzd", "pab", "pen", "pgk", "php", "pkr", "pln", "pyg", "qar", "ron", "rsd", "rub", "rwf", "sar", "sbd", "scr", "sek", "sgd", "shp", "sll", "sos", "srd", "std", "svc", "szl", "thb", "tjs", "top", "try", "ttd", "twd", "tzs", "uah", "ugx", "uyu", "uzs", "vnd", "vuv", "wst", "xaf", "xcd", "xof", "xpf", "yer", "zar", "zmw"], "default_currency"=>"usd", "details_submitted"=>false, "display_name"=>nil, "email"=>nil, "managed"=>true, "statement_descriptor"=>nil, "support_phone"=>nil, "timezone"=>"Etc/UTC", "transfers_enabled"=>true, "debit_negative_balances"=>false, "decline_charge_on"=>{"avs_failure"=>false, "cvc_failure"=>false}, "external_accounts"=>{"object"=>"list", "data"=>[{"id"=>"ba_17SWBzALH9ylSV0DmWrXWXiv", "object"=>"bank_account", "account"=>"acct_17SVByALH9ylSV0D", "bank_name"=>"STRIPE TEST BANK", "country"=>"US", "currency"=>"usd", "default_for_currency"=>true, "fingerprint"=>"RHmpw2FNYY8tV7jp", "last4"=>"6789", "metadata"=>{}, "name"=>nil, "routing_number"=>"110000000", "status"=>"new"}], "has_more"=>false, "total_count"=>1, "url"=>"/v1/accounts/acct_17SVByALH9ylSV0D/external_accounts"}, "legal_entity"=>{"additional_owners"=>nil, "address"=>{"city"=>"Cupertino", "country"=>"US", "line1"=>"1 Infinite Loop", "line2"=>nil, "postal_code"=>"95014", "state"=>"CA"}, "business_name"=>"ABC Store", "dob"=>{"day"=>4, "month"=>4, "year"=>1990}, "first_name"=>"ABC", "last_name"=>"Developer", "personal_address"=>{"city"=>"Cupertino", "country"=>"US", "line1"=>"1 Infinite Loop", "line2"=>nil, "postal_code"=>"95014", "state"=>"CA"}, "personal_id_number_provided"=>false, "ssn_last_4_provided"=>false, "type"=>"company", "verification"=>{"details"=>nil, "details_code"=>nil, "document"=>nil, "status"=>"unverified"}}, "metadata"=>{}, "product_description"=>nil, "tos_acceptance"=>{"date"=>1452037418, "ip"=>"127.0.0.1", "user_agent"=>nil}, "transfer_schedule"=>{"delay_days"=>2, "interval"=>"weekly", "weekly_anchor"=>"monday"}, "verification"=>{"disabled_reason"=>nil, "due_by"=>nil, "fields_needed"=>nil}}, "previous_attributes"=>{"business_name"=>"ABC Developer Store"}}, "livemode"=>false, "pending_webhooks"=>1, "request"=>"req_7i1yoOdJ9idSi9", "type"=>"account.updated", "user_id"=>"acct_17SVByALH9ylSV0D", "webhook"=>{"id"=>"evt_17Sbu8ALH9ylSV0DbMbDtR0r", "object"=>"event", "api_version"=>"2015-10-16", "created"=>1452642624, "data"=>{"object"=>{"id"=>"acct_17SVByALH9ylSV0D", "object"=>"account", "business_logo"=>nil, "business_name"=>"ABC Store", "business_url"=>nil, "charges_enabled"=>true, "country"=>"US", "currencies_supported"=>["usd", "aed", "afn", "all", "amd", "ang", "aoa", "ars", "aud", "awg", "azn", "bam", "bbd", "bdt", "bgn", "bif", "bmd", "bnd", "bob", "brl", "bsd", "bwp", "bzd", "cad", "cdf", "chf", "clp", "cny", "cop", "crc", "cve", "czk", "djf", "dkk", "dop", "dzd", "egp", "etb", "eur", "fjd", "fkp", "gbp", "gel", "gip", "gmd", "gnf", "gtq", "gyd", "hkd", "hnl", "hrk", "htg", "huf", "idr", "ils", "inr", "isk", "jmd", "jpy", "kes", "kgs", "khr", "kmf", "krw", "kyd", "kzt", "lak", "lbp", "lkr", "lrd", "lsl", "ltl", "mad", "mdl", "mga", "mkd", "mnt", "mop", "mro", "mur", "mvr", "mwk", "mxn", "myr", "mzn", "nad", "ngn", "nio", "nok", "npr", "nzd", "pab", "pen", "pgk", "php", "pkr", "pln", "pyg", "qar", "ron", "rsd", "rub", "rwf", "sar", "sbd", "scr", "sek", "sgd", "shp", "sll", "sos", "srd", "std", "svc", "szl", "thb", "tjs", "top", "try", "ttd", "twd", "tzs", "uah", "ugx", "uyu", "uzs", "vnd", "vuv", "wst", "xaf", "xcd", "xof", "xpf", "yer", "zar", "zmw"], "default_currency"=>"usd", "details_submitted"=>false, "display_name"=>nil, "email"=>nil, "managed"=>true, "statement_descriptor"=>nil, "support_phone"=>nil, "timezone"=>"Etc/UTC", "transfers_enabled"=>true, "debit_negative_balances"=>false, "decline_charge_on"=>{"avs_failure"=>false, "cvc_failure"=>false}, "external_accounts"=>{"object"=>"list", "data"=>[{"id"=>"ba_17SWBzALH9ylSV0DmWrXWXiv", "object"=>"bank_account", "account"=>"acct_17SVByALH9ylSV0D", "bank_name"=>"STRIPE TEST BANK", "country"=>"US", "currency"=>"usd", "default_for_currency"=>true, "fingerprint"=>"RHmpw2FNYY8tV7jp", "last4"=>"6789", "metadata"=>{}, "name"=>nil, "routing_number"=>"110000000", "status"=>"new"}], "has_more"=>false, "total_count"=>1, "url"=>"/v1/accounts/acct_17SVByALH9ylSV0D/external_accounts"}, "legal_entity"=>{"additional_owners"=>nil, "address"=>{"city"=>"Cupertino", "country"=>"US", "line1"=>"1 Infinite Loop", "line2"=>nil, "postal_code"=>"95014", "state"=>"CA"}, "business_name"=>"ABC Store", "dob"=>{"day"=>4, "month"=>4, "year"=>1990}, "first_name"=>"ABC", "last_name"=>"Developer", "personal_address"=>{"city"=>"Cupertino", "country"=>"US", "line1"=>"1 Infinite Loop", "line2"=>nil, "postal_code"=>"95014", "state"=>"CA"}, "personal_id_number_provided"=>false, "ssn_last_4_provided"=>false, "type"=>"company", "verification"=>{"details"=>nil, "details_code"=>nil, "document"=>nil, "status"=>"unverified"}}, "metadata"=>{}, "product_description"=>nil, "tos_acceptance"=>{"date"=>1452037418, "ip"=>"127.0.0.1", "user_agent"=>nil}, "transfer_schedule"=>{"delay_days"=>2, "interval"=>"weekly", "weekly_anchor"=>"monday"}, "verification"=>{"disabled_reason"=>nil, "due_by"=>nil, "fields_needed"=>nil}}, "previous_attributes"=>{"business_name"=>"ABC Developer Store"}}, "livemode"=>false, "pending_webhooks"=>1, "request"=>"req_7i1yoOdJ9idSi9", "type"=>"account.updated", "user_id"=>"acct_17SVByALH9ylSV0D"}}
15:50:32 log.1    | Geokit is using the domain: ngrok.io
15:50:32 log.1    | Completed 401 Unauthorized in 212ms (ActiveRecord: 0.0ms)
15:50:32 web.1    | [94107] 54.187.216.72 - - [12/Jan/2016:15:50:32 -0800] "POST / HTTP/1.1" 401 - 0.2298

But, other events, such as charge.succeeded work with no problem.

15:42:35 log.1    | Started POST "/stripe/webhooks" for 54.187.216.72 at 2016-01-12 15:42:35 -0800
15:42:35 log.1    | Cannot render console from 54.187.216.72! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
15:42:35 log.1    | Value for params[:data][:object][:refunds][:data] was set to nil, because it was one of [], [null] or [null, null, ...]. Go to http://guides.rubyonrails.org/security.html#unsafe-query-generation for more information.
15:42:35 log.1    | Processing by StripeEvent::WebhookController#event as XML
15:42:35 log.1    |   Parameters: {"id"=>"evt_17SbmQGRxo80gNumHix3Ut6y", "object"=>"event", "api_version"=>"2015-10-16", "created"=>1452642146, "data"=>{"object"=>{"id"=>"ch_17SbmPGRxo80gNumukbaoHrB", "object"=>"charge", "amount"=>99, "amount_refunded"=>0, "application_fee"=>"fee_7i1qWN24rvCXIx", "balance_transaction"=>"txn_17SbmPGRxo80gNumP1nLgbcn", "captured"=>true, "created"=>1452642145, "currency"=>"usd", "customer"=>nil, "description"=>nil, "destination"=>"acct_17BAkdB2ypFVGqS2", "dispute"=>nil, "failure_code"=>nil, "failure_message"=>nil, "fraud_details"=>{}, "invoice"=>nil, "livemode"=>false, "metadata"=>{}, "paid"=>true, "receipt_email"=>nil, "receipt_number"=>nil, "refunded"=>false, "refunds"=>{"object"=>"list", "data"=>nil, "has_more"=>false, "total_count"=>0, "url"=>"/v1/charges/ch_17SbmPGRxo80gNumukbaoHrB/refunds"}, "shipping"=>nil, "source"=>{"id"=>"card_17SbmKGRxo80gNumk8gnMXL1", "object"=>"card", "address_city"=>nil, "address_country"=>nil, "address_line1"=>nil, "address_line1_check"=>nil, "address_line2"=>nil, "address_state"=>nil, "address_zip"=>nil, "address_zip_check"=>nil, "brand"=>"Visa", "country"=>"US", "customer"=>nil, "cvc_check"=>"pass", "dynamic_last4"=>nil, "exp_month"=>11, "exp_year"=>2016, "fingerprint"=>"s8DNQYfXI7bbYeRi", "funding"=>"credit", "last4"=>"4242", "metadata"=>{}, "name"=>nil, "tokenization_method"=>nil}, "statement_descriptor"=>nil, "status"=>"succeeded", "transfer"=>"tr_17SbmPGRxo80gNumgIzYFWYP"}}, "livemode"=>false, "pending_webhooks"=>1, "request"=>"req_7i1qYloeVk3PYk", "type"=>"charge.succeeded", "webhook"=>{"id"=>"evt_17SbmQGRxo80gNumHix3Ut6y", "object"=>"event", "api_version"=>"2015-10-16", "created"=>1452642146, "data"=>{"object"=>{"id"=>"ch_17SbmPGRxo80gNumukbaoHrB", "object"=>"charge", "amount"=>99, "amount_refunded"=>0, "application_fee"=>"fee_7i1qWN24rvCXIx", "balance_transaction"=>"txn_17SbmPGRxo80gNumP1nLgbcn", "captured"=>true, "created"=>1452642145, "currency"=>"usd", "customer"=>nil, "description"=>nil, "destination"=>"acct_17BAkdB2ypFVGqS2", "dispute"=>nil, "failure_code"=>nil, "failure_message"=>nil, "fraud_details"=>{}, "invoice"=>nil, "livemode"=>false, "metadata"=>{}, "paid"=>true, "receipt_email"=>nil, "receipt_number"=>nil, "refunded"=>false, "refunds"=>{"object"=>"list", "data"=>nil, "has_more"=>false, "total_count"=>0, "url"=>"/v1/charges/ch_17SbmPGRxo80gNumukbaoHrB/refunds"}, "shipping"=>nil, "source"=>{"id"=>"card_17SbmKGRxo80gNumk8gnMXL1", "object"=>"card", "address_city"=>nil, "address_country"=>nil, "address_line1"=>nil, "address_line1_check"=>nil, "address_line2"=>nil, "address_state"=>nil, "address_zip"=>nil, "address_zip_check"=>nil, "brand"=>"Visa", "country"=>"US", "customer"=>nil, "cvc_check"=>"pass", "dynamic_last4"=>nil, "exp_month"=>11, "exp_year"=>2016, "fingerprint"=>"s8DNQYfXI7bbYeRi", "funding"=>"credit", "last4"=>"4242", "metadata"=>{}, "name"=>nil, "tokenization_method"=>nil}, "statement_descriptor"=>nil, "status"=>"succeeded", "transfer"=>"tr_17SbmPGRxo80gNumgIzYFWYP"}}, "livemode"=>false, "pending_webhooks"=>1, "request"=>"req_7i1qYloeVk3PYk", "type"=>"charge.succeeded"}}
15:42:35 log.1    | Geokit is using the domain: ngrok.io
15:42:35 log.1    | AccountUpdated event: evt_17SbmQGRxo80gNumHix3Ut6y
15:42:35 log.1    | StripeEvent event: charge.succeeded
15:42:35 log.1    | got here
15:42:35 log.1    | Completed 200 OK in 217ms (ActiveRecord: 0.0ms)

I am using ngrok to expose my local server to Stripe for the webhook endpoints. But, I don't believe that this is causing any issue. Could it be due to this being a "managed account"? Does that matter?

Thanks,
Patrick

EXTREMELY hard to test with rspec

I feel like I MUST be missing something. This should be MUCH easier but Ive spent hours on this.

Using the dashboard (not test webhooks as the README states) I created a bunch of actions. I used RequestBin to capture the raw response and saved them locally to fixture files.

I then created a spec to post the data to the server to test the hooks. It simply doesnt work. I either get 500 errors due to Hash Indifference or the events are processed as strings.

Here is are some examples:

 require 'spec_helper'

  describe 'StripeEvents receiving web hooks from Stripe' do
    it "should not allow creation of an application" do
      json = JSON.parse(File.read("spec/fixtures/stripe/customer.subscription.deleted.json"))

      @pro = create(:professional, active: true, stripe_customer_id: '123123123')
      json["data"]["object"]["customer"] = '123123123'

      post( '/stripe', json, :content_type => 'application/json')

      response.status.should eq(200)
      expect(@pro.active).to eq false
    end
  end

which returns an Unauthorized. I've also tried doing different variations of these:

 if Rails.env.development? || Rails.env.test?
  # StripeEvent.event_retriever = lambda { |params| params }
  # StripeEvent.event_retriever = lambda {|params|   Stripe::Event.construct_from(params) }
end

I simply need a way to create specs for every possible hook I have wired up for testing. Ideally I dont need to actually perform every one of those actions on Stripe to get the response.

How can I stub and test the various event types without going crazy?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.