pay 0.0.2 → 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pay might be problematic. Click here for more details.

Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +256 -29
  4. data/Rakefile +1 -6
  5. data/app/controllers/pay/webhooks/braintree_controller.rb +56 -0
  6. data/app/jobs/pay/email_sync_job.rb +12 -0
  7. data/app/mailers/pay/user_mailer.rb +42 -0
  8. data/app/models/pay/charge.rb +31 -0
  9. data/app/models/pay/subscription.rb +77 -0
  10. data/app/views/pay/user_mailer/receipt.html.erb +20 -0
  11. data/app/views/pay/user_mailer/refund.html.erb +21 -0
  12. data/app/views/pay/user_mailer/subscription_renewing.html.erb +6 -0
  13. data/config/routes.rb +3 -1
  14. data/db/migrate/20170205020145_create_subscriptions.rb +1 -1
  15. data/db/migrate/20170503131610_add_fields_to_users.rb +3 -2
  16. data/db/migrate/20170727235816_create_charges.rb +17 -0
  17. data/lib/generators/pay/email_views_generator.rb +13 -0
  18. data/lib/pay.rb +65 -1
  19. data/lib/pay/billable.rb +54 -24
  20. data/lib/pay/billable/sync_email.rb +41 -0
  21. data/lib/pay/braintree.rb +16 -0
  22. data/lib/pay/braintree/api.rb +30 -0
  23. data/lib/pay/braintree/billable.rb +219 -0
  24. data/lib/pay/braintree/charge.rb +27 -0
  25. data/lib/pay/braintree/subscription.rb +173 -0
  26. data/lib/pay/engine.rb +14 -1
  27. data/lib/pay/receipts.rb +37 -0
  28. data/lib/pay/stripe.rb +17 -0
  29. data/lib/pay/stripe/api.rb +13 -0
  30. data/lib/pay/stripe/billable.rb +143 -0
  31. data/lib/pay/stripe/charge.rb +30 -0
  32. data/lib/pay/stripe/subscription.rb +48 -0
  33. data/lib/pay/stripe/webhooks.rb +39 -0
  34. data/lib/pay/stripe/webhooks/charge_refunded.rb +25 -0
  35. data/lib/pay/stripe/webhooks/charge_succeeded.rb +47 -0
  36. data/lib/pay/stripe/webhooks/customer_deleted.rb +31 -0
  37. data/lib/pay/stripe/webhooks/customer_updated.rb +19 -0
  38. data/lib/pay/stripe/webhooks/source_deleted.rb +19 -0
  39. data/lib/pay/stripe/webhooks/subscription_created.rb +46 -0
  40. data/lib/pay/stripe/webhooks/subscription_deleted.rb +21 -0
  41. data/lib/pay/stripe/webhooks/subscription_renewing.rb +25 -0
  42. data/lib/pay/stripe/webhooks/subscription_updated.rb +35 -0
  43. data/lib/pay/version.rb +1 -1
  44. metadata +124 -30
  45. data/app/models/subscription.rb +0 -59
  46. data/config/initializers/pay.rb +0 -3
  47. data/config/initializers/stripe.rb +0 -1
  48. data/db/development.sqlite3 +0 -0
  49. data/lib/pay/billable/braintree.rb +0 -57
  50. data/lib/pay/billable/stripe.rb +0 -47
  51. data/lib/tasks/pay_tasks.rake +0 -4
