stripe_event 0.6.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 92e8ad6c1ab515d871b8aba7a4c6d15ecc0dd967
4
- data.tar.gz: 027dd82b3834d95bb4255e43caebaf56d7b3082f
3
+ metadata.gz: 6d62c1dcd19a7c4efa5ac0a32ce6e0d01d65fb54
4
+ data.tar.gz: 98148c9f17cdaab997eb100e04ef4aaa7b417e4c
5
5
  SHA512:
6
- metadata.gz: 8c5848d09cc4d4d14ac5e4f244444c3a2d987712055d2be058adc6d36e413f2b099c84d275a191b20a6ee64173b8f2015770dc0efb33ecf2df376bcb1030e53a
7
- data.tar.gz: c13e2087d74ec7fe77131d527d990ca0273cffa12c018b36279765e86c1ee941b7101ef4553e7af53304e49963cad4d22e38c1ba5feaeab9145a69b9cc855405
6
+ metadata.gz: 907c5f1b0888fcf5093cb5853a916d4ca280cebb1b6eace62d4926f145262f8c8deb13eff2d617b4c61837348c7af635271a52e7e6387809999952a2036d1143
7
+ data.tar.gz: 256549e22835b44d8d933c46c636eb04cabf8d549899e535a4227b0befa53ec56687a6281308737710a3acdb4f42b79a2f9944977974f18b79fa092df1943648
data/.travis.yml CHANGED
@@ -2,9 +2,9 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.2
4
4
  - 1.9.3
5
- - rbx-19mode
6
- - jruby-19mode
7
5
  - 2.0.0
6
+ - jruby-19mode
7
+ - jruby-head
8
8
  gemfile:
9
9
  - gemfiles/rails3.1.gemfile
10
10
  - gemfiles/rails3.2.gemfile
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ### 1.0.0 (December 19, 2013)
2
+ * Internally namespace dispatched events to avoid maintaining a list of all possible event types.
3
+ * Subscribe to all event types with `StripeEvent.all` instead of `StripeEvent.subscribe`.
4
+ * Remove ability to subscribe to many event types with once call to `StripeEvent.subscribe`.
5
+ * Subscribers can be an object that responds to #call.
6
+ * Allow subscriber-generated `Stripe::StripeError`'s to bubble up. Thank you to [adamonduty](https://github.com/adamonduty) for the [patch](https://github.com/integrallis/stripe_event/pull/26).
7
+ * Only depend on `stripe` and `activesupport` gems.
8
+ * Add `rails` as a development dependency.
9
+ * Only `require 'stripe_event/engine'` if `Rails` constant exists to allow StripeEvent to be used outside of a Rails application.
10
+
1
11
  ### 0.6.1 (August 19, 2013)
2
12
  * Update event type list
3
13
  * Update test gemfiles
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # StripeEvent [![Build Status](https://secure.travis-ci.org/integrallis/stripe_event.png?branch=master)](http://travis-ci.org/integrallis/stripe_event) [![Dependency Status](https://gemnasium.com/integrallis/stripe_event.png)](https://gemnasium.com/integrallis/stripe_event) [![Gem Version](https://badge.fury.io/rb/stripe_event.png)](http://badge.fury.io/rb/stripe_event)
2
2
 
