reji 1.0.0 → 1.1.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +73 -0
  4. data/.rubocop_todo.yml +31 -0
  5. data/Appraisals +2 -0
  6. data/Gemfile +1 -1
  7. data/README.md +41 -17
  8. data/Rakefile +8 -2
  9. data/app/controllers/reji/payment_controller.rb +4 -4
  10. data/app/controllers/reji/webhook_controller.rb +51 -62
  11. data/app/views/payment.html.erb +4 -4
  12. data/app/views/receipt.html.erb +16 -16
  13. data/config/routes.rb +2 -0
  14. data/gemfiles/rails_5.0.gemfile +9 -9
  15. data/gemfiles/rails_5.1.gemfile +7 -9
  16. data/gemfiles/rails_5.2.gemfile +7 -9
  17. data/gemfiles/rails_6.0.gemfile +7 -9
  18. data/lib/generators/reji/install/install_generator.rb +20 -24
  19. data/lib/generators/reji/install/templates/reji.rb +2 -2
  20. data/lib/reji.rb +12 -8
  21. data/lib/reji/concerns/manages_customer.rb +25 -29
  22. data/lib/reji/concerns/manages_invoices.rb +37 -44
  23. data/lib/reji/concerns/manages_payment_methods.rb +45 -62
  24. data/lib/reji/concerns/manages_subscriptions.rb +13 -13
  25. data/lib/reji/concerns/performs_charges.rb +7 -7
  26. data/lib/reji/concerns/prorates.rb +1 -1
  27. data/lib/reji/configuration.rb +2 -2
  28. data/lib/reji/engine.rb +2 -0
  29. data/lib/reji/errors.rb +9 -9
  30. data/lib/reji/invoice.rb +57 -56
  31. data/lib/reji/invoice_line_item.rb +21 -23
  32. data/lib/reji/payment.rb +9 -5
  33. data/lib/reji/payment_method.rb +8 -4
  34. data/lib/reji/subscription.rb +165 -183
  35. data/lib/reji/subscription_builder.rb +41 -49
  36. data/lib/reji/subscription_item.rb +26 -26
  37. data/lib/reji/tax.rb +8 -10
  38. data/lib/reji/version.rb +1 -1
  39. data/reji.gemspec +5 -4
  40. data/spec/dummy/app/models/user.rb +3 -7
  41. data/spec/dummy/application.rb +3 -7
  42. data/spec/dummy/db/schema.rb +3 -4
  43. data/spec/feature/charges_spec.rb +1 -1
  44. data/spec/feature/customer_spec.rb +1 -1
  45. data/spec/feature/invoices_spec.rb +6 -6
  46. data/spec/feature/multiplan_subscriptions_spec.rb +51 -53
  47. data/spec/feature/payment_methods_spec.rb +25 -25
  48. data/spec/feature/pending_updates_spec.rb +26 -26
  49. data/spec/feature/subscriptions_spec.rb +78 -78
  50. data/spec/feature/webhooks_spec.rb +72 -72
  51. data/spec/spec_helper.rb +2 -2
  52. data/spec/support/feature_helpers.rb +6 -12
  53. data/spec/unit/customer_spec.rb +13 -13
  54. data/spec/unit/invoice_line_item_spec.rb +12 -14
  55. data/spec/unit/invoice_spec.rb +7 -9
  56. data/spec/unit/payment_spec.rb +3 -3
  57. data/spec/unit/subscription_spec.rb +29 -30
  58. metadata +26 -11
  59. data/Gemfile.lock +0 -133
@@ -8,7 +8,7 @@ module Reji
8
8
 
9
9
  # Get the total amount that will be paid.
10
10
  def amount
11
- Reji.format_amount(self.raw_amount, @payment_intent.currency)
11
+ Reji.format_amount(raw_amount, @payment_intent.currency)
12
12
  end
13
13
 
14
14
  # Get the raw total amount that will be paid.
@@ -32,20 +32,20 @@ module Reji
32
32
  end
33
33
 
34
34
  # Determine if the payment was cancelled.
35
- def is_cancelled
35
+ def cancelled?
36
36
  @payment_intent.status == 'canceled'
37
37
  end
38
38
 
39
39
  # Determine if the payment was successful.
40
- def is_succeeded
40
+ def succeeded?
41
41
  @payment_intent.status == 'succeeded'
