effective_orders 1.3.13 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a8b46b9f926b8ab975a3e81e57164dc222f14a36
4
- data.tar.gz: 4107592428dbfde1afe5247d6a813923f5a90f1d
3
+ metadata.gz: b754cd5d2aeb5e4000ee3b4c2fc1c16278f320e3
4
+ data.tar.gz: 0270f46b3bbef65baa6dbc4ac7167d8ca6871d05
5
5
  SHA512:
6
- metadata.gz: d65f51de361cb78dcc07dc27cae84ae0e11a1946240e5cf5bdb9638e2e7ba6482d8eda7156df9659304f1423ec6f2313515068ce7c0400b407f77897c0533d02
7
- data.tar.gz: b2e93919ec0839a2cbb1b84e343cf3a0a0ce20c6b7b33adf0e08d046eb961bad72783afc44e0312c041011fb5881a53619ab0873380b897987763e8aea47abee
6
+ metadata.gz: 84b348ed34d246acb8b47b4c71efe6a7e0feb58814aaf5e1ed4cf3228f4c6bdea625948cd1a519d6e4cbba7c1b792c2689d431ecf56e9d9e7690531168538faf
7
+ data.tar.gz: 43466c0a0104dc78c678ec8e458ca30d95acf73796a19e934c6df44b5aef7354dfc789d98954e75aa7e388d972fe711f0b2a6b787fdb09e6fc75e93601673b77
@@ -1,22 +1,23 @@
1
1
  module Effective
2
2
  class WebhooksController < ApplicationController
3
- protect_from_forgery :except => [:stripe]
3
+ protect_from_forgery except: [:stripe]
4
4
  skip_authorization_check if defined?(CanCan)
5
5
 
6
6
  # Webhook from stripe
7
7
  def stripe
8
- (head(:ok) and return) if (params[:livemode] == false && Rails.env.production?) || params[:object] != 'event' || params[:id].blank?
8
+ (head(:ok) && return) if (params[:livemode] == false && Rails.env.production?) || params[:object] != 'event' || params[:id].blank?
9
9
 
10
10
  # Dont trust the POST, and instead request the actual event from Stripe
11
- @event = Stripe::Event.retrieve(params[:id]) rescue (head(:ok) and return)
11
+ @event = Stripe::Event.retrieve(params[:id]) rescue (head(:ok) && return)
12
12
 
13
13
  Effective::Customer.transaction do
14
14
  begin
15
15
  case @event.type
16
- when 'customer.created' ; stripe_customer_created(@event)
17
- when 'customer.deleted' ; stripe_customer_deleted(@event)
18
- when 'customer.subscription.created' ; stripe_subscription_created(@event)
19
- when 'customer.subscription.deleted' ; stripe_subscription_deleted(@event)
16
+ when 'customer.created' then stripe_customer_created(@event)
17
+ when 'customer.deleted' then stripe_customer_deleted(@event)
18
+ when 'customer.subscription.created' then stripe_subscription_created(@event)
19
+ when 'customer.subscription.deleted' then stripe_subscription_deleted(@event)
20
+ when 'invoice.payment_succeeded' then invoice_payment_succeeded(@event)
20
21
  end
21
22
  rescue => e
22
23
  Rails.logger.info "Stripe Webhook Error: #{e.message}"
@@ -31,7 +32,7 @@ module Effective
31
32
 
32
33
  def stripe_customer_created(event)
33
34
  stripe_customer = event.data.object
34
- user = ::User.where(:email => stripe_customer.email).first
35
+ user = ::User.where(email: stripe_customer.email).first
35
36
 
36
37
  if user.present?
37
38
  customer = Effective::Customer.for_user(user) # This is a first_or_create
@@ -42,20 +43,20 @@ module Effective
42
43
 
43
44
  def stripe_customer_deleted(event)
44
45
  stripe_customer = event.data.object
45
- user = ::User.where(:email => stripe_customer.email).first
46
+ user = ::User.where(email: stripe_customer.email).first
46
47
 
47
48
  if user.present?
48
- customer = Effective::Customer.where(:user_id => user.id).first
49
+ customer = Effective::Customer.where(user_id: user.id).first
49
50
  customer.destroy! if customer