@@ -0,0 +1,19 @@
1
+ module Pay
2
+ module Stripe
3
+ module Webhooks
4
+
5
+ class CustomerUpdated
6
+ def call(event)
7
+ object = event.data.object
8
+ user = Pay.user_model.find_by(processor: :stripe, processor_id: object.id)
9
+
10
+ # Couldn't find user, we can skip
11
+ return unless user.present?
12
+
13
+ user.sync_card_from_stripe
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module Pay
2
+ module Stripe
3
+ module Webhooks
4
+
5
+ class SourceDeleted
6
+ def call(event)
7
+ object = event.data.object
8
+ user = Pay.user_model.find_by(processor: :stripe, processor_id: object.customer)
9
+
10
+ # Couldn't find user, we can skip
11
+ return unless user.present?
12
+
13
+ user.sync_card_from_stripe
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,46 @@
1
+ module Pay
2
+ module Stripe
3
+ module Webhooks
4
+
5
+ class SubscriptionCreated
6
+ def call(event)
7
+ object = event.data.object
8
+
9
+ # We may already have the subscription in the database, so we can update that record
10
+ subscription = Pay.subscription_model.find_by(processor: :stripe, processor_id: object.id)
11
+
12
+ if subscription.nil?
13
+ # The customer should already be in the database
14
+ owner = Pay.user_model.find_by(processor: :stripe, processor_id: object.customer)
15
+
16
+ Rails.logger.error("[Pay] Unable to find #{Pay.user_model} with processor: :stripe and processor_id: '#{object.customer}'")
17
+ return if owner.nil?
18
+
19
+ subscription = Pay.subscription_model.new(owner: owner)
20
+ end
21
+
22
+ subscription.quantity = object.quantity
23
+ subscription.processor_plan = object.plan.id
24
+ subscription.trial_ends_at = Time.at(object.trial_end) if object.trial_end.present?
25
+
26
+ # If user was on trial, their subscription ends at the end of the trial
27
+ if object.cancel_at_period_end && subscription.on_trial?
28
+ subscription.ends_at = subscription.trial_ends_at
29
+
30
+ # User wasn't on trial, so subscription ends at period end
31
+ elsif object.cancel_at_period_end
32
+ subscription.ends_at = Time.at(object.current_period_end)
33
+
34
+ # Subscription isn't marked to cancel at period end
35
+ else
36
+ subscription.ends_at = nil
37
+ end
38
+
39
+ subscription.save!
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,21 @@
1
+ module Pay
2
+ module Stripe
3
+ module Webhooks
4
+
5
+ class SubscriptionDeleted
6
+ def call(event)
7
+ object = event.data.object
8
+ subscription = Pay.subscription_model.find_by(processor: :stripe, processor_id: object.id)
9
+
10
+ # We couldn't find the subscription for some reason, maybe it's from another service
11
+ return if subscription.nil?
12
+
13
+ # User canceled subscriptions have an ends_at
14
+ # Automatically canceled subscriptions need this value set
15
+ subscription.update!(ends_at: Time.at(object.ended_at)) if subscription.ends_at.blank? && object.ended_at.present?
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ module Pay
2
+ module Stripe
3
+ module Webhooks
4
+
5
+ class SubscriptionRenewing
6
+ def call(event)
7
+ # Event is of type "invoice" see:
8
+ # https://stripe.com/docs/api/invoices/object
9
+ subscription = Pay.subscription_model.find_by(
10
+ processor: :stripe,
11
+ processor_id: event.data.object.subscription
12
+ )
13
+ notify_user(subscription.owner, subscription) if subscription.present?
14
+ end
15
+
16
+ def notify_user(user, subscription)
17
+ if Pay.send_emails
18
+ Pay::UserMailer.subscription_renewing(user, subscription).deliver_later
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ module Pay
2
+ module Stripe
3
+ module Webhooks
4
+
5
+ class SubscriptionUpdated
6
+ def call(event)
7
+ object = event.data.object
8
+ subscription = Pay.subscription_model.find_by(processor: :stripe, processor_id: object.id)
9
+
10
+ return if subscription.nil?
11
+
12
+ subscription.quantity = object.quantity
13
+ subscription.processor_plan = object.plan.id
14
+ subscription.trial_ends_at = Time.at(object.trial_end) if object.trial_end.present?
15
+
16
+ # If user was on trial, their subscription ends at the end of the trial
17
+ if object.cancel_at_period_end && subscription.on_trial?
18
+ subscription.ends_at = subscription.trial_ends_at
19
+
20
+ # User wasn't on trial, so subscription ends at period end
21
+ elsif object.cancel_at_period_end
22
+ subscription.ends_at = Time.at(object.current_period_end)
23
+
24
+ # Subscription isn't marked to cancel at period end
25
+ else
26
+ subscription.ends_at = nil
27
+ end
28
+
29
+ subscription.save!
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
1
  module Pay
2
- VERSION = '0.0.2'
2
+ VERSION = '1.0.0.beta2'
3
3
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 1.0.0.beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Charnes
8
+ - Chris Oliver
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2017-06-12 00:00:00.000000000 Z
12
+ date: 2019-03-11 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: rails
@@ -25,33 +26,87 @@ dependencies:
25
26
  - !ruby/object:Gem::Version
26
27
  version: '4.2'
27
28
  - !ruby/object:Gem::Dependency
28
- name: stripe
29
+ name: rails-html-sanitizer
29
30
  requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
32
  - - "~>"
32
33
  - !ruby/object:Gem::Version
33
- version: '1.0'
34
+ version: 1.0.4
34
35
  type: :runtime
35
36
  prerelease: false
36
37
  version_requirements: !ruby/object:Gem::Requirement
37
38
  requirements:
38
39
  - - "~>"
39
40
  - !ruby/object:Gem::Version