3
- StripeEvent is built on the [ActiveSupport::Notifications API](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html). Incoming webhook requests are authenticated by retrieving the [event object](https://stripe.com/docs/api?lang=ruby#event_object) from Stripe[[1]](https://answers.stripe.com/questions/what-is-the-recommended-way-to-authenticate-a-webhook-callback). Define subscriber blocks to handle one, many, or all event types.
3
+ StripeEvent is built on the [ActiveSupport::Notifications API](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html). Incoming webhook requests are authenticated by [retrieving the event object](https://stripe.com/docs/api?lang=ruby#retrieve_event) from Stripe. Define subscribers to handle a single event type or all event types. Subscribers can be a block or any object that responds to `#call`.
4
4
 
5
5
  ## Install
6
6
 
@@ -23,19 +23,41 @@ Stripe.api_key = ENV['STRIPE_API_KEY'] # Set your api key
23
23
  StripeEvent.setup do
24
24
  subscribe 'charge.failed' do |event|
25
25
  # Define subscriber behavior based on the event object
26
- event.class #=> Stripe::Event
27
- event.type #=> "charge.failed"
28
- event.data #=> { ... }
26
+ event.class #=> Stripe::Event
27
+ event.type #=> "charge.failed"
28
+ event.data.object #=> #<Stripe::Charge:0x3fcb34c115f8>
29
29
  end
30
30
 
31
- subscribe 'customer.created', 'customer.updated' do |event|
32
- # Handle multiple event types
31
+ all do |event|
32
+ # Handle all event types - logging, etc.
33
33
  end
34
+ end
34
35
 
35
- subscribe do |event|
36
- # Handle all event types - logging, etc.
36
+ # Subscriber objects that respond to #call
37
+
38
+ class CustomerCreated
39
+ def call(event)
40
+ # Event handling
41
+ end
42
+ end
43
+
44
+ class BillingEventLogger
45
+ def initialize(logger = nil)
46
+ @logger = logger || begin
47
+ require 'logger'
48
+ Logger.new($stdout)
49
+ end
50
+ end
51
+
52
+ def call(event)
53
+ @logger.info "BILLING-EVENT: #{event.type} #{event.id}"
37
54
  end
38
55
  end
56
+
57
+ StripeEvent.setup do
58
+ all BillingEventLogger.new(Rails.logger)
59
+ subscribe 'customer.created', CustomerCreated.new
60
+ end
39
61
  ```
40
62
 
41
63
  ## Configuration
@@ -44,28 +66,81 @@ If you have built an application that has multiple Stripe accounts--say, each of
44
66
 
45
67
  ```ruby
46
68
  StripeEvent.event_retriever = lambda do |params|
47
- secret_key = Account.find_by_stripe_user_id(params[:user_id]).secret_key
48
- Stripe::Event.retrieve(params[:id], secret_key)
69
+ api_key = Account.find_by!(stripe_user_id: params[:user_id]).api_key
70
+ Stripe::Event.retrieve(params[:id], api_key)
71
+ end
72
+
73
+ # Or use any object that responds to #call
74
+
75
+ class EventRetriever
76
+ def call(params)
77
+ api_key = retrieve_api_key(params[:user_id])
78
+ Stripe::Event.retrieve(params[:id], api_key)
79
+ end
80
+
81
+ def retrieve_api_key(stripe_user_id)
82
+ Account.find_by!(stripe_user_id: stripe_user_id).api_key
83
+ rescue ActiveRecord::RecordNotFound
84
+ # whoops something went wrong - error handling
85
+ end
49
86
  end
87
+
88
+ StripeEvent.event_retriever = EventRetriever.new
50
89
  ```
51
90
 
52
- During development it may be useful to skip retrieving the event from Stripe, and deal with the params hash directly. Just remember that the data has not been authenticated.
91
+ ## Without Rails
92
+
93
+ StripeEvent can be used outside of Rails applications as well. Here is a basic Sinatra implementation:
53
94
 
54
95
  ```ruby
55
- StripeEvent.event_retriever = lambda { |params| params }
96
+ require 'json'
97
+ require 'sinatra'
98
+ require 'stripe_event'
99
+
100
+ Stripe.api_key = ENV['STRIPE_API_KEY']
101
+
102
+ StripeEvent.subscribe 'charge.failed' do |event|
103
+ # Look ma, no Rails!
104
+ end
105
+
106
+ post '/_billing_events' do
107
+ data = JSON.parse(request.body.read, symbolize_names: true)
108
+ StripeEvent.instrument(data)
109
+ 200
110
+ end
56
111
  ```
57
112
 
58
- ### Register webhook url with Stripe
113
+ ## Testing
114
+
115
+ 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 request used to authenticate the webhook request. Tools like [Webmock](https://github.com/bblimke/webmock) and [VCR](https://github.com/vcr/vcr) work well. [RequestBin](http://requestb.in/) is great for collecting the payloads. For exploratory phases of development, [UltraHook](http://www.ultrahook.com/) and other tools can forward webhook requests directly to localhost. You can check out [test-hooks](https://github.com/invisiblefunnel/test-hooks), and example Rails application to see how to test StripeEvent subscribers with RSpec request specs and Webmock. A quick look:
116
+
117
+ ```ruby
118
+ # spec/requests/billing_events_spec.rb
119
+ require 'spec_helper'
59
120
 
60
- ![Setup webhook url](https://raw.github.com/integrallis/stripe_event/master/dashboard-webhook.png "webhook setup")
121
+ describe "Billing Events" do
122
+ def stub_event(fixture_id, status = 200)
123
+ stub_request(:get, "https://api.stripe.com/v1/events/#{fixture_id}").
124
+ to_return(status: status, body: File.read("spec/support/fixtures/#{fixture_id}.json"))
125
+ end
61
126
 
62
- ### Examples
127
+ describe "customer.created" do
128
+ before do
129
+ stub_event 'evt_customer_created'
130
+ end
63
131
 
64
- The [RailsApps](https://github.com/RailsApps) project by Daniel Kehoe has released an [example Rails 3.2 app](https://github.com/RailsApps/rails-stripe-membership-saas) with recurring billing using Stripe. The application uses StripeEvent to handle `customer.subscription.deleted` events.
132
+ it "is successful" do
133
+ post '/_billing_events', id: 'evt_customer_created'
134
+ expect(response.code).to eq "200"
135
+ # Additional expectations...
136
+ end
137
+ end
138
+ end
139
+ ```
65
140
 
66
141
  ### Note: 'Test Webhooks' Button on Stripe Dashboard
67
142
 
68
- 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 Dashboard to create test payments, customers, etc.
143
+ 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.
69
144
 
70
145
  ### License
71
146
 
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'rspec/core/rake_task'
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
8
  if ENV['CI']
9
- task :default => :spec
9
+ task default: :spec
10
10
  else
11
11
  require 'appraisal'
12
12
  task :default do
@@ -3,7 +3,7 @@ module StripeEvent
3
3
  def event
4
4
  StripeEvent.instrument(params)
5
5
  head :ok
6
- rescue Stripe::StripeError
6
+ rescue StripeEvent::UnauthorizedError
7
7
  head :unauthorized
8
8
  end
9
9
  end
data/config/routes.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  StripeEvent::Engine.routes.draw do
2
- root :to => 'webhook#event', :via => :post
2
+ root to: 'webhook#event', via: :post
3
3
  end
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- stripe_event (0.6.1)
5
- rails (>= 3.1)
4
+ stripe_event (1.0.0)
5
+ activesupport (>= 3.1)
6
6
  stripe (~> 1.6)
7
7
 
8
8
  GEM
@@ -44,17 +44,17 @@ GEM
44
44
  builder (3.0.4)
45
45
  crack (0.4.1)
46
46
  safe_yaml (~> 0.9.0)
47
- diff-lcs (1.2.4)
47
+ diff-lcs (1.2.5)
48
48
  erubis (2.7.0)
49
49
  hike (1.2.3)
50
- i18n (0.6.5)
51
- json (1.8.0)
50
+ i18n (0.6.9)
51
+ json (1.8.1)
52
52
  mail (2.4.4)
53
53
  i18n (>= 0.4.0)
54
54
  mime-types (~> 1.16)
55
55
  treetop (~> 1.4.8)
56
- mime-types (1.24)
57
- multi_json (1.7.9)
56
+ mime-types (1.25.1)
57
+ multi_json (1.8.2)
58
58
  polyglot (0.3.3)
59
59
  rack (1.3.10)
60
60
  rack-cache (1.2)
@@ -85,10 +85,10 @@ GEM
85
85
  json (~> 1.4)
86
86
  rest-client (1.6.7)
87
87
  mime-types (>= 1.16)
88
- rspec-core (2.14.5)
89
- rspec-expectations (2.14.2)
88
+ rspec-core (2.14.7)
89
+ rspec-expectations (2.14.4)
90
90
  diff-lcs (>= 1.1.3, < 2.0)
91
- rspec-mocks (2.14.3)
91
+ rspec-mocks (2.14.4)
92
92
  rspec-rails (2.14.0)
93
93
  actionpack (>= 3.0)
94
94
  activesupport (>= 3.0)
@@ -96,12 +96,13 @@ GEM
96
96
  rspec-core (~> 2.14.0)
97
97
  rspec-expectations (~> 2.14.0)
98
98
  rspec-mocks (~> 2.14.0)
99
- safe_yaml (0.9.5)
99
+ safe_yaml (0.9.7)
100
100
  sprockets (2.0.4)
101
101
  hike (~> 1.2)
102
102
  rack (~> 1.0)
103
103
  tilt (~> 1.1, != 1.3.0)
104
- stripe (1.8.5)
104
+ stripe (1.9.9)
105
+ mime-types (~> 1.25)
105
106
  multi_json (>= 1.0.4, < 2)
106
107
  rest-client (~> 1.4)
107
108
  thor (0.14.6)
@@ -109,8 +110,8 @@ GEM
109
110
  treetop (1.4.15)
110
111
  polyglot
111
112
  polyglot (>= 0.3.1)
112
- tzinfo (0.3.37)
113
- webmock (1.13.0)
113
+ tzinfo (0.3.38)
114
+ webmock (1.16.0)
114
115
  addressable (>= 2.2.7)
115
116
  crack (>= 0.3.2)
116
117
 
@@ -1,19 +1,19 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- stripe_event (0.6.1)
5
- rails (>= 3.1)
4
+ stripe_event (1.0.0)
5
+ activesupport (>= 3.1)
6
6
  stripe (~> 1.6)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- actionmailer (3.2.14)
12
- actionpack (= 3.2.14)
11
+ actionmailer (3.2.16)
12
+ actionpack (= 3.2.16)
13
13
  mail (~> 2.5.4)
14
- actionpack (3.2.14)
15
- activemodel (= 3.2.14)
16
- activesupport (= 3.2.14)
14
+ actionpack (3.2.16)
15
+ activemodel (= 3.2.16)
16
+ activesupport (= 3.2.16)
17
17
  builder (~> 3.0.0)
18
18
  erubis (~> 2.7.0)
19
19
  journey (~> 1.0.4)
@@ -21,39 +21,39 @@ GEM
21
21
  rack-cache (~> 1.2)
22
22
  rack-test (~> 0.6.1)
23
23
  sprockets (~> 2.2.1)
24
- activemodel (3.2.14)
25
- activesupport (= 3.2.14)
24
+ activemodel (3.2.16)
25
+ activesupport (= 3.2.16)
26
26
  builder (~> 3.0.0)
27
- activerecord (3.2.14)
28
- activemodel (= 3.2.14)
29
- activesupport (= 3.2.14)
27
+ activerecord (3.2.16)
28
+ activemodel (= 3.2.16)
29
+ activesupport (= 3.2.16)
30
30
  arel (~> 3.0.2)
31
31
  tzinfo (~> 0.3.29)
32
- activeresource (3.2.14)
33
- activemodel (= 3.2.14)
34
- activesupport (= 3.2.14)
35
- activesupport (3.2.14)
32
+ activeresource (3.2.16)
33
+ activemodel (= 3.2.16)
34
+ activesupport (= 3.2.16)
35
+ activesupport (3.2.16)
36
36
  i18n (~> 0.6, >= 0.6.4)
37
37
  multi_json (~> 1.0)
38
38
  addressable (2.3.5)
39
39
  appraisal (0.5.2)
40
40
  bundler
41
41
  rake
42
- arel (3.0.2)
42
+ arel (3.0.3)
43
43
  builder (3.0.4)
44
44
  crack (0.4.1)
45
45
  safe_yaml (~> 0.9.0)
46
- diff-lcs (1.2.4)
46
+ diff-lcs (1.2.5)
47
47
  erubis (2.7.0)
48
48
  hike (1.2.3)
49
- i18n (0.6.5)
49
+ i18n (0.6.9)
50
50
  journey (1.0.4)
51
- json (1.8.0)
51
+ json (1.8.1)
52
52
  mail (2.5.4)
53
53
  mime-types (~> 1.16)
54
54
  treetop (~> 1.4.8)
55
- mime-types (1.24)
56
- multi_json (1.7.9)
55
+ mime-types (1.25.1)
56
+ multi_json (1.8.2)
57
57
  polyglot (0.3.3)
58
58
  rack (1.4.5)
59
59
  rack-cache (1.2)
@@ -62,17 +62,17 @@ GEM
62
62
  rack
63
63
  rack-test (0.6.2)
64
64
  rack (>= 1.0)
65
- rails (3.2.14)
66
- actionmailer (= 3.2.14)
67
- actionpack (= 3.2.14)
68
- activerecord (= 3.2.14)
69
- activeresource (= 3.2.14)
70
- activesupport (= 3.2.14)
65
+ rails (3.2.16)
66
+ actionmailer (= 3.2.16)
67
+ actionpack (= 3.2.16)
68
+ activerecord (= 3.2.16)
69
+ activeresource (= 3.2.16)
70
+ activesupport (= 3.2.16)
71
71
  bundler (~> 1.0)
72
- railties (= 3.2.14)
73
- railties (3.2.14)
74
- actionpack (= 3.2.14)
75
- activesupport (= 3.2.14)
72
+ railties (= 3.2.16)
73
+ railties (3.2.16)
74
+ actionpack (= 3.2.16)
75
+ activesupport (= 3.2.16)
76
76
  rack-ssl (~> 1.3.2)
77
77
  rake (>= 0.8.7)
78
78
  rdoc (~> 3.4)
@@ -82,10 +82,10 @@ GEM
82
82
  json (~> 1.4)
83
83
  rest-client (1.6.7)
84
84
  mime-types (>= 1.16)
85
- rspec-core (2.14.5)
86
- rspec-expectations (2.14.2)
85
+ rspec-core (2.14.7)
86
+ rspec-expectations (2.14.4)
87
87
  diff-lcs (>= 1.1.3, < 2.0)
88
- rspec-mocks (2.14.3)
88
+ rspec-mocks (2.14.4)
89
89
  rspec-rails (2.14.0)
90
90
  actionpack (>= 3.0)
91
91
  activesupport (>= 3.0)
@@ -93,13 +93,14 @@ GEM
93
93
  rspec-core (~> 2.14.0)
94
94
  rspec-expectations (~> 2.14.0)
95
95
  rspec-mocks (~> 2.14.0)
96
- safe_yaml (0.9.5)
96
+ safe_yaml (0.9.7)
97
97
  sprockets (2.2.2)
98
98
  hike (~> 1.2)
99
99
  multi_json (~> 1.0)
100
100
  rack (~> 1.0)
101
101
  tilt (~> 1.1, != 1.3.0)
102
- stripe (1.8.5)
102
+ stripe (1.9.9)
103
+ mime-types (~> 1.25)
103
104
  multi_json (>= 1.0.4, < 2)
104
105
  rest-client (~> 1.4)
105
106
  thor (0.18.1)
@@ -107,8 +108,8 @@ GEM
107
108
  treetop (1.4.15)
108
109
  polyglot
109
110
  polyglot (>= 0.3.1)
110
- tzinfo (0.3.37)
111
- webmock (1.13.0)
111
+ tzinfo (0.3.38)
112
+ webmock (1.16.0)
112
113
  addressable (>= 2.2.7)
113
114
  crack (>= 0.3.2)
114
115
 
@@ -1,32 +1,32 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- stripe_event (0.6.1)
5
- rails (>= 3.1)
4
+ stripe_event (1.0.0)
5
+ activesupport (>= 3.1)
6
6
  stripe (~> 1.6)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- actionmailer (4.0.0)
12
- actionpack (= 4.0.0)
13
- mail (~> 2.5.3)
14
- actionpack (4.0.0)
15
- activesupport (= 4.0.0)
11
+ actionmailer (4.0.2)
12
+ actionpack (= 4.0.2)
13
+ mail (~> 2.5.4)
14
+ actionpack (4.0.2)
15
+ activesupport (= 4.0.2)
16
16
  builder (~> 3.1.0)
17
17
  erubis (~> 2.7.0)
18
18
  rack (~> 1.5.2)
19
19
  rack-test (~> 0.6.2)
20
- activemodel (4.0.0)
21
- activesupport (= 4.0.0)
20
+ activemodel (4.0.2)
21
+ activesupport (= 4.0.2)
22
22
  builder (~> 3.1.0)
23
- activerecord (4.0.0)
24
- activemodel (= 4.0.0)
23
+ activerecord (4.0.2)
24
+ activemodel (= 4.0.2)
25
25
  activerecord-deprecated_finders (~> 1.0.2)
26
- activesupport (= 4.0.0)
26
+ activesupport (= 4.0.2)
27
27
  arel (~> 4.0.0)
28
28
  activerecord-deprecated_finders (1.0.3)
29
- activesupport (4.0.0)
29
+ activesupport (4.0.2)
30
30
  i18n (~> 0.6, >= 0.6.4)
31
31
  minitest (~> 4.2)
32
32
  multi_json (~> 1.3)
@@ -36,45 +36,45 @@ GEM
36
36
  appraisal (0.5.2)
37
37
  bundler
38
38
  rake
39
- arel (4.0.0)
40
- atomic (1.1.13)
39
+ arel (4.0.1)
40
+ atomic (1.1.14)
41
41
  builder (3.1.4)
42
42
  crack (0.4.1)
43
43
  safe_yaml (~> 0.9.0)
44
- diff-lcs (1.2.4)
44
+ diff-lcs (1.2.5)
45
45
  erubis (2.7.0)
46
46
  hike (1.2.3)
47
- i18n (0.6.5)
47
+ i18n (0.6.9)
48
48
  mail (2.5.4)
49
49
  mime-types (~> 1.16)
50
50
  treetop (~> 1.4.8)
51
- mime-types (1.24)
51
+ mime-types (1.25.1)
52
52
  minitest (4.7.5)
53
- multi_json (1.7.9)
53
+ multi_json (1.8.2)
54
54
  polyglot (0.3.3)
55
55
  rack (1.5.2)
56
56
  rack-test (0.6.2)
57
57
  rack (>= 1.0)
58
- rails (4.0.0)
59
- actionmailer (= 4.0.0)
60
- actionpack (= 4.0.0)
61
- activerecord (= 4.0.0)
62
- activesupport (= 4.0.0)
58
+ rails (4.0.2)
59
+ actionmailer (= 4.0.2)
60
+ actionpack (= 4.0.2)
61
+ activerecord (= 4.0.2)
62
+ activesupport (= 4.0.2)
63
63
  bundler (>= 1.3.0, < 2.0)
64
- railties (= 4.0.0)
64
+ railties (= 4.0.2)
65
65
  sprockets-rails (~> 2.0.0)
66
- railties (4.0.0)
67
- actionpack (= 4.0.0)
68
- activesupport (= 4.0.0)
66
+ railties (4.0.2)
67
+ actionpack (= 4.0.2)
68
+ activesupport (= 4.0.2)
69
69
  rake (>= 0.8.7)
70
70
  thor (>= 0.18.1, < 2.0)
71
71
  rake (10.1.0)
72
72
  rest-client (1.6.7)
73
73
  mime-types (>= 1.16)
74
- rspec-core (2.14.5)
75
- rspec-expectations (2.14.2)
74
+ rspec-core (2.14.7)
75
+ rspec-expectations (2.14.4)
76
76
  diff-lcs (>= 1.1.3, < 2.0)
77
- rspec-mocks (2.14.3)
77
+ rspec-mocks (2.14.4)
78
78
  rspec-rails (2.14.0)
79
79
  actionpack (>= 3.0)
80
80
  activesupport (>= 3.0)
@@ -82,28 +82,29 @@ GEM
82
82
  rspec-core (~> 2.14.0)
83
83
  rspec-expectations (~> 2.14.0)
84
84
  rspec-mocks (~> 2.14.0)
85
- safe_yaml (0.9.5)
86
- sprockets (2.10.0)
85
+ safe_yaml (0.9.7)
86
+ sprockets (2.10.1)
87
87
  hike (~> 1.2)
88
88
  multi_json (~> 1.0)
89
89
  rack (~> 1.0)
90
90
  tilt (~> 1.1, != 1.3.0)
91
- sprockets-rails (2.0.0)
91
+ sprockets-rails (2.0.1)
92
92
  actionpack (>= 3.0)
93
93
  activesupport (>= 3.0)
94
94
  sprockets (~> 2.8)
95
- stripe (1.8.5)
95
+ stripe (1.9.9)
96
+ mime-types (~> 1.25)
96
97
  multi_json (>= 1.0.4, < 2)
97
98
  rest-client (~> 1.4)
98
99
  thor (0.18.1)
99
- thread_safe (0.1.2)
100
+ thread_safe (0.1.3)
100
101
  atomic
101
102
  tilt (1.4.1)
102
103
  treetop (1.4.15)
103
104
  polyglot
104
105
  polyglot (>= 0.3.1)
105
- tzinfo (0.3.37)
106
- webmock (1.13.0)
106
+ tzinfo (0.3.38)
107
+ webmock (1.16.0)
107
108
  addressable (>= 2.2.7)
108
109
  crack (>= 0.3.2)
109
110
 
data/lib/stripe_event.rb CHANGED
@@ -1,77 +1,60 @@
1
- require "set"
1
+ require "active_support/notifications"
2
2
  require "stripe"
3
- require "stripe_event/engine"
3
+ require "stripe_event/engine" if defined?(Rails)
4
4
 
5
5
  module StripeEvent
6
6
  class << self
7
- attr_accessor :backend, :event_retriever
7
+ attr_accessor :adapter, :backend, :event_retriever, :namespace
8
8
 
9
9
  def setup(&block)
10
10
  instance_eval(&block)
11
11
  end
12
12
 
13
13
  def instrument(params)
14
- event = event_retriever.call(params)
15
- publish(event)
14
+ begin
15
+ event = event_retriever.call(params)
16
+ rescue Stripe::StripeError => e
17
+ raise UnauthorizedError.new(e)
18
+ end
19
+
20
+ backend.instrument namespace.call(event[:type]), event
16
21
  end
17
22
 
18
- def publish(event)
19
- backend.publish(event[:type], event)
23
+ def subscribe(name, callable = Proc.new)
24
+ backend.subscribe namespace.to_regexp(name), adapter.call(callable)
20
25
  end
21
26
 
22
- def subscribe(*names, &block)
23
- pattern = Regexp.union(names.empty? ? TYPE_LIST.to_a : names)
27
+ def all(callable = Proc.new)
28
+ subscribe nil, callable
29
+ end
30
+ end
24
31
 
25
- backend.subscribe(pattern) do |*args|
26
- payload = args.last
27
- block.call(payload)
28
- end
32
+ class Namespace < Struct.new(:value, :delimiter)
33
+ def call(name = nil)
34
+ name ? "#{value}#{delimiter}#{name}" : value
35
+ end
36
+
37
+ def to_regexp(name = nil)
38
+ %r{^#{Regexp.escape call(name)}}
29
39
  end
30
40
  end
31
41
 
42
+ class NotificationAdapter < Struct.new(:subscriber)
43
+ def self.call(callable)
44
+ new(callable)
45
+ end
46
+
47
+ def call(*args)
48
+ payload = args.last
49
+ subscriber.call(payload)
50
+ end
51
+ end
52
+
53
+ class Error < StandardError; end
54
+ class UnauthorizedError < Error; end
55
+
56
+ self.adapter = NotificationAdapter
32
57
  self.backend = ActiveSupport::Notifications
33
58
  self.event_retriever = lambda { |params| Stripe::Event.retrieve(params[:id]) }
34
-
35
- TYPE_LIST = Set[
36
- 'account.updated',
37
- 'account.application.deauthorized',
38
- 'balance.available',
39
- 'charge.succeeded',
40
- 'charge.failed',
41
- 'charge.refunded',
42
- 'charge.captured',
43
- 'charge.dispute.created',
44
- 'charge.dispute.updated',
45
- 'charge.dispute.closed',
46
- 'customer.created',
47
- 'customer.updated',
48
- 'customer.deleted',
49
- 'customer.card.created',
50
- 'customer.card.updated',
51
- 'customer.card.deleted',
52
- 'customer.subscription.created',
53
- 'customer.subscription.updated',
54
- 'customer.subscription.deleted',
55
- 'customer.subscription.trial_will_end',
56
- 'customer.discount.created',
57
- 'customer.discount.updated',
58
- 'customer.discount.deleted',
59
- 'invoice.created',
60
- 'invoice.updated',
61
- 'invoice.payment_succeeded',
62
- 'invoice.payment_failed',
63
- 'invoiceitem.created',
64
- 'invoiceitem.updated',
65
- 'invoiceitem.deleted',
66
- 'plan.created',
67
- 'plan.updated',
68
- 'plan.deleted',
69
- 'coupon.created',
70
- 'coupon.deleted',
71
- 'transfer.created',
72
- 'transfer.updated',
73
- 'transfer.paid',
74
- 'transfer.failed',
75
- 'ping'
76
- ].freeze
59
+ self.namespace = Namespace.new("stripe_event", ".")
77
60
  end
@@ -1,3 +1,3 @@
1
1
  module StripeEvent
2
- VERSION = "0.6.1"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,28 +1,42 @@
1
+ require 'rails_helper'
1
2
  require 'spec_helper'
2
3
 
3
4
  describe StripeEvent::WebhookController do
4
- def event_post(params)
5
- post :event, params.merge(:use_route => :stripe_event)
5
+ def stub_event(identifier, status = 200)
6
+ stub_request(:get, "https://api.stripe.com/v1/events/#{identifier}").
7
+ to_return(status: status, body: File.read("spec/support/fixtures/#{identifier}.json"))
8
+ end
9
+
10
+ def webhook(params)
11
+ post :event, params.merge(use_route: :stripe_event)
6
12
  end
7
13
 
8
14
  it "succeeds with valid event data" do
15
+ count = 0
16
+ StripeEvent.subscribe('charge.succeeded') { |evt| count += 1 }
9
17
  stub_event('evt_charge_succeeded')
10
18
 
11
- event_post :id => 'evt_charge_succeeded'
12
- expect(response).to be_success
19
+ webhook id: 'evt_charge_succeeded'
20
+
21
+ expect(response.code).to eq '200'
22
+ expect(count).to eq 1
13
23
  end
14
24
 
15
25
  it "denies access with invalid event data" do
26
+ count = 0
27
+ StripeEvent.subscribe('charge.succeeded') { |evt| count += 1 }
16
28
  stub_event('evt_invalid_id', 404)
17
29
 
18
- event_post :id => 'evt_invalid_id'
30
+ webhook id: 'evt_invalid_id'
31
+
19
32
  expect(response.code).to eq '401'
33
+ expect(count).to eq 0
20
34
  end
21
35
 
22
- it "succeeds with a custom event retriever" do
23
- StripeEvent.event_retriever = Proc.new { |params| params }
36
+ it "ensures user-generated Stripe exceptions pass through" do
37
+ StripeEvent.subscribe('charge.succeeded') { |evt| raise Stripe::StripeError, "testing" }
38
+ stub_event('evt_charge_succeeded')
24
39
 
25
- event_post :id => '1'
26
- expect(response).to be_success
40
+ expect { webhook id: 'evt_charge_succeeded' }.to raise_error(Stripe::StripeError, /testing/)
27
41
  end
28
42
  end
@@ -1,45 +1,101 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe StripeEvent do
4
- let(:event_type) { StripeEvent::TYPE_LIST.sample }
4
+ let(:events) { [] }
5
+ let(:subscriber) { ->(evt){ events << evt } }
6
+ let(:charge_succeeded) { double('charge succeeded') }
7
+ let(:charge_failed) { double('charge failed') }
5
8
 
6
- it "backend defaults to AS::Notifications" do
7
- expect(described_class.backend).to eq ActiveSupport::Notifications
8
- end
9
+ describe "subscribing to a specific event type" do
10
+ before do
11
+ expect(charge_succeeded).to receive(:[]).with(:type).and_return('charge.succeeded')
12
+ expect(Stripe::Event).to receive(:retrieve).with('evt_charge_succeeded').and_return(charge_succeeded)
13
+ end
9
14
 
10
- it "registers a subscriber" do
11
- subscriber = described_class.subscribe(event_type) { |e| }
12
- subscribers = subscribers_for_type(event_type)
13
- expect(subscribers).to eq [subscriber]
14
- end
15
+ context "with a block subscriber" do
16
+ it "calls the subscriber with the retrieved event" do
17
+ StripeEvent.subscribe('charge.succeeded', &subscriber)
18
+
19
+ StripeEvent.instrument(id: 'evt_charge_succeeded', type: 'charge.succeeded')
15
20
 
16
- it "registers subscribers within a parent block" do
17
- described_class.setup do
18
- subscribe('invoice.payment_succeeded') { |e| }
21
+ expect(events).to eq [charge_succeeded]
22
+ end
23
+ end
24
+
25
+ context "with a subscriber that responds to #call" do
26
+ it "calls the subscriber with the retrieved event" do
27
+ StripeEvent.subscribe('charge.succeeded', subscriber)
28
+
29
+ StripeEvent.instrument(id: 'evt_charge_succeeded', type: 'charge.succeeded')
30
+
31
+ expect(events).to eq [charge_succeeded]
32
+ end
19
33
  end
20
- subscribers = subscribers_for_type('invoice.payment_succeeded')
21
- expect(subscribers).to_not be_empty
22
34
  end
23
35
 
24
- it "passes only the event object to the subscribed block" do
25
- event = { :type => event_type }
36
+ describe "subscribing to all event types" do
37
+ before do
38
+ expect(charge_succeeded).to receive(:[]).with(:type).and_return('charge.succeeded')
39
+ expect(Stripe::Event).to receive(:retrieve).with('evt_charge_succeeded').and_return(charge_succeeded)
40
+
41
+ expect(charge_failed).to receive(:[]).with(:type).and_return('charge.failed')
42
+ expect(Stripe::Event).to receive(:retrieve).with('evt_charge_failed').and_return(charge_failed)
43
+ end
44
+
45
+ context "with a block subscriber" do
46
+ it "calls the subscriber with all retrieved events" do
47
+ StripeEvent.all(&subscriber)
48
+
49
+ StripeEvent.instrument(id: 'evt_charge_succeeded', type: 'charge.succeeded')
50
+ StripeEvent.instrument(id: 'evt_charge_failed', type: 'charge.failed')
26
51
 
27
- expect { |block|
28
- described_class.subscribe(event_type, &block)
29
- described_class.publish(event)
30
- }.to yield_with_args(event)
52
+ expect(events).to eq [charge_succeeded, charge_failed]
53
+ end
54
+ end
55
+
56
+ context "with a subscriber that responds to #call" do
57
+ it "calls the subscriber with all retrieved events" do
58
+ StripeEvent.all(subscriber)
59
+
60
+ StripeEvent.instrument(id: 'evt_charge_succeeded', type: 'charge.succeeded')
61
+ StripeEvent.instrument(id: 'evt_charge_failed', type: 'charge.failed')
62
+
63
+ expect(events).to eq [charge_succeeded, charge_failed]
64
+ end
65
+ end
31
66
  end
32
67
 
33
- it "uses Stripe::Event as the default event retriever" do
34
- Stripe::Event.should_receive(:retrieve).with('1')
35
- described_class.event_retriever.call(:id => '1')
68
+ describe StripeEvent::NotificationAdapter do
69
+ let(:adapter) { StripeEvent.adapter }
70
+
71
+ it "calls the subscriber with the last argument" do
72
+ expect(subscriber).to receive(:call).with(:last)
73
+
74
+ adapter.call(subscriber).call(:first, :last)
75
+ end
36
76
  end
37
77
 
38
- it "allows setting an event_retriever" do
39
- params = { :id => '1' }
78
+ describe StripeEvent::Namespace do
79
+ let(:namespace) { StripeEvent.namespace }
80
+
81
+ describe "#call" do
82
+ it "prepends the namespace to a given string" do
83
+ expect(namespace.call('foo.bar')).to eq 'stripe_event.foo.bar'
84
+ end
85
+
86
+ it "returns the namespace given no arguments" do
87
+ expect(namespace.call).to eq 'stripe_event'
88
+ end
89
+ end
90
+
91
+ describe "#to_regexp" do
92
+ it "matches namespaced strings" do
93
+ expect(namespace.to_regexp('foo.bar')).to match namespace.call('foo.bar')
94
+ end
40
95
 
41
- described_class.event_retriever = Proc.new { |arg| arg }
42
- event = described_class.event_retriever.call(params)
43
- expect(event).to eq params
96
+ it "matches all namespaced strings given no arguments" do
97
+ expect(namespace.to_regexp).to match namespace.call('foo.bar')
98
+ end
99
+ end
44
100
  end
45
101
  end
@@ -0,0 +1,3 @@
1
+ ENV["RAILS_ENV"] = 'test'
2
+ require File.expand_path("../dummy/config/environment", __FILE__)
3
+ require 'rspec/rails'
data/spec/spec_helper.rb CHANGED
@@ -1,15 +1,8 @@
1
- ENV["RAILS_ENV"] ||= 'test'
2
- require File.expand_path("../dummy/config/environment", __FILE__)
3
- require 'rspec/rails'
4
1
  require 'webmock/rspec'
5
-
6
- support_dir = File.join(File.dirname(__FILE__), '../spec/support/**/*.rb')
7
- Dir[support_dir].each { |f| require f }
2
+ require File.expand_path('../../lib/stripe_event', __FILE__)
3
+ Dir[File.expand_path('../spec/support/**/*.rb', __FILE__)].each { |f| require f }
8
4
 
9
5
  RSpec.configure do |config|
10
- config.include FixtureHelper
11
- config.include ActiveSupportHelper
12
-
13
6
  config.order = 'random'
14
7
 
15
8
  config.expect_with :rspec do |c|
data/stripe_event.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.version = StripeEvent::VERSION
10
10
  s.license = "MIT"
11
11
  s.authors = ["Danny Whalen"]
12
- s.email = ["dwhalen@integrallis.com"]
12
+ s.email = "daniel.r.whalen@gmail.com"
13
13
  s.homepage = "https://github.com/integrallis/stripe_event"
14
14
  s.summary = "Stripe webhook integration for Rails applications."
15
15
  s.description = "Stripe webhook integration for Rails applications."
@@ -17,9 +17,10 @@ Gem::Specification.new do |s|
17
17
  s.files = `git ls-files`.split("\n")
18
18
  s.test_files = `git ls-files -- Appraisals {spec,gemfiles}/*`.split("\n")
19
19
 
20
- s.add_dependency "rails", ">= 3.1"
20
+ s.add_dependency "activesupport", ">= 3.1"
21
21
  s.add_dependency "stripe", "~> 1.6"
22
22
 
23
+ s.add_development_dependency "rails", ">= 3.1"
23
24
  s.add_development_dependency "rspec-rails", "~> 2.12"
24
25
  s.add_development_dependency "webmock", "~> 1.9"
25
26
  s.add_development_dependency "appraisal"
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stripe_event
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Danny Whalen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-19 00:00:00.000000000 Z
11
+ date: 2013-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - '>='
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rspec-rails
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -81,8 +95,7 @@ dependencies:
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
83
97
  description: Stripe webhook integration for Rails applications.
84
- email:
85
- - dwhalen@integrallis.com
98
+ email: daniel.r.whalen@gmail.com
86
99
  executables: []
87
100
  extensions: []
88
101
  extra_rdoc_files: []
@@ -98,7 +111,6 @@ files:
98
111
  - Rakefile
99
112
  - app/controllers/stripe_event/webhook_controller.rb
100
113
  - config/routes.rb
101
- - dashboard-webhook.png
102
114
  - gemfiles/rails3.1.gemfile
103
115
  - gemfiles/rails3.1.gemfile.lock
104
116
  - gemfiles/rails3.2.gemfile
@@ -141,10 +153,8 @@ files:
141
153
  - spec/dummy/public/favicon.ico
142
154
  - spec/dummy/script/rails
143
155
  - spec/lib/stripe_event_spec.rb
156
+ - spec/rails_helper.rb
144
157
  - spec/spec_helper.rb
145
- - spec/support/active_support_helper.rb
146
- - spec/support/core_ext.rb
147
- - spec/support/fixture_helper.rb
148
158
  - spec/support/fixtures/evt_charge_succeeded.json
149
159
  - spec/support/fixtures/evt_invalid_id.json
150
160
  - stripe_event.gemspec
@@ -168,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
168
178
  version: '0'
169
179
  requirements: []
170
180
  rubyforge_project:
171
- rubygems_version: 2.0.3
181
+ rubygems_version: 2.0.14
172
182
  signing_key:
173
183
  specification_version: 4
174
184
  summary: Stripe webhook integration for Rails applications.
@@ -213,9 +223,8 @@ test_files:
213
223
  - spec/dummy/public/favicon.ico
214
224
  - spec/dummy/script/rails
215
225
  - spec/lib/stripe_event_spec.rb
226
+ - spec/rails_helper.rb
216
227
  - spec/spec_helper.rb
217
- - spec/support/active_support_helper.rb
218
- - spec/support/core_ext.rb
219
- - spec/support/fixture_helper.rb
220
228
  - spec/support/fixtures/evt_charge_succeeded.json
221
229
  - spec/support/fixtures/evt_invalid_id.json
230
+ has_rdoc:
Binary file
@@ -1,5 +0,0 @@
1
- module ActiveSupportHelper
2
- def subscribers_for_type(name)
3
- ActiveSupport::Notifications.notifier.listeners_for(name)
4
- end
5
- end
@@ -1,4 +0,0 @@
1
- class Set
2
- # Just for tests
3
- delegate :sample, :to => :to_a
4
- end
@@ -1,10 +0,0 @@
1
- module FixtureHelper
2
- def stub_event(identifier, status = 200)
3
- stub_request(:get, "https://api.stripe.com/v1/events/#{identifier}").
4
- to_return(:status => status, :body => get_fixture(identifier), :headers => {})
5
- end
6
-
7
- def get_fixture(name)
8
- File.read("spec/support/fixtures/#{name}.json")
9
- end
10
- end