50
51
  end
51
52
  end
52
53
 
53
54
  def stripe_subscription_created(event)
54
55
  stripe_subscription = event.data.object
55
- @customer = Effective::Customer.where(:stripe_customer_id => stripe_subscription.customer).first
56
+ @customer = Effective::Customer.where(stripe_customer_id: stripe_subscription.customer).first
56
57
 
57
58
  if @customer.present?
58
- subscription = @customer.subscriptions.where(:stripe_plan_id => stripe_subscription.plan.id).first_or_initialize
59
+ subscription = @customer.subscriptions.where(stripe_plan_id: stripe_subscription.plan.id).first_or_initialize
59
60
 
60
61
  subscription.stripe_subscription_id = stripe_subscription.id
61
62
  subscription.stripe_plan_id = (stripe_subscription.plan.id rescue nil)
@@ -75,12 +76,43 @@ module Effective
75
76
 
76
77
  def stripe_subscription_deleted(event)
77
78
  stripe_subscription = event.data.object
78
- customer = Effective::Customer.where(:stripe_customer_id => stripe_subscription.customer).first
79
+ @customer = Effective::Customer.where(stripe_customer_id: stripe_subscription.customer).first
79
80
 
80
- if customer.present?
81
- customer.subscriptions.find { |subscription| subscription.stripe_plan_id == stripe_subscription.plan.id }.try(:destroy)
81
+ if @customer.present?
82
+ @customer.subscriptions.find { |subscription| subscription.stripe_plan_id == stripe_subscription.plan.id }.try(:destroy)
83
+ subscription_deleted_callback(event)
84
+ end
85
+ end
86
+
87
+ def invoice_payment_succeeded(event)
88
+ @customer = Effective::Customer.where(stripe_customer_id: event.data.object.customer).first
89
+
90
+ check_for_subscription_renewal(event) if @customer.present?
91
+ end
92
+
93
+ def check_for_subscription_renewal(event)
94
+ invoice_payment = event.data.object
95
+ subscription_payments = invoice_payment.lines.select { |line_item| line_item.type == 'subscription' }
96
+
97
+ if subscription_payments.present?
98
+ customer = Stripe::Customer.retrieve(invoice_payment.customer)
99
+ subscription_payments.each do |subscription_payment|
100
+ subscription_renewed_callback(event) if stripe_subscription_renewed?(customer, subscription_payment)
101
+ end
82
102
  end
83
103
  end
84
104
 
105
+ def stripe_subscription_renewed?(customer, subscription_payment)
106
+ subscription = customer.subscriptions.retrieve(subscription_payment.id) rescue nil # API client raises error when object not found
107
+ subscription.present? && subscription.status == 'active' && subscription.start < (subscription_payment.period.start - 1.day)
108
+ end
109
+
110
+ def subscription_deleted_callback(_event)
111
+ # Can be overridden in Effective::WebhooksController within a Rails application
112
+ end
113
+
114
+ def subscription_renewed_callback(_event)
115
+ # Can be overridden in Effective::WebhooksController within a Rails application
116
+ end
85
117
  end
86
118
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveOrders
2
- VERSION = '1.3.13'.freeze
2
+ VERSION = '1.4.0'.freeze
3
3
  end
@@ -9,10 +9,11 @@ describe Effective::WebhooksController do
9
9
  let(:order) { FactoryGirl.create(:order) }
10
10
  let(:buyer) { Effective::Customer.for_user(order.user) }
11
11
 
12
- let(:event) { StripeMock.mock_webhook_event('customer.subscription.created') }
13
12
  let(:event_hash) { event.to_hash }
14
13
 
15
14
  describe '#stripe' do
15
+ let(:event) { StripeMock.mock_webhook_event('customer.subscription.created') }
16
+
16
17
  it 'retrieves the real event from Stripe based on passed ID' do
17
18
  Stripe::Event.should_receive(:retrieve).with(event_hash[:id])
18
19
  post :stripe, event_hash
@@ -41,13 +42,12 @@ describe Effective::WebhooksController do
41
42
  assigns(:event).should eq nil