40
- version: '1.0'
41
+ version: 1.0.4
41
42
  - !ruby/object:Gem::Dependency
42
43
  name: braintree
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: 2.92.0
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '3.0'
52
+ type: :development
53
+ prerelease: false
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 2.92.0
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: stripe
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '2.8'
69
+ - - "<"
70
+ - !ruby/object:Gem::Version
71
+ version: '5.0'
72
+ type: :development
73
+ prerelease: false
74
+ version_requirements: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '2.8'
79
+ - - "<"
80
+ - !ruby/object:Gem::Version
81
+ version: '5.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: stripe_event
43
84
  requirement: !ruby/object:Gem::Requirement
44
85
  requirements:
45
86
  - - "~>"
46
87
  - !ruby/object:Gem::Version
47
- version: '2.75'
48
- type: :runtime
88
+ version: '2.2'
89
+ type: :development
49
90
  prerelease: false
50
91
  version_requirements: !ruby/object:Gem::Requirement
51
92
  requirements:
52
93
  - - "~>"
53
94
  - !ruby/object:Gem::Version
54
- version: '2.75'
95
+ version: '2.2'
96
+ - !ruby/object:Gem::Dependency
97
+ name: bundler
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '2'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "<"
108
+ - !ruby/object:Gem::Version
109
+ version: '2'
55
110
  - !ruby/object:Gem::Dependency
56
111
  name: byebug
57
112
  requirement: !ruby/object:Gem::Requirement
@@ -95,21 +150,35 @@ dependencies:
95
150
  - !ruby/object:Gem::Version
96
151
  version: '0'
97
152
  - !ruby/object:Gem::Dependency
98
- name: rubocop
153
+ name: sqlite3
99
154
  requirement: !ruby/object:Gem::Requirement
100
155
  requirements:
101
- - - ">="
156
+ - - "~>"
102
157
  - !ruby/object:Gem::Version
103
- version: '0'
158
+ version: 1.3.6
104
159
  type: :development
105
160
  prerelease: false
106
161
  version_requirements: !ruby/object:Gem::Requirement
107
162
  requirements:
108
- - - ">="
163
+ - - "~>"
109
164
  - !ruby/object:Gem::Version
110
- version: '0'
165
+ version: 1.3.6
111
166
  - !ruby/object:Gem::Dependency
112
- name: sqlite3
167
+ name: stripe-ruby-mock
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '2.5'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '2.5'
180
+ - !ruby/object:Gem::Dependency
181
+ name: vcr
113
182
  requirement: !ruby/object:Gem::Requirement
114
183
  requirements:
115
184
  - - ">="
@@ -123,22 +192,23 @@ dependencies:
123
192
  - !ruby/object:Gem::Version
124
193
  version: '0'
125
194
  - !ruby/object:Gem::Dependency
126
- name: stripe-ruby-mock
195
+ name: webmock
127
196
  requirement: !ruby/object:Gem::Requirement
128
197
  requirements:
129
- - - "~>"
198
+ - - ">="
130
199
  - !ruby/object:Gem::Version
131
- version: '2.4'
200
+ version: '0'
132
201
  type: :development
133
202
  prerelease: false
134
203
  version_requirements: !ruby/object:Gem::Requirement
135
204
  requirements:
136
- - - "~>"
205
+ - - ">="
137
206
  - !ruby/object:Gem::Version
138
- version: '2.4'
207
+ version: '0'
139
208
  description: A Ruby on Rails subscription engine.
140
209
  email:
141
210
  - jason@thecharnes.com
211
+ - excid3@gmail.com
142
212
  executables: []
143
213
  extensions: []
144
214
  extra_rdoc_files: []
@@ -150,25 +220,50 @@ files:
150
220
  - app/assets/javascripts/pay/application.js
151
221
  - app/assets/stylesheets/pay/application.css
152
222
  - app/controllers/pay/application_controller.rb
223
+ - app/controllers/pay/webhooks/braintree_controller.rb
153
224
  - app/helpers/pay/application_helper.rb
154
225
  - app/jobs/pay/application_job.rb
226
+ - app/jobs/pay/email_sync_job.rb
155
227
  - app/mailers/pay/application_mailer.rb
228
+ - app/mailers/pay/user_mailer.rb
156
229
  - app/models/pay/application_record.rb
157
- - app/models/subscription.rb
230
+ - app/models/pay/charge.rb
231
+ - app/models/pay/subscription.rb
158
232
  - app/views/layouts/pay/application.html.erb
159
- - config/initializers/pay.rb
160
- - config/initializers/stripe.rb
233
+ - app/views/pay/user_mailer/receipt.html.erb
234
+ - app/views/pay/user_mailer/refund.html.erb
235
+ - app/views/pay/user_mailer/subscription_renewing.html.erb
161
236
  - config/routes.rb
162
- - db/development.sqlite3
163
237
  - db/migrate/20170205020145_create_subscriptions.rb
164
238
  - db/migrate/20170503131610_add_fields_to_users.rb
239
+ - db/migrate/20170727235816_create_charges.rb
240
+ - lib/generators/pay/email_views_generator.rb
165
241
  - lib/pay.rb
166
242
  - lib/pay/billable.rb
167
- - lib/pay/billable/braintree.rb
168
- - lib/pay/billable/stripe.rb
243
+ - lib/pay/billable/sync_email.rb
244
+ - lib/pay/braintree.rb
245
+ - lib/pay/braintree/api.rb
246
+ - lib/pay/braintree/billable.rb
247
+ - lib/pay/braintree/charge.rb
248
+ - lib/pay/braintree/subscription.rb
169
249
  - lib/pay/engine.rb
250
+ - lib/pay/receipts.rb
251
+ - lib/pay/stripe.rb
252
+ - lib/pay/stripe/api.rb
253
+ - lib/pay/stripe/billable.rb
254
+ - lib/pay/stripe/charge.rb
255
+ - lib/pay/stripe/subscription.rb
256
+ - lib/pay/stripe/webhooks.rb
257
+ - lib/pay/stripe/webhooks/charge_refunded.rb
258
+ - lib/pay/stripe/webhooks/charge_succeeded.rb
259
+ - lib/pay/stripe/webhooks/customer_deleted.rb
260
+ - lib/pay/stripe/webhooks/customer_updated.rb
261
+ - lib/pay/stripe/webhooks/source_deleted.rb
262
+ - lib/pay/stripe/webhooks/subscription_created.rb
263
+ - lib/pay/stripe/webhooks/subscription_deleted.rb
264
+ - lib/pay/stripe/webhooks/subscription_renewing.rb
265
+ - lib/pay/stripe/webhooks/subscription_updated.rb
170
266
  - lib/pay/version.rb
171
- - lib/tasks/pay_tasks.rake
172
267
  homepage: https://github.com/jasoncharnes/pay
173
268
  licenses:
174
269
  - MIT
@@ -184,12 +279,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
184
279
  version: '0'
185
280
  required_rubygems_version: !ruby/object:Gem::Requirement
186
281
  requirements:
187
- - - ">="
282
+ - - ">"
188
283
  - !ruby/object:Gem::Version
189
- version: '0'
284
+ version: 1.3.1
190
285
  requirements: []
191
- rubyforge_project:
192
- rubygems_version: 2.6.12
286
+ rubygems_version: 3.0.1
193
287
  signing_key:
194
288
  specification_version: 4
195
289
  summary: A Ruby on Rails subscription engine.
@@ -1,59 +0,0 @@
1
- class Subscription < ApplicationRecord
2
- # Associations
3
- belongs_to :owner, class_name: Pay.billable_class, foreign_key: :owner_id
4
-
5
- # Validations
6
- validates :name, presence: true
7
- validates :processor, presence: true
8
- validates :processor_id, presence: true
9
- validates :processor_plan, presence: true
10
- validates :quantity, presence: true
11
-
12
- # Scopes
13
- scope :for_name, ->(name) { where(name: name) }
14
-
15
- def on_trial?
16
- trial_ends_at? && Time.zone.now < trial_ends_at
17
- end
18
-
19
- def cancelled?
20
- ends_at?
21
- end
22
-
23
- def on_grace_period?
24
- cancelled? && Time.zone.now < ends_at
25
- end
26
-
27
- def active?
28
- ends_at.nil? || on_grace_period? || on_trial?
29
- end
30
-
31
- def cancel
32
- subscription = processor_subscription.delete(at_period_end: true)
33
- update(ends_at: Time.at(subscription.current_period_end))
34
- end
35
-
36
- def cancel_now!
37
- subscription = processor_subscription.delete
38
- update(ends_at: Time.at(subscription.current_period_end))
39
- end
40
-
41
- def resume
42
- unless on_grace_period?
43
- raise StandardError,
44
- 'You can only resume subscriptions within their grace period.'
45
- end
46
-
47
- subscription = processor_subscription
48
- subscription.plan = processor_plan
49
- subscription.trial_end = on_trial? ? trial_ends_at.to_i : 'now'
50
- subscription.save
51
-
52
- update(ends_at: nil)
53
- self
54
- end
55
-
56
- def processor_subscription
57
- owner.processor_subscription(processor_id)
58
- end
59
- end