42
42
  end
43
43
 
44
44
  # Validate if the payment intent was successful and throw an exception if not.
45
45
  def validate
46
- raise Reji::PaymentFailureError::invalid_payment_method(self) if self.requires_payment_method
46
+ raise Reji::PaymentFailureError.invalid_payment_method(self) if requires_payment_method
47
47
 
48
- raise Reji::PaymentActionRequiredError::incomplete(self) if self.requires_action
48
+ raise Reji::PaymentActionRequiredError.incomplete(self) if requires_action
49
49
  end
50
50
 
51
51
  # The Stripe PaymentIntent instance.
@@ -57,5 +57,9 @@ module Reji
57
57
  def method_missing(key)
58
58
  @payment_intent[key]
59
59
  end
60
+
61
+ def respond_to_missing?(method_name, include_private = false)
62
+ super
63
+ end
60
64
  end
61
65
  end
@@ -3,7 +3,9 @@
3
3
  module Reji
4
4
  class PaymentMethod
5
5
  def initialize(owner, payment_method)
6
- raise Reji::InvalidPaymentMethodError.invalid_owner(payment_method, owner) if owner.stripe_id != payment_method.customer
6
+ if owner.stripe_id != payment_method.customer
7
+ raise Reji::InvalidPaymentMethodError.invalid_owner(payment_method, owner)
8
+ end
7
9
 
8
10
  @owner = owner
9
11
  @payment_method = payment_method
@@ -15,9 +17,7 @@ module Reji
15
17
  end
16
18
 
17
19
  # Get the Stripe model instance.
18
- def owner
19
- @owner
20
- end
20
+ attr_reader :owner
21
21
 
22
22
  # Get the Stripe PaymentMethod instance.
23
23
  def as_stripe_payment_method
@@ -28,5 +28,9 @@ module Reji
28
28
  def method_missing(key)
29
29
  @payment_method[key]
30
30
  end
31
+
32
+ def respond_to_missing?(method_name, include_private = false)
33
+ super
34
+ end
31
35
  end
32
36
  end
@@ -10,8 +10,8 @@ module Reji
10
10
 
11
11
  scope :incomplete, -> { where(stripe_status: 'incomplete') }
12
12
  scope :past_due, -> { where(stripe_status: 'past_due') }