42
43
  response.code.should eq '200'
43
44
  end
44
-
45
45
  end
46
46
 
47
47
  describe '#stripe.subscription_created' do
48
- before(:each) do
49
- buyer.update_attributes(:stripe_customer_id => event.data.object.customer)
50
- end
48
+ let(:event) { StripeMock.mock_webhook_event('customer.subscription.created') }
49
+
50
+ before { buyer.update_attributes(stripe_customer_id: event.data.object.customer) }
51
51
 
52
52
  it 'assigns the existing customer, if exists' do
53
53
  post :stripe, event_hash
@@ -73,7 +73,98 @@ describe Effective::WebhooksController do
73
73
 
74
74
  assigns(:order).present?.should eq false
75
75
  end
76
+ end
77
+
78
+ describe '#stripe.subscription_deleted' do
79
+ let(:event) { StripeMock.mock_webhook_event('customer.subscription.deleted') }
80
+ let!(:subscription) { FactoryGirl.create(:subscription, customer_id: buyer.id) }
81
+
82
+ context 'when customer exists' do
83
+ before do
84
+ buyer.update_attributes(stripe_customer_id: event.data.object.customer)
85
+ subscription.stripe_plan_id = event.data.object.plan.id
86
+ subscription.save(validate: false)
87
+ end
88
+
89
+ it 'assigns the existing customer' do
90
+ post :stripe, event_hash
91
+ assigns(:customer).should eq buyer
92
+ end
93
+
94
+ it 'should destroy customer subscription' do
95
+ expect { post :stripe, event_hash }.to change { buyer.subscriptions.count }.from(1).to(0)
96
+ end
97
+
98
+ it 'should invoke subscription_deleted_callback' do
99
+ controller.should_receive(:subscription_deleted_callback).with(kind_of(Stripe::Event)).once
100
+ post :stripe, event_hash
101
+ end
102
+ end
103
+
104
+ context 'when customer does not exist' do
105
+ it 'should not destroy any of subscriptions' do
106
+ expect { post :stripe, event_hash }.not_to change { Effective::Subscription.count }
107
+ end
76
108
 
109
+ it 'should not invoke subscription_deleted_callback' do
110
+ controller.should_not_receive(:subscription_deleted_callback)
111
+ post :stripe, event_hash
112
+ end
113
+ end
77
114
  end
78
115
 
116
+ describe '#stripe.invoice_payment_succeeded' do
117
+ let(:event) { StripeMock.mock_webhook_event('invoice.payment_succeeded') }
118
+
119
+ context 'when customer exists' do
120
+ before { buyer.update_attributes(stripe_customer_id: event.data.object.customer) }
121
+
122
+ context 'when subscription payments present' do
123
+ context 'with renewals' do
124
+ let(:subscription_mock) { double('subscription', status: 'active', start: 1383672652) }
125
+ let(:subscriptions) { double('subscriptions', retrieve: subscription_mock) }
126
+
127
+ before { Stripe::Customer.should_receive(:retrieve).and_return(double('customer', subscriptions: subscriptions)) }
128
+
129
+ it 'assigns the existing customer, if exists' do
130
+ post :stripe, event_hash
131
+ assigns(:customer).should eq buyer
132
+ end
133
+
134
+ it 'should invoke subscription_renewed_callback' do
135
+ controller.should_receive(:subscription_renewed_callback).with(kind_of(Stripe::Event)).once
136
+ post :stripe, event_hash
137
+ end
138
+ end
139
+
140
+ context 'without renewals' do
141
+ let(:subscription_mock) { double('subscription', status: 'active', start: 1383759053) } # start and period.start are equal
142
+ let(:subscriptions) { double('subscriptions', retrieve: subscription_mock) }
143
+
144
+ before { Stripe::Customer.should_receive(:retrieve).and_return(double('customer', subscriptions: subscriptions)) }
145
+
146
+ it 'should not invoke subscription_renewed_callback' do
147
+ controller.should_not_receive(:subscription_renewed_callback)
148
+ post :stripe, event_hash
149
+ end
150
+ end
151
+ end
152
+
153
+ context 'when no subscription payments' do
154
+ let(:event) { StripeMock.mock_webhook_event('invoice.payment_succeeded.without_renewals') }
155
+
156
+ it 'should not invoke subscription_renewed_callback' do
157
+ controller.should_not_receive(:subscription_renewed_callback)
158
+ post :stripe, event_hash
159
+ end
160
+ end
161
+ end
162
+
163
+ context 'when customer does not exist' do
164
+ it 'should not invoke subscription_renewed_callback' do
165
+ controller.should_not_receive(:subscription_renewed_callback)
166
+ post :stripe, event_hash
167
+ end
168
+ end
169
+ end
79
170
  end
@@ -0,0 +1,76 @@
1
+ {
2
+ "created": 1326853478,
3
+ "livemode": false,
4
+ "id": "evt_00000000000000",
5
+ "type": "invoice.payment_succeeded",
6
+ "object": "event",
7
+ "data": {
8
+ "object": {
9
+ "date": 1394018368,
10
+ "id": "in_00000000000000",
11
+ "period_start": 1394018368,
12
+ "period_end": 1394018368,
13
+ "lines": {
14
+ "object": "list",
15
+ "count": 3,
16
+ "url": "/v1/invoices/in_00000000000000/lines",
17
+ "data": [
18
+ {
19
+ "id": "ii_00000000000000",
20
+ "object": "line_item",
21
+ "type": "invoiceitem",
22
+ "livemode": false,
23
+ "amount": 19000,
24
+ "currency": "usd",
25
+ "proration": true,
26
+ "period": {
27
+ "start": 1393765661,
28
+ "end": 1393765661
29
+ },
30
+ "quantity": null,
31
+ "plan": null,
32
+ "description": "Remaining time on Platinum after 02 Mar 2014",
33
+ "metadata": {}
34
+ },
35
+ {
36
+ "id": "ii_00000000000001",
37
+ "object": "line_item",
38
+ "type": "invoiceitem",
39
+ "livemode": false,
40
+ "amount": -9000,
41
+ "currency": "usd",
42
+ "proration": true,
43
+ "period": {
44
+ "start": 1393765661,
45
+ "end": 1393765661
46
+ },
47
+ "quantity": null,
48
+ "plan": null,
49
+ "description": "Unused time on Gold after 05 Mar 2014",
50
+ "metadata": {}
51
+ }
52
+ ]
53
+ },
54
+ "subtotal": 30000,
55
+ "total": 30000,
56
+ "customer": "cus_00000000000000",
57
+ "object": "invoice",
58
+ "attempted": true,
59
+ "closed": true,
60
+ "paid": true,
61
+ "livemode": false,
62
+ "attempt_count": 1,
63
+ "amount_due": 0,
64
+ "currency": "usd",
65
+ "starting_balance": 0,
66
+ "ending_balance": 0,
67
+ "next_payment_attempt": null,
68
+ "charge": "ch_00000000000000",
69
+ "discount": null,
70
+ "application_fee": null,
71
+ "subscription": "su_00000000000000",
72
+ "metadata": {},
73
+ "description": null
74
+ }
75
+ }
76
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_orders
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.13
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-24 00:00:00.000000000 Z
11
+ date: 2015-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -463,6 +463,7 @@ files:
463
463
  - spec/dummy/public/422.html
464
464
  - spec/dummy/public/500.html
465
465
  - spec/dummy/public/favicon.ico
466
+ - spec/fixtures/stripe_webhooks/invoice.payment_succeeded.without_renewals.json
466
467
  - spec/helpers/effective_orders_helper_spec.rb
467
468
  - spec/models/acts_as_purchasable_spec.rb
468
469
  - spec/models/customer_spec.rb
@@ -545,6 +546,7 @@ test_files:
545
546
  - spec/dummy/public/favicon.ico
546
547
  - spec/dummy/Rakefile
547
548
  - spec/dummy/README.rdoc
549
+ - spec/fixtures/stripe_webhooks/invoice.payment_succeeded.without_renewals.json
548
550
  - spec/helpers/effective_orders_helper_spec.rb
549
551
  - spec/models/acts_as_purchasable_spec.rb
550
552
  - spec/models/customer_spec.rb