13
- scope :active, -> {
14
- query = (where(ends_at: nil).or(on_grace_period))
13
+ scope :active, lambda {
14
+ query = where(ends_at: nil).or(on_grace_period)
15
15
  .where('stripe_status != ?', 'incomplete')
16
16
  .where('stripe_status != ?', 'incomplete_expired')
17
17
  .where('stripe_status != ?', 'unpaid')
@@ -24,171 +24,171 @@ module Reji
24
24
  scope :cancelled, -> { where.not(ends_at: nil) }
25
25
  scope :not_cancelled, -> { where(ends_at: nil) }
26
26
  scope :ended, -> { cancelled.not_on_grace_period }
27
- scope :on_trial, -> { where.not(trial_ends_at: nil).where('trial_ends_at > ?', Time.now) }
28
- scope :not_on_trial, -> { where(trial_ends_at: nil).or(where('trial_ends_at <= ?', Time.now)) }
29
- scope :on_grace_period, -> { where.not(ends_at: nil).where('ends_at > ?', Time.now) }
30
- scope :not_on_grace_period, -> { where(ends_at: nil).or(where('ends_at <= ?', Time.now)) }
27
+ scope :on_trial, -> { where.not(trial_ends_at: nil).where('trial_ends_at > ?', Time.current) }
28
+ scope :not_on_trial, -> { where(trial_ends_at: nil).or(where('trial_ends_at <= ?', Time.current)) }
29
+ scope :on_grace_period, -> { where.not(ends_at: nil).where('ends_at > ?', Time.current) }
30
+ scope :not_on_grace_period, -> { where(ends_at: nil).or(where('ends_at <= ?', Time.current)) }
31
31
 
32
32
  # The date on which the billing cycle should be anchored.
33
33
  @billing_cycle_anchor = nil
34
34
 
35
35
  # Get the user that owns the subscription.
36
36
  def user
37
- self.owner
37
+ owner
38
38
  end
39
39
 
40
40
  # Determine if the subscription has multiple plans.
41
- def has_multiple_plans
42
- self.stripe_plan.nil?
41
+ def multiple_plans?
42
+ stripe_plan.nil?
43
43
  end
44
44
 
45
45
  # Determine if the subscription has a single plan.
46
- def has_single_plan
47
- ! self.has_multiple_plans
46
+ def single_plan?
47
+ !multiple_plans?
48
48
  end
49
49
 
50
50
  # Determine if the subscription has a specific plan.
51
- def has_plan(plan)
52
- return self.items.any? { |item| item.stripe_plan == plan } if self.has_multiple_plans
51
+ def plan?(plan)
52
+ return items.any? { |item| item.stripe_plan == plan } if multiple_plans?
53
53
 
54
- self.stripe_plan == plan
54
+ stripe_plan == plan
55
55
  end
56
56
 
57
57
  # Get the subscription item for the given plan.
58
58
  def find_item_or_fail(plan)
59
- self.items.where(stripe_plan: plan).first
59
+ items.where(stripe_plan: plan).first
60
60
  end
61
61
 
62
62
  # Determine if the subscription is active, on trial, or within its grace period.
63
63
  def valid
64
- self.active || self.on_trial || self.on_grace_period
64
+ active || on_trial || on_grace_period
65
65
  end
66
66
 
67
67
  # Determine if the subscription is incomplete.
68
68
  def incomplete
69
- self.stripe_status == 'incomplete'
69
+ stripe_status == 'incomplete'
70
70
  end
71
71
 
72
72
  # Determine if the subscription is past due.
73
73
  def past_due
74
- self.stripe_status == 'past_due'
74
+ stripe_status == 'past_due'
75
75
  end
76
76
 
77
77
  # Determine if the subscription is active.
78
78
  def active
79
- (self.ends_at.nil? || self.on_grace_period) &&
80
- self.stripe_status != 'incomplete' &&
81
- self.stripe_status != 'incomplete_expired' &&
82
- self.stripe_status != 'unpaid' &&
83
- (! Reji.deactivate_past_due || self.stripe_status != 'past_due')
79
+ (ends_at.nil? || on_grace_period) &&
80
+ stripe_status != 'incomplete' &&
81
+ stripe_status != 'incomplete_expired' &&
82
+ stripe_status != 'unpaid' &&
83
+ (!Reji.deactivate_past_due || stripe_status != 'past_due')
84
84
  end
85
85
 
86
86
  # Sync the Stripe status of the subscription.
87
87
  def sync_stripe_status
88
- subscription = self.as_stripe_subscription
88
+ subscription = as_stripe_subscription
89
89
 
90
- self.update({stripe_status: subscription.status})
90
+ update({ stripe_status: subscription.status })
91
91
  end
92
92
 
93
93
  # Determine if the subscription is recurring and not on trial.
94
94
  def recurring
95
- ! self.on_trial && ! self.cancelled
95
+ !on_trial && !cancelled
96
96
  end
97
97
 
98
98
  # Determine if the subscription is no longer active.
99
99
  def cancelled
100
- ! self.ends_at.nil?
100
+ !ends_at.nil?
101
101
  end
102
102
 
103
103
  # Determine if the subscription has ended and the grace period has expired.
104
104
  def ended
105
- !! (self.cancelled && ! self.on_grace_period)
105
+ !!(cancelled && !on_grace_period)
106
106
  end
107
107
 
108
108
  # Determine if the subscription is within its trial period.
109
109
  def on_trial
110
- !! (self.trial_ends_at && self.trial_ends_at.future?)
110
+ !!(trial_ends_at && trial_ends_at.future?)
111
111
  end
112
112
 
113
113
  # Determine if the subscription is within its grace period after cancellation.
114
114
  def on_grace_period
115
- !! (self.ends_at && self.ends_at.future?)
115
+ !!(ends_at && ends_at.future?)
116
116
  end
117
117
 
118
118
  # Increment the quantity of the subscription.
119
119
  def increment_quantity(count = 1, plan = nil)
120
- self.guard_against_incomplete
120
+ guard_against_incomplete
121
121
 
122
122
  if plan
123
- self.find_item_or_fail(plan)
124
- .set_proration_behavior(self.prorate_behavior)
123
+ find_item_or_fail(plan)
124
+ .set_proration_behavior(proration_behavior)
125
125
  .increment_quantity(count)
126
126
 
127
127
  return self
128
128
  end
129
129
 
130
- self.guard_against_multiple_plans
130
+ guard_against_multiple_plans
131
131
 
132
- self.update_quantity(self.quantity + count, plan)
132
+ update_quantity(quantity + count, plan)
133
133
  end
134
134
 
135
135
  # Increment the quantity of the subscription, and invoice immediately.
136
136
  def increment_and_invoice(count = 1, plan = nil)
137
- self.guard_against_incomplete
137
+ guard_against_incomplete
138
138
 
139
- self.always_invoice
139
+ always_invoice
140
140
 
141
141
  if plan
142
- self.find_item_or_fail(plan)
143
- .set_proration_behavior(self.prorate_behavior)
142
+ find_item_or_fail(plan)
143
+ .set_proration_behavior(proration_behavior)
144
144
  .increment_quantity(count)
145
145
 
146
146
  return self
147
147
  end
148
148
 
149
- self.guard_against_multiple_plans
149
+ guard_against_multiple_plans
150
150
 
151
- self.increment_quantity(count, plan)
151
+ increment_quantity(count, plan)
152
152
  end
153
153
 
154
154
  # Decrement the quantity of the subscription.
155
155
  def decrement_quantity(count = 1, plan = nil)
156
- self.guard_against_incomplete
156
+ guard_against_incomplete
157
157
 
158
158
  if plan
159
- self.find_item_or_fail(plan)
160
- .set_proration_behavior(self.prorate_behavior)
159
+ find_item_or_fail(plan)
160
+ .set_proration_behavior(proration_behavior)
161
161
  .decrement_quantity(count)
162
162
 
163
163
  return self
164
164
  end
165
165
 
166
- self.guard_against_multiple_plans
166
+ guard_against_multiple_plans
167
167
 
168
- self.update_quantity([1, self.quantity - count].max, plan)
168
+ update_quantity([1, quantity - count].max, plan)
169
169
  end
170
170
 
171
171
  # Update the quantity of the subscription.
172
172
  def update_quantity(quantity, plan = nil)
173
- self.guard_against_incomplete
173
+ guard_against_incomplete
174
174
 
175
175
  if plan
176
- self.find_item_or_fail(plan)
177
- .set_proration_behavior(self.prorate_behavior)
176
+ find_item_or_fail(plan)
177
+ .set_proration_behavior(proration_behavior)
178
178
  .update_quantity(quantity)
179
179
 
180
180
  return self
181
181
  end
182
182
 
183
- self.guard_against_multiple_plans
183
+ guard_against_multiple_plans
184
184
 
185
- stripe_subscription = self.as_stripe_subscription
185
+ stripe_subscription = as_stripe_subscription
186
186
  stripe_subscription.quantity = quantity
187
- stripe_subscription.payment_behavior = self.payment_behavior
188
- stripe_subscription.proration_behavior = self.prorate_behavior
187
+ stripe_subscription.payment_behavior = payment_behavior
188
+ stripe_subscription.proration_behavior = proration_behavior
189
189
  stripe_subscription.save
190
190
 
191
- self.update(quantity: quantity)
191
+ update(quantity: quantity)
192
192
 
193
193
  self
194
194
  end
@@ -209,13 +209,13 @@ module Reji
209
209
 
210
210
  # Extend an existing subscription's trial period.
211
211
  def extend_trial(date)
212
- raise ArgumentError.new("Extending a subscription's trial requires a date in the future.") unless date.future?
212
+ raise ArgumentError, "Extending a subscription's trial requires a date in the future." unless date.future?
213
213
 
214
- subscription = self.as_stripe_subscription
214
+ subscription = as_stripe_subscription
215
215
  subscription.trial_end = date.to_i
216
216
  subscription.save
217
217
 
218
- self.update(trial_ends_at: date)
218
+ update(trial_ends_at: date)
219
219
 
220
220
  self
221
221
  end
@@ -224,25 +224,23 @@ module Reji
224
224
  def swap(plans, options = {})
225
225
  plans = [plans] unless plans.instance_of? Array
226
226
 
227
- raise ArgumentError.new('Please provide at least one plan when swapping.') if plans.empty?
227
+ raise ArgumentError, 'Please provide at least one plan when swapping.' if plans.empty?
228
228
 
229
- self.guard_against_incomplete
229
+ guard_against_incomplete
230
230
 
231
- items = self.merge_items_that_should_be_deleted_during_swap(
232
- self.parse_swap_plans(plans)
233
- )
231
+ items = merge_items_that_should_be_deleted_during_swap(parse_swap_plans(plans))
234
232
 
235
- stripe_subscription = Stripe::Subscription::update(
236
- self.stripe_id,
237
- self.get_swap_options(items, options),
238
- self.owner.stripe_options
233
+ stripe_subscription = Stripe::Subscription.update(
234
+ stripe_id,
235
+ get_swap_options(items, options),
236
+ owner.stripe_options
239
237
  )
240
238
 
241
- self.update({
242
- :stripe_status => stripe_subscription.status,
243
- :stripe_plan => stripe_subscription.plan ? stripe_subscription.plan.id : nil,
244
- :quantity => stripe_subscription.quantity,
245
- :ends_at => nil,
239
+ update({
240
+ stripe_status: stripe_subscription.status,
241
+ stripe_plan: stripe_subscription.plan ? stripe_subscription.plan.id : nil,
242
+ quantity: stripe_subscription.quantity,
243
+ ends_at: nil,
246
244
  })
247
245
 
248
246
  stripe_subscription.items.each do |item|
@@ -255,48 +253,46 @@ module Reji
255
253
  # Delete items that aren't attached to the subscription anymore...
256
254
  self.items.where('stripe_plan NOT IN (?)', items.values.pluck(:plan).compact).destroy_all
257
255
 
258
- if self.has_incomplete_payment
259
- Payment.new(stripe_subscription.latest_invoice.payment_intent).validate
260
- end
256
+ Payment.new(stripe_subscription.latest_invoice.payment_intent).validate if incomplete_payment?
261
257
 
262
258
  self
263
259
  end
264
260
 
265
261
  # Swap the subscription to new Stripe plans, and invoice immediately.
266
262
  def swap_and_invoice(plans, options = {})
267
- self.always_invoice
263
+ always_invoice
268
264
 
269
- self.swap(plans, options)
265
+ swap(plans, options)
270
266
  end
271
267
 
272
268
  # Add a new Stripe plan to the subscription.
273
269
  def add_plan(plan, quantity = 1, options = {})
274
- self.guard_against_incomplete
270
+ guard_against_incomplete
275
271
 
276
- if self.items.any? { |item| item.stripe_plan == plan }
277
- raise Reji::SubscriptionUpdateFailureError::duplicate_plan(self, plan)
272
+ if items.any? { |item| item.stripe_plan == plan }
273
+ raise Reji::SubscriptionUpdateFailureError.duplicate_plan(self, plan)
278
274
  end
279
275
 
280
- subscription = self.as_stripe_subscription
276
+ subscription = as_stripe_subscription
281
277
 
282
278
  item = subscription.items.create({
283
- :plan => plan,
284
- :quantity => quantity,
285
- :tax_rates => self.get_plan_tax_rates_for_payload(plan),
286
- :payment_behavior => self.payment_behavior,
287
- :proration_behavior => self.prorate_behavior,
279
+ plan: plan,
280
+ quantity: quantity,
281
+ tax_rates: get_plan_tax_rates_for_payload(plan),
282
+ payment_behavior: payment_behavior,
283
+ proration_behavior: proration_behavior,
288
284
  }.merge(options))
289
285
 
290
- self.items.create({
291
- :stripe_id => item.id,
292
- :stripe_plan => plan,
293
- :quantity => quantity
286
+ items.create({
287
+ stripe_id: item.id,
288
+ stripe_plan: plan,
289
+ quantity: quantity,
294
290
  })
295
291
 
296
- if self.has_single_plan
297
- self.update({
298
- :stripe_plan => nil,
299
- :quantity => nil,
292
+ if single_plan?
293
+ update({
294
+ stripe_plan: nil,
295
+ quantity: nil,
300
296
  })
301
297
  end
302
298
 
@@ -305,29 +301,29 @@ module Reji
305
301
 
306
302
  # Add a new Stripe plan to the subscription, and invoice immediately.
307
303
  def add_plan_and_invoice(plan, quantity = 1, options = {})
308
- self.always_invoice
304
+ always_invoice
309
305
 
310
- self.add_plan(plan, quantity, options)
306
+ add_plan(plan, quantity, options)
311
307
  end
312
308
 
313
309
  # Remove a Stripe plan from the subscription.
314
310
  def remove_plan(plan)
315
- raise Reji::SubscriptionUpdateFailureError::cannot_delete_last_plan(self) if self.has_single_plan
311
+ raise Reji::SubscriptionUpdateFailureError.cannot_delete_last_plan(self) if single_plan?
316
312
 
317
- item = self.find_item_or_fail(plan)
313
+ item = find_item_or_fail(plan)
318
314
 
319
315
  item.as_stripe_subscription_item.delete({
320
- :proration_behavior => self.prorate_behavior
316
+ proration_behavior: proration_behavior,
321
317
  })
322
318
 
323
- self.items.where(stripe_plan: plan).destroy_all
319
+ items.where(stripe_plan: plan).destroy_all
324
320
 
325
- if self.items.count < 2
326
- item = self.items.first
321
+ if items.count < 2
322
+ item = items.first
327
323
 
328
- self.update({
329
- :stripe_plan => item.stripe_plan,
330
- :quantity => quantity,
324
+ update({
325
+ stripe_plan: item.stripe_plan,
326
+ quantity: quantity,
331
327
  })
332
328
  end
333
329
 
@@ -336,7 +332,7 @@ module Reji
336
332
 
337
333
  # Cancel the subscription at the end of the billing period.
338
334
  def cancel
339
- subscription = self.as_stripe_subscription
335
+ subscription = as_stripe_subscription
340
336
 
341
337
  subscription.cancel_at_period_end = true
342
338
 
@@ -347,70 +343,62 @@ module Reji
347
343
  # If the user was on trial, we will set the grace period to end when the trial
348
344
  # would have ended. Otherwise, we'll retrieve the end of the billing period
349
345
  # period and make that the end of the grace period for this current user.
350
- if self.on_trial
351
- self.ends_at = self.trial_ends_at
352
- else
353
- self.ends_at = Time.at(subscription.current_period_end)
354
- end
346
+ self.ends_at = on_trial ? trial_ends_at : Time.zone.at(subscription.current_period_end)
355
347
 
356
- self.save
348
+ save
357
349
 
358
350
  self
359
351
  end
360
352
 
361
353
  # Cancel the subscription immediately.
362
354
  def cancel_now
363
- self.as_stripe_subscription.cancel({
364
- :prorate => self.prorate_behavior == 'create_prorations',
355
+ as_stripe_subscription.cancel({
356
+ prorate: proration_behavior == 'create_prorations',
365
357
  })
366
358
 
367
- self.mark_as_cancelled
359
+ mark_as_cancelled
368
360
 
369
361
  self
370
362
  end
371
363
 
372
364
  # Cancel the subscription and invoice immediately.
373
365
  def cancel_now_and_invoice
374
- self.as_stripe_subscription.cancel({
375
- :invoice_now => true,
376
- :prorate => self.prorate_behavior == 'create_prorations',
366
+ as_stripe_subscription.cancel({
367
+ invoice_now: true,
368
+ prorate: proration_behavior == 'create_prorations',
377
369
  })
378
370
 
379
- self.mark_as_cancelled
371
+ mark_as_cancelled
380
372
 
381
373
  self
382
374
  end
383
375
 
384
376
  # Mark the subscription as cancelled.
385
377
  def mark_as_cancelled
386
- self.update({
387
- :stripe_status => 'canceled',
388
- :ends_at => Time.now,
378
+ update({
379
+ stripe_status: 'canceled',
380
+ ends_at: Time.current,
389
381
  })
390
382
  end
391
383
 
392
384
  # Resume the cancelled subscription.
393
385
  def resume
394
- raise ArgumentError.new('Unable to resume subscription that is not within grace period.') unless self.on_grace_period
386
+ raise ArgumentError, 'Unable to resume subscription that is not within grace period.' unless on_grace_period
395
387
 
396
- subscription = self.as_stripe_subscription
388
+ subscription = as_stripe_subscription
397
389
 
398
390
  subscription.cancel_at_period_end = false
399
391
 
400
- if self.on_trial
401
- subscription.trial_end = Time.at(self.trial_ends_at).to_i
402
- else
403
- subscription.trial_end = 'now'
404
- end
392
+ subscription.trial_end = on_trial ? Time.zone.at(trial_ends_at).to_i : 'now'
405
393
 
406
394
  subscription = subscription.save
407
395
 
408
396
  # Finally, we will remove the ending timestamp from the user's record in the
409
397
  # local database to indicate that the subscription is active again and is
410
398
  # no longer "cancelled". Then we will save this record in the database.
411
- self.update({
412
- :stripe_status => subscription.status,
413
- :ends_at => nil,
399
+ update({
400
+ stripe_status: subscription.status,
401
+ ends_at: nil,
414
402
  })
415
403
 
416
404
  self
@@ -418,51 +406,49 @@ module Reji
418
406
 
419
407
  # Determine if the subscription has pending updates.
420
408
  def pending
421
- ! self.as_stripe_subscription.pending_update.nil?
409
+ !as_stripe_subscription.pending_update.nil?
422
410
  end
423
411
 
424
412
  # Invoice the subscription outside of the regular billing cycle.
425
413
  def invoice(options = {})
426
- begin
427
- self.user.invoice(options.merge({
428
- :subscription => self.stripe_id
429
- }))
430
- rescue IncompletePaymentError => e
431
- # Set the new Stripe subscription status immediately when payment fails...
432
- self.update(stripe_status: e.payment.invoice.subscription.status)
433
-
434
- raise e
435
- end
414
+ user.invoice(options.merge({
415
+ subscription: stripe_id,
416
+ }))
417
+ rescue IncompletePaymentError => e
418
+ # Set the new Stripe subscription status immediately when payment fails...
419
+ update(stripe_status: e.payment.invoice.subscription.status)
420
+
421
+ raise e
436
422
  end
437
423
 
438
424
  # Get the latest invoice for the subscription.
439
425
  def latest_invoice
440
- stripe_subscription = self.as_stripe_subscription(['latest_invoice'])
426
+ stripe_subscription = as_stripe_subscription(['latest_invoice'])
441
427
 
442
- Invoice.new(self.user, stripe_subscription.latest_invoice)
428
+ Invoice.new(user, stripe_subscription.latest_invoice)
443
429
  end
444
430
 
445
431
  # Sync the tax percentage of the user to the subscription.
446
432
  def sync_tax_percentage
447
- subscription = self.as_stripe_subscription
433
+ subscription = as_stripe_subscription
448
434
 
449
- subscription.tax_percentage = self.user.tax_percentage
435
+ subscription.tax_percentage = user.tax_percentage
450
436
 
451
437
  subscription.save
452
438
  end
453
439
 
454
440
  # Sync the tax rates of the user to the subscription.
455
441
  def sync_tax_rates
456
- subscription = self.as_stripe_subscription
442
+ subscription = as_stripe_subscription
457
443
 
458
- subscription.default_tax_rates = self.user.tax_rates
444
+ subscription.default_tax_rates = user.tax_rates
459
445
 
460
446
  subscription.save
461
447
 
462
- self.items.each do |item|
448
+ items.each do |item|
463
449
  stripe_subscription_item = item.as_stripe_subscription_item
464
450
 
465
- stripe_subscription_item.tax_rates = self.get_plan_tax_rates_for_payload(item.stripe_plan)
451
+ stripe_subscription_item.tax_rates = get_plan_tax_rates_for_payload(item.stripe_plan)
466
452
 
467
453
  stripe_subscription_item.save
468
454
  end
@@ -470,21 +456,19 @@ module Reji
470
456
 
471
457
  # Get the plan tax rates for the Stripe payload.
472
458
  def get_plan_tax_rates_for_payload(plan)
473
- tax_rates = self.user.plan_tax_rates
459
+ tax_rates = user.plan_tax_rates
474
460
 
475
- if tax_rates
476
- tax_rates.key?(plan) ? tax_rates[plan] : nil
477
- end
461
+ tax_rates[plan] || nil unless tax_rates.empty?
478
462
  end
479
463
 
480
464
  # Determine if the subscription has an incomplete payment.
481
- def has_incomplete_payment
482
- self.past_due || self.incomplete
465
+ def incomplete_payment?
466
+ past_due || incomplete
483
467
  end
484
468
 
485
469
  # Get the latest payment for a Subscription.
486
470
  def latest_payment
487
- payment_intent = self.as_stripe_subscription(['latest_invoice.payment_intent'])
471
+ payment_intent = as_stripe_subscription(['latest_invoice.payment_intent'])
488
472
  .latest_invoice
489
473
  .payment_intent
490
474
 
@@ -493,64 +477,62 @@ module Reji
493
477
 
494
478
  # Make sure a subscription is not incomplete when performing changes.
495
479
  def guard_against_incomplete
496
- raise Reji::SubscriptionUpdateFailureError.incomplete_subscription(self) if self.incomplete
480
+ raise Reji::SubscriptionUpdateFailureError.incomplete_subscription(self) if incomplete
497
481
  end
498
482
 
499
483
  # Make sure a plan argument is provided when the subscription is a multi plan subscription.
500
484
  def guard_against_multiple_plans
501
- raise ArgumentError.new('This method requires a plan argument since the subscription has multiple plans.') if self.has_multiple_plans
485
+ return unless multiple_plans?
486
+
487
+ raise ArgumentError, 'This method requires a plan argument since the subscription has multiple plans.'
502
488
  end
503
489
 
504
490
  # Update the underlying Stripe subscription information for the model.
505
491
  def update_stripe_subscription(options = {})
506
492
  Stripe::Subscription.update(
507
- self.stripe_id, options, self.owner.stripe_options
493
+ stripe_id, options, owner.stripe_options
508
494
  )
509
495
  end
510
496
 
511
497
  # Get the subscription as a Stripe subscription object.
512
498
  def as_stripe_subscription(expand = {})
513
- Stripe::Subscription::retrieve(
514
- {:id => self.stripe_id, :expand => expand}, self.owner.stripe_options
499
+ Stripe::Subscription.retrieve(
500
+ { id: stripe_id, expand: expand }, owner.stripe_options
515
501
  )
516
502
  end
517
503
 
518
- protected
519
-
520
504
  # Parse the given plans for a swap operation.
521
- def parse_swap_plans(plans)
522
- plans.map {
523
- |plan| [plan, {
524
- :plan => plan,
525
- :tax_rates => self.get_plan_tax_rates_for_payload(plan)
526
- }]
527
- }.to_h
505
+ protected def parse_swap_plans(plans)
506
+ plans.map do |plan|
507
+ [plan, {
508
+ plan: plan,
509
+ tax_rates: get_plan_tax_rates_for_payload(plan),
510
+ },]
511
+ end.to_h
528
512
  end
529
513
 
530
514
  # Merge the items that should be deleted during swap into the given items collection.
531
- def merge_items_that_should_be_deleted_during_swap(items)
532
- self.as_stripe_subscription.items.data.each do |stripe_subscription_item|
515
+ protected def merge_items_that_should_be_deleted_during_swap(items)
516
+ as_stripe_subscription.items.data.each do |stripe_subscription_item|
533
517
  plan = stripe_subscription_item.plan.id
534
518
 
535
519
  item = items.key?(plan) ? items[plan] : {}
536
520
 
537
- if item.empty?
538
- item[:deleted] = true
539
- end
521
+ item[:deleted] = true if item.empty?
540
522
 
541
- items[plan] = item.merge({:id => stripe_subscription_item.id})
523
+ items[plan] = item.merge({ id: stripe_subscription_item.id })
542
524
  end
543
525
 
544
526
  items
545
527
  end
546
528
 
547
529
  # Get the options array for a swap operation.
548
- def get_swap_options(items, options)
530
+ protected def get_swap_options(items, options)
549
531
  payload = {
550
- :items => items.values,
551
- :payment_behavior => self.payment_behavior,
552
- :proration_behavior => self.prorate_behavior,
553
- :expand => ['latest_invoice.payment_intent'],
532
+ items: items.values,
533
+ payment_behavior: payment_behavior,
534
+ proration_behavior: proration_behavior,
535
+ expand: ['latest_invoice.payment_intent'],
554
536
  }
555
537
 
556
538
  payload[:cancel_at_period_end] = false if payload[:payment_behavior] != 'pending_if_incomplete'
@@ -559,7 +541,7 @@ module Reji
559
541
 
560
542
  payload[:billing_cycle_anchor] = @billing_cycle_anchor unless @billing_cycle_anchor.nil?
561
543
 
562
- payload[:trial_end] = self.on_trial ? self.trial_ends_at : 'now'
544
+ payload[:trial_end] = on_trial ? trial_ends_at : 'now'
563
545
 
564
546
  payload
565
547
  end