reji 1.0.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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +14 -0
  3. data/.gitattributes +4 -0
  4. data/.gitignore +15 -0
  5. data/.travis.yml +28 -0
  6. data/Appraisals +17 -0
  7. data/Gemfile +8 -0
  8. data/Gemfile.lock +133 -0
  9. data/LICENSE +20 -0
  10. data/README.md +1285 -0
  11. data/Rakefile +21 -0
  12. data/app/controllers/reji/payment_controller.rb +31 -0
  13. data/app/controllers/reji/webhook_controller.rb +170 -0
  14. data/app/views/payment.html.erb +228 -0
  15. data/app/views/receipt.html.erb +250 -0
  16. data/bin/setup +12 -0
  17. data/config/routes.rb +6 -0
  18. data/gemfiles/rails_5.0.gemfile +13 -0
  19. data/gemfiles/rails_5.1.gemfile +13 -0
  20. data/gemfiles/rails_5.2.gemfile +13 -0
  21. data/gemfiles/rails_6.0.gemfile +13 -0
  22. data/lib/generators/reji/install/install_generator.rb +69 -0
  23. data/lib/generators/reji/install/templates/db/migrate/add_reji_to_users.rb.erb +16 -0
  24. data/lib/generators/reji/install/templates/db/migrate/create_subscription_items.rb.erb +19 -0
  25. data/lib/generators/reji/install/templates/db/migrate/create_subscriptions.rb.erb +22 -0
  26. data/lib/generators/reji/install/templates/reji.rb +36 -0
  27. data/lib/reji.rb +75 -0
  28. data/lib/reji/billable.rb +13 -0
  29. data/lib/reji/concerns/interacts_with_payment_behavior.rb +33 -0
  30. data/lib/reji/concerns/manages_customer.rb +113 -0
  31. data/lib/reji/concerns/manages_invoices.rb +136 -0
  32. data/lib/reji/concerns/manages_payment_methods.rb +202 -0
  33. data/lib/reji/concerns/manages_subscriptions.rb +91 -0
  34. data/lib/reji/concerns/performs_charges.rb +36 -0
  35. data/lib/reji/concerns/prorates.rb +38 -0
  36. data/lib/reji/configuration.rb +59 -0
  37. data/lib/reji/engine.rb +4 -0
  38. data/lib/reji/errors.rb +66 -0
  39. data/lib/reji/invoice.rb +243 -0
  40. data/lib/reji/invoice_line_item.rb +98 -0
  41. data/lib/reji/payment.rb +61 -0
  42. data/lib/reji/payment_method.rb +32 -0
  43. data/lib/reji/subscription.rb +567 -0
  44. data/lib/reji/subscription_builder.rb +206 -0
  45. data/lib/reji/subscription_item.rb +97 -0
  46. data/lib/reji/tax.rb +48 -0
  47. data/lib/reji/version.rb +5 -0
  48. data/reji.gemspec +32 -0
  49. data/spec/dummy/app/models/user.rb +21 -0
  50. data/spec/dummy/application.rb +53 -0
  51. data/spec/dummy/config/database.yml +11 -0
  52. data/spec/dummy/db/schema.rb +40 -0
  53. data/spec/feature/charges_spec.rb +67 -0
  54. data/spec/feature/customer_spec.rb +23 -0
  55. data/spec/feature/invoices_spec.rb +73 -0
  56. data/spec/feature/multiplan_subscriptions_spec.rb +319 -0
  57. data/spec/feature/payment_methods_spec.rb +149 -0
  58. data/spec/feature/pending_updates_spec.rb +77 -0
  59. data/spec/feature/subscriptions_spec.rb +650 -0
  60. data/spec/feature/webhooks_spec.rb +162 -0
  61. data/spec/spec_helper.rb +27 -0
  62. data/spec/support/feature_helpers.rb +39 -0
  63. data/spec/unit/customer_spec.rb +54 -0
  64. data/spec/unit/invoice_line_item_spec.rb +72 -0
  65. data/spec/unit/invoice_spec.rb +192 -0
  66. data/spec/unit/payment_spec.rb +33 -0
  67. data/spec/unit/subscription_spec.rb +103 -0
  68. metadata +237 -0
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'payment_methods', type: :request do
6
+ it 'can_start_a_new_setup_intent_session' do
7
+ user = create_customer('we_can_start_a_new_setup_intent_session')
8
+ setup_intent = user.create_setup_intent
9
+ expect(setup_intent).to be_an_instance_of(Stripe::SetupIntent)
10
+ end
11
+
12
+ it 'can_add_payment_methods' do
13
+ user = create_customer('we_can_add_payment_methods')
14
+ user.create_as_stripe_customer
15
+
16
+ payment_method = user.add_payment_method('pm_card_visa')
17
+
18
+ expect(payment_method).to be_an_instance_of(Reji::PaymentMethod)
19
+ expect(payment_method.card.brand).to eq('visa')
20
+ expect(payment_method.card.last4).to eq('4242')
21
+ expect(user.has_payment_method).to be true
22
+ expect(user.has_default_payment_method).to be false
23
+ end
24
+
25
+ it 'can_remove_payment_methods' do
26
+ user = create_customer('we_can_remove_payment_methods')
27
+ user.create_as_stripe_customer
28
+
29
+ payment_method = user.add_payment_method('pm_card_visa')
30
+
31
+ expect(user.payment_methods.count).to eq(1)
32
+ expect(user.has_payment_method).to be true
33
+
34
+ user.remove_payment_method(payment_method.as_stripe_payment_method)
35
+
36
+ expect(user.payment_methods.count).to eq(0)
37
+ expect(user.has_payment_method).to be false
38
+ end
39
+
40
+ it 'can_remove_the_default_payment_method' do
41
+ user = create_customer('we_can_remove_the_default_payment_method')
42
+ user.create_as_stripe_customer
43
+
44
+ payment_method = user.update_default_payment_method('pm_card_visa')
45
+
46
+ expect(user.payment_methods.count).to eq(1)
47
+ expect(user.has_payment_method).to be true
48
+ expect(user.has_default_payment_method).to be true
49
+
50
+ user.remove_payment_method(payment_method.as_stripe_payment_method)
51
+
52
+ expect(user.payment_methods.count).to eq(0)
53
+ expect(user.default_payment_method).to be_nil
54
+ expect(user.card_brand).to be_nil
55
+ expect(user.card_last_four).to be_nil
56
+ expect(user.has_payment_method).to be false
57
+ expect(user.has_default_payment_method).to be false
58
+ end
59
+
60
+ it 'can_set_a_default_payment_method' do
61
+ user = create_customer('we_can_set_a_default_payment_method')
62
+ user.create_as_stripe_customer
63
+
64
+ payment_method = user.update_default_payment_method('pm_card_visa')
65
+
66
+ expect(payment_method).to be_an_instance_of(Reji::PaymentMethod)
67
+ expect(payment_method.card.brand).to eq('visa')
68
+ expect(payment_method.card.last4).to eq('4242')
69
+ expect(user.has_default_payment_method).to be true
70
+
71
+ payment_method = user.default_payment_method
72
+
73
+ expect(payment_method).to be_an_instance_of(Reji::PaymentMethod)
74
+ expect(payment_method.card.brand).to eq('visa')
75
+ expect(payment_method.card.last4).to eq('4242')
76
+ end
77
+
78
+ it 'can_retrieve_an_old_default_source_as_a_default_payment_method' do
79
+ user = create_customer('we_can_retrieve_an_old_default_source_as_a_default_payment_method')
80
+ customer = user.create_as_stripe_customer
81
+ card = Stripe::Customer.create_source(customer.id, {:source => 'tok_visa'}, user.stripe_options)
82
+ customer.default_source = card.id
83
+ customer.save
84
+
85
+ payment_method = user.default_payment_method
86
+
87
+ expect(payment_method).to be_an_instance_of(Stripe::Card)
88
+ expect(payment_method.brand).to eq('Visa')
89
+ expect(payment_method.last4).to eq('4242')
90
+ end
91
+
92
+ it 'can_retrieve_all_payment_methods' do
93
+ user = create_customer('we_can_retrieve_all_payment_methods')
94
+ customer = user.create_as_stripe_customer
95
+
96
+ payment_method = Stripe::PaymentMethod.retrieve('pm_card_visa', user.stripe_options)
97
+ payment_method.attach({:customer => customer.id})
98
+
99
+ payment_method = Stripe::PaymentMethod.retrieve('pm_card_mastercard', user.stripe_options)
100
+ payment_method.attach({:customer => customer.id})
101
+
102
+ payment_methods = user.payment_methods
103
+
104
+ expect(payment_methods.count).to eq(2)
105
+ expect(payment_methods.first.card.brand).to eq('mastercard')
106
+ expect(payment_methods.last.card.brand).to eq('visa')
107
+ end
108
+
109
+ it 'can_sync_the_payment_method_from_stripe' do
110
+ user = create_customer('we_can_sync_the_payment_method_from_stripe')
111
+ customer = user.create_as_stripe_customer
112
+
113
+ payment_method = Stripe::PaymentMethod.retrieve('pm_card_visa', user.stripe_options)
114
+ payment_method.attach({:customer => customer.id})
115
+
116
+ customer.invoice_settings = {:default_payment_method => payment_method.id}
117
+
118
+ customer.save
119
+
120
+ expect(user.card_brand).to be_nil
121
+ expect(user.card_last_four).to be_nil
122
+
123
+ user.update_default_payment_method_from_stripe
124
+
125
+ expect(user.card_brand).to eq('visa')
126
+ expect(user.card_last_four).to eq('4242')
127
+ end
128
+
129
+ it 'delete_all_payment_methods' do
130
+ user = create_customer('we_delete_all_payment_methods')
131
+ customer = user.create_as_stripe_customer
132
+
133
+ payment_method = Stripe::PaymentMethod.retrieve('pm_card_visa', user.stripe_options)
134
+ payment_method.attach({:customer => customer.id})
135
+
136
+ payment_method = Stripe::PaymentMethod.retrieve('pm_card_mastercard', user.stripe_options)
137
+ payment_method.attach({:customer => customer.id})
138
+
139
+ payment_methods = user.payment_methods
140
+
141
+ expect(payment_methods.count).to eq(2)
142
+
143
+ user.delete_payment_methods
144
+
145
+ payment_methods = user.payment_methods
146
+
147
+ expect(payment_methods.count).to eq(0)
148
+ end
149
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'pending updates', type: :request do
6
+ before(:all) do
7
+ @product_id = "#{stripe_prefix}product-1-#{SecureRandom.hex(5)}"
8
+ @plan_id = "#{stripe_prefix}monthly-10-#{SecureRandom.hex(5)}"
9
+ @other_plan_id = "#{stripe_prefix}monthly-10-#{SecureRandom.hex(5)}"
10
+ @premium_plan_id = "#{stripe_prefix}monthly-20-premium-#{SecureRandom.hex(5)}"
11
+
12
+ Stripe::Product.create({
13
+ :id => @product_id,
14
+ :name => 'Rails Reji Test Product',
15
+ :type => 'service',
16
+ })
17
+
18
+ Stripe::Plan.create({
19
+ :id => @plan_id,
20
+ :nickname => 'Monthly $10',
21
+ :currency => 'USD',
22
+ :interval => 'month',
23
+ :billing_scheme => 'per_unit',
24
+ :amount => 1000,
25
+ :product => @product_id,
26
+ })
27
+
28
+ Stripe::Plan.create({
29
+ :id => @other_plan_id,
30
+ :nickname => 'Monthly $10 Other',
31
+ :currency => 'USD',
32
+ :interval => 'month',
33
+ :billing_scheme => 'per_unit',
34
+ :amount => 1000,
35
+ :product => @product_id,
36
+ })
37
+
38
+ Stripe::Plan.create({
39
+ :id => @premium_plan_id,
40
+ :nickname => 'Monthly $20 Premium',
41
+ :currency => 'USD',
42
+ :interval => 'month',
43
+ :billing_scheme => 'per_unit',
44
+ :amount => 2000,
45
+ :product => @product_id,
46
+ })
47
+ end
48
+
49
+ after(:all) do
50
+ delete_stripe_resource(Stripe::Plan.retrieve(@plan_id))
51
+ delete_stripe_resource(Stripe::Plan.retrieve(@other_plan_id))
52
+ delete_stripe_resource(Stripe::Plan.retrieve(@premium_plan_id))
53
+ delete_stripe_resource(Stripe::Product.retrieve(@product_id))
54
+ end
55
+
56
+ it 'test_subscription_can_error_if_incomplete' do
57
+ user = create_customer('subscription_can_error_if_incomplete')
58
+
59
+ subscription = user.new_subscription('main', @plan_id).create('pm_card_visa')
60
+
61
+ # Set a faulty card as the customer's default payment method.
62
+ user.update_default_payment_method('pm_card_threeDSecure2Required')
63
+
64
+ begin
65
+ # Attempt to swap and pay with a faulty card.
66
+ subscription = subscription.error_if_payment_fails.swap_and_invoice(@premium_plan_id)
67
+
68
+ raise RSpec::Expectations::ExpectationNotMetError.new('Expected exception PaymentFailureError was not thrown.')
69
+ rescue Stripe::CardError => e
70
+ # Assert that the plan was not swapped.
71
+ expect(subscription.stripe_plan).to eq(@plan_id)
72
+
73
+ # Assert subscription is active.
74
+ expect(subscription.active).to be true
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,650 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'subscriptions', type: :request do
6
+ before(:all) do
7
+ @product_id = "#{stripe_prefix}product-1-#{SecureRandom.hex(5)}"
8
+ @plan_id = "#{stripe_prefix}monthly-10-#{SecureRandom.hex(5)}"
9
+ @other_plan_id = "#{stripe_prefix}monthly-10-#{SecureRandom.hex(5)}"
10
+ @premium_plan_id = "#{stripe_prefix}monthly-20-premium-#{SecureRandom.hex(5)}"
11
+ @coupon_id = "#{stripe_prefix}coupon-#{SecureRandom.hex(5)}"
12
+
13
+ Stripe::Product.create({
14
+ :id => @product_id,
15
+ :name => 'Rails Reji Test Product',
16
+ :type => 'service',
17
+ })
18
+
19
+ Stripe::Plan.create({
20
+ :id => @plan_id,
21
+ :nickname => 'Monthly $10',
22
+ :currency => 'USD',
23
+ :interval => 'month',
24
+ :billing_scheme => 'per_unit',
25
+ :amount => 1000,
26
+ :product => @product_id,
27
+ })
28
+
29
+ Stripe::Plan.create({
30
+ :id => @other_plan_id,
31
+ :nickname => 'Monthly $10 Other',
32
+ :currency => 'USD',
33
+ :interval => 'month',
34
+ :billing_scheme => 'per_unit',
35
+ :amount => 1000,
36
+ :product => @product_id,
37
+ })
38
+
39
+ Stripe::Plan.create({
40
+ :id => @premium_plan_id,
41
+ :nickname => 'Monthly $20 Premium',
42
+ :currency => 'USD',
43
+ :interval => 'month',
44
+ :billing_scheme => 'per_unit',
45
+ :amount => 2000,
46
+ :product => @product_id,
47
+ })
48
+
49
+ Stripe::Coupon.create({
50
+ :id => @coupon_id,
51
+ :duration => 'repeating',
52
+ :amount_off => 500,
53
+ :duration_in_months => 3,
54
+ :currency => 'USD',
55
+ })
56
+
57
+ @tax_rate_id = Stripe::TaxRate.create({
58
+ :display_name => 'VAT',
59
+ :description => 'VAT Belgium',
60
+ :jurisdiction => 'BE',
61
+ :percentage => 21,
62
+ :inclusive => false,
63
+ }).id
64
+ end
65
+
66
+ after(:all) do
67
+ delete_stripe_resource(Stripe::Plan.retrieve(@plan_id))
68
+ delete_stripe_resource(Stripe::Plan.retrieve(@other_plan_id))
69
+ delete_stripe_resource(Stripe::Plan.retrieve(@premium_plan_id))
70
+ delete_stripe_resource(Stripe::Product.retrieve(@product_id))
71
+ delete_stripe_resource(Stripe::Coupon.retrieve(@coupon_id))
72
+ end
73
+
74
+ it 'test_subscriptions_can_be_created' do
75
+ user = create_customer('subscriptions_can_be_created')
76
+
77
+ # Create Subscription
78
+ user.new_subscription('main', @plan_id).create('pm_card_visa')
79
+
80
+ expect(user.subscriptions.count).to eq(1)
81
+ expect(user.subscription('main').stripe_id).to_not be_nil
82
+
83
+ expect(user.subscribed('main')).to be true
84
+ expect(user.subscribed_to_plan(@plan_id, 'main')).to be true
85
+ expect(user.subscribed_to_plan(@plan_id, 'something')).to be false
86
+ expect(user.subscribed_to_plan(@other_plan_id, 'main')).to be false
87
+ expect(user.subscribed('main', @plan_id)).to be true
88
+ expect(user.subscribed('main', @other_plan_id)).to be false
89
+ expect(user.subscription('main').active).to be true
90
+ expect(user.subscription('main').cancelled).to be false
91
+ expect(user.subscription('main').on_grace_period).to be false
92
+ expect(user.subscription('main').recurring).to be true
93
+ expect(user.subscription('main').ended).to be false
94
+
95
+ # Cancel Subscription
96
+ subscription = user.subscription('main')
97
+ subscription.cancel
98
+
99
+ expect(subscription.active).to be true
100
+ expect(subscription.cancelled).to be true
101
+ expect(subscription.on_grace_period).to be true
102
+ expect(subscription.recurring).to be false
103
+ expect(subscription.ended).to be false
104
+
105
+ # Modify Ends Date To Past
106
+ old_grace_period = subscription.ends_at
107
+ subscription.update({:ends_at => Time.now - 5.days})
108
+
109
+ expect(subscription.active).to be false
110
+ expect(subscription.cancelled).to be true
111
+ expect(subscription.on_grace_period).to be false
112
+ expect(subscription.recurring).to be false
113
+ expect(subscription.ended).to be true
114
+
115
+ subscription.update({:ends_at => old_grace_period})
116
+
117
+ # Resume Subscription
118
+ subscription.resume
119
+
120
+ expect(subscription.active).to be true
121
+ expect(subscription.cancelled).to be false
122
+ expect(subscription.on_grace_period).to be false
123
+ expect(subscription.recurring).to be true
124
+ expect(subscription.ended).to be false
125
+
126
+ # Increment & Decrement
127
+ subscription.increment_quantity
128
+
129
+ expect(subscription.quantity).to eq(2)
130
+
131
+ subscription.decrement_quantity
132
+
133
+ expect(subscription.quantity).to eq(1)
134
+
135
+ # Swap Plan and invoice immediately.
136
+ subscription.swap_and_invoice(@other_plan_id)
137
+
138
+ expect(subscription.stripe_plan).to eq(@other_plan_id)
139
+
140
+ # Invoice Tests
141
+ invoice = user.invoices.last
142
+
143
+ expect(invoice.total).to eq('$10.00')
144
+ expect(invoice.has_discount).to be false
145
+ expect(invoice.has_starting_balance).to be false
146
+ expect(invoice.coupon).to be_nil
147
+ end
148
+
149
+ it 'test_swapping_subscription_with_coupon' do
150
+ user = create_customer('swapping_subscription_with_coupon')
151
+ user.new_subscription('main', @plan_id).create('pm_card_visa')
152
+ subscription = user.subscription('main')
153
+
154
+ subscription.swap(@other_plan_id, {:coupon => @coupon_id})
155
+
156
+ expect(subscription.as_stripe_subscription.discount.coupon.id).to eq(@coupon_id)
157
+ end
158
+
159
+ it 'test_declined_card_during_subscribing_results_in_an_exception' do
160
+ user = create_customer('declined_card_during_subscribing_results_in_an_exception')
161
+
162
+ begin
163
+ user.new_subscription('main', @plan_id).create('pm_card_chargeCustomerFail')
164
+
165
+ raise RSpec::Expectations::ExpectationNotMetError.new('Expected exception PaymentFailureError was not thrown.')
166
+ rescue Reji::PaymentFailureError => e
167
+ # Assert that the payment needs a valid payment method.
168
+ expect(e.payment.requires_payment_method).to be true
169
+
170
+ # Assert subscription was added to the billable entity.
171
+ subscription = user.subscription('main')
172
+ expect(subscription).to be_an_instance_of Reji::Subscription
173
+
174
+ # Assert subscription is incomplete.
175
+ expect(subscription.incomplete).to be true
176
+ end
177
+ end
178
+
179
+ it 'test_next_action_needed_during_subscribing_results_in_an_exception' do
180
+ user = create_customer('next_action_needed_during_subscribing_results_in_an_exception')
181
+
182
+ begin
183
+ user.new_subscription('main', @plan_id).create('pm_card_threeDSecure2Required')
184
+
185
+ raise RSpec::Expectations::ExpectationNotMetError.new('Expected exception PaymentActionRequiredError was not thrown.')
186
+ rescue Reji::PaymentActionRequiredError => e
187
+ # Assert that the payment needs an extra action.
188
+ expect(e.payment.requires_action).to be true
189
+
190
+ # Assert subscription was added to the billable entity.
191
+ subscription = user.subscription('main')
192
+ expect(subscription).to be_an_instance_of Reji::Subscription
193
+
194
+ # Assert subscription is incomplete.
195
+ expect(subscription.incomplete).to be true
196
+ end
197
+ end
198
+
199
+ it 'test_declined_card_during_plan_swap_results_in_an_exception' do
200
+ user = create_customer('declined_card_during_plan_swap_results_in_an_exception')
201
+
202
+ subscription = user.new_subscription('main', @plan_id).create('pm_card_visa')
203
+
204
+ # Set a faulty card as the customer's default payment method.
205
+ user.update_default_payment_method('pm_card_chargeCustomerFail')
206
+
207
+ begin
208
+ # Attempt to swap and pay with a faulty card.
209
+ subscription = subscription.swap_and_invoice(@premium_plan_id)
210
+
211
+ raise RSpec::Expectations::ExpectationNotMetError.new('Expected exception PaymentFailureError was not thrown.')
212
+ rescue Reji::PaymentFailureError => e
213
+ # Assert that the payment needs a valid payment method.
214
+ expect(e.payment.requires_payment_method).to be true
215
+
216
+ # Assert that the plan was swapped anyway.
217
+ expect(subscription.stripe_plan).to eq(@premium_plan_id)
218
+
219
+ # Assert subscription is past due.
220
+ expect(subscription.past_due).to be true
221
+ end
222
+ end
223
+
224
+ it 'test_next_action_needed_during_plan_swap_results_in_an_exception' do
225
+ user = create_customer('next_action_needed_during_plan_swap_results_in_an_exception')
226
+
227
+ subscription = user.new_subscription('main', @plan_id).create('pm_card_visa')
228
+
229
+ # Set a card that requires a next action as the customer's default payment method.
230
+ user.update_default_payment_method('pm_card_threeDSecure2Required')
231
+
232
+ begin
233
+ # Attempt to swap and pay with a faulty card.
234
+ subscription = subscription.swap_and_invoice(@premium_plan_id)
235
+
236
+ raise RSpec::Expectations::ExpectationNotMetError.new('Expected exception PaymentActionRequiredError was not thrown.')
237
+ rescue Reji::PaymentActionRequiredError => e
238
+ # Assert that the payment needs an extra action.
239
+ expect(e.payment.requires_action).to be true
240
+
241
+ # Assert that the plan was swapped anyway.
242
+ expect(subscription.stripe_plan).to eq(@premium_plan_id)
243
+
244
+ # Assert subscription is past due.
245
+ expect(subscription.past_due).to be true
246
+ end
247
+ end
248
+
249
+ it 'test_downgrade_with_faulty_card_does_not_incomplete_subscription' do
250
+ user = create_customer('downgrade_with_faulty_card_does_not_incomplete_subscription')
251
+
252
+ subscription = user.new_subscription('main', @premium_plan_id).create('pm_card_visa')
253
+
254
+ # Set a card that requires a next action as the customer's default payment method.
255
+ user.update_default_payment_method('pm_card_chargeCustomerFail')
256
+
257
+ # Attempt to swap and pay with a faulty card.
258
+ subscription = subscription.swap(@plan_id)
259
+
260
+ # Assert that the plan was swapped anyway.
261
+ expect(subscription.stripe_plan).to eq(@plan_id)
262
+
263
+ # Assert subscription is still active.
264
+ expect(subscription.active).to be true
265
+ end
266
+
267
+ it 'test_downgrade_with_3d_secure_does_not_incomplete_subscription' do
268
+ user = create_customer('downgrade_with_3d_secure_does_not_incomplete_subscription')
269
+
270
+ subscription = user.new_subscription('main', @premium_plan_id).create('pm_card_visa')
271
+
272
+ # Set a card that requires a next action as the customer's default payment method.
273
+ user.update_default_payment_method('pm_card_threeDSecure2Required')
274
+
275
+ # Attempt to swap and pay with a faulty card.
276
+ subscription = subscription.swap(@plan_id)
277
+
278
+ # Assert that the plan was swapped anyway.
279
+ expect(subscription.stripe_plan).to eq(@plan_id)
280
+
281
+ # Assert subscription is still active.
282
+ expect(subscription.active).to be true
283
+ end
284
+
285
+ it 'test_creating_subscription_with_coupons' do
286
+ user = create_customer('creating_subscription_with_coupons')
287
+
288
+ # Create Subscription
289
+ user.new_subscription('main', @plan_id)
290
+ .with_coupon(@coupon_id)
291
+ .create('pm_card_visa')
292
+
293
+ subscription = user.subscription('main')
294
+
295
+ expect(user.subscribed('main')).to be true
296
+ expect(user.subscribed('main', @plan_id)).to be true
297
+ expect(user.subscribed('main', @other_plan_id)).to be false
298
+ expect(subscription.active).to be true
299
+ expect(subscription.cancelled).to be false
300
+ expect(subscription.on_grace_period).to be false
301
+ expect(subscription.recurring).to be true
302
+ expect(subscription.ended).to be false
303
+
304
+ # Invoice Tests
305
+ invoice = user.invoices.first
306
+
307
+ expect(invoice.has_discount).to be true
308
+ expect(invoice.total).to eq('$5.00')
309
+ expect(invoice.amount_off).to eq('$5.00')
310
+ expect(invoice.discount_is_percentage).to be false
311
+ end
312
+
313
+ it 'test_creating_subscription_with_an_anchored_billing_cycle' do
314
+ user = create_customer('creating_subscription_with_an_anchored_billing_cycle')
315
+
316
+ # Create Subscription
317
+ user.new_subscription('main', @plan_id)
318
+ .anchor_billing_cycle_on(Time.now.next_month.at_beginning_of_month.to_i)
319
+ .create('pm_card_visa')
320
+
321
+ subscription = user.subscription('main')
322
+
323
+ expect(user.subscribed('main')).to be true
324
+ expect(user.subscribed('main', @plan_id)).to be true
325
+ expect(user.subscribed('main', @other_plan_id)).to be false
326
+ expect(subscription.active).to be true
327
+ expect(subscription.cancelled).to be false
328
+ expect(subscription.on_grace_period).to be false
329
+ expect(subscription.recurring).to be true
330
+ expect(subscription.ended).to be false
331
+
332
+ # Invoice Tests
333
+ invoice = user.invoices.first
334
+ invoice_period = invoice.invoice_items.first.period
335
+
336
+ expect(Time.at(invoice_period.start).strftime('%Y-%m-%d')).to eq(Time.now.strftime('%Y-%m-%d'))
337
+ expect(Time.at(invoice_period.end).strftime('%Y-%m-%d')).to eq(Time.now.next_month.at_beginning_of_month.strftime('%Y-%m-%d'))
338
+ end
339
+
340
+ it 'test_creating_subscription_with_trial' do
341
+ user = create_customer('creating_subscription_with_trial')
342
+
343
+ # Create Subscription
344
+ user.new_subscription('main', @plan_id)
345
+ .trial_days(7)
346
+ .create('pm_card_visa')
347
+
348
+ subscription = user.subscription('main')
349
+
350
+ expect(subscription.active).to be true
351
+ expect(subscription.on_trial).to be true
352
+ expect(subscription.recurring).to be false
353
+ expect(subscription.ended).to be false
354
+ expect(Time.at(subscription.trial_ends_at).strftime('%Y-%m-%d')).to eq((Time.now + 7.days).strftime('%Y-%m-%d'))
355
+
356
+ # Cancel Subscription
357
+ subscription.cancel
358
+
359
+ expect(subscription.active).to be true
360
+ expect(subscription.on_grace_period).to be true
361
+ expect(subscription.recurring).to be false
362
+ expect(subscription.ended).to be false
363
+
364
+ # Resume Subscription
365
+ subscription.resume
366
+
367
+ expect(subscription.active).to be true
368
+ expect(subscription.on_grace_period).to be false
369
+ expect(subscription.on_trial).to be true
370
+ expect(subscription.recurring).to be false
371
+ expect(subscription.ended).to be false
372
+ expect(Time.at(subscription.trial_ends_at).day).to eq((Time.now + 7.days).day)
373
+ end
374
+
375
+ it 'test_creating_subscription_with_explicit_trial' do
376
+ user = create_customer('creating_subscription_with_explicit_trial')
377
+
378
+ # Create Subscription
379
+ user.new_subscription('main', @plan_id)
380
+ .trial_until(Time.now + 1.day + 3.hours + 15.minutes)
381
+ .create('pm_card_visa')
382
+
383
+ subscription = user.subscription('main')
384
+
385
+ expect(subscription.active).to be true
386
+ expect(subscription.on_trial).to be true
387
+ expect(subscription.recurring).to be false
388
+ expect(subscription.ended).to be false
389
+ expect(Time.at(subscription.trial_ends_at).strftime('%Y-%m-%d')).to eq((Time.now + 1.day + 3.hours + 15.minutes).strftime('%Y-%m-%d'))
390
+
391
+ # Cancel Subscription
392
+ subscription.cancel
393
+
394
+ expect(subscription.active).to be true
395
+ expect(subscription.on_grace_period).to be true
396
+ expect(subscription.recurring).to be false
397
+ expect(subscription.ended).to be false
398
+
399
+ # Resume Subscription
400
+ subscription.resume
401
+
402
+ expect(subscription.active).to be true
403
+ expect(subscription.on_grace_period).to be false
404
+ expect(subscription.on_trial).to be true
405
+ expect(subscription.recurring).to be false
406
+ expect(subscription.ended).to be false
407
+ expect(Time.at(subscription.trial_ends_at).day).to eq((Time.now + 1.day + 3.hours + 15.minutes).day)
408
+ end
409
+
410
+ it 'test_subscription_changes_can_be_prorated' do
411
+ user = create_customer('subscription_changes_can_be_prorated')
412
+
413
+ subscription = user.new_subscription('main', @premium_plan_id).create('pm_card_visa')
414
+
415
+ invoice = user.invoices.first
416
+
417
+ expect(invoice.raw_total).to eq(2000)
418
+
419
+ subscription.no_prorate.swap(@plan_id)
420
+
421
+ # Assert that no new invoice was created because of no prorating.
422
+ expect(user.invoices.first.id).to eq(invoice.id)
423
+ expect(user.upcoming_invoice.raw_total).to eq(1000)
424
+
425
+ subscription.swap_and_invoice(@premium_plan_id)
426
+
427
+ # Assert that a new invoice was created because of immediate invoicing.
428
+ expect(user.invoices.first.id).not_to eq(invoice.id)
429
+ invoice = user.invoices.first
430
+ expect(invoice.raw_total).to eq(1000)
431
+ expect(user.upcoming_invoice.raw_total).to eq(2000)
432
+
433
+ subscription.prorate.swap(@plan_id)
434
+
435
+ # Get back from unused time on premium plan on next invoice.
436
+ expect(user.upcoming_invoice.raw_total).to eq(0)
437
+ end
438
+
439
+ it 'test_no_prorate_on_subscription_create' do
440
+ user = create_customer('no_prorate_on_subscription_create')
441
+
442
+ subscription = user.new_subscription('main', @plan_id)
443
+ .no_prorate
444
+ .create('pm_card_visa', {}, {
445
+ :collection_method => 'send_invoice',
446
+ :days_until_due => 30,
447
+ :backdate_start_date => (Time.now + 5.days - 1.year).beginning_of_day.to_i,
448
+ :billing_cycle_anchor => (Time.now + 5.days).beginning_of_day.to_i,
449
+ })
450
+
451
+ expect(subscription.stripe_plan).to eq(@plan_id)
452
+ expect(subscription.active).to be true
453
+
454
+ subscription = subscription.swap(@other_plan_id)
455
+
456
+ expect(subscription.stripe_plan).to eq(@other_plan_id)
457
+ expect(subscription.active).to be true
458
+ end
459
+
460
+ it 'test_swap_and_invoice_after_no_prorate_with_billing_cycle_anchor_delays_invoicing' do
461
+ user = create_customer('always_invoice_after_no_prorate')
462
+
463
+ subscription = user.new_subscription('main', @plan_id)
464
+ .no_prorate
465
+ .create('pm_card_visa', {}, {
466
+ :collection_method => 'send_invoice',
467
+ :days_until_due => 30,
468
+ :backdate_start_date => (Time.now + 5.days - 1.year).beginning_of_day.to_i,
469
+ :billing_cycle_anchor => (Time.now + 5.days).beginning_of_day.to_i,
470
+ })
471
+
472
+ expect(subscription.stripe_plan).to eq(@plan_id)
473
+ expect(user.invoices.count).to eq(0)
474
+ expect(user.upcoming_invoice.status).to eq('draft')
475
+ expect(subscription.active).to be true
476
+
477
+ subscription = subscription.swap_and_invoice(@other_plan_id)
478
+
479
+ expect(subscription.stripe_plan).to eq(@other_plan_id)
480
+ expect(user.invoices.count).to eq(0)
481
+ expect(user.upcoming_invoice.status).to eq('draft')
482
+ expect(subscription.active).to be true
483
+ end
484
+
485
+ it 'test_trials_can_be_extended' do
486
+ user = create_customer('trials_can_be_extended')
487
+
488
+ subscription = user.new_subscription('main', @plan_id).create('pm_card_visa')
489
+
490
+ expect(subscription.trial_ends_at).to be_nil
491
+
492
+ trial_ends_at = Time.now + 1.day
493
+
494
+ subscription.extend_trial(trial_ends_at)
495
+
496
+ expect(subscription.trial_ends_at).to eq(trial_ends_at)
497
+ expect(subscription.as_stripe_subscription.trial_end).to eq(trial_ends_at.to_i)
498
+ end
499
+
500
+ it 'test_applying_coupons_to_existing_customers' do
501
+ user = create_customer('applying_coupons_to_existing_customers')
502
+
503
+ user.new_subscription('main', @plan_id).create('pm_card_visa')
504
+
505
+ user.apply_coupon(@coupon_id)
506
+
507
+ customer = user.as_stripe_customer
508
+
509
+ expect(customer[:discount][:coupon][:id]).to eq(@coupon_id)
510
+ end
511
+
512
+ it 'test_subscription_state_scopes' do
513
+ user = create_customer('subscription_state_scopes')
514
+
515
+ # Start with an incomplete subscription.
516
+ subscription = user.subscriptions.create({
517
+ :name => 'yearly',
518
+ :stripe_id => 'xxxx',
519
+ :stripe_status => 'incomplete',
520
+ :stripe_plan => 'stripe-yearly',
521
+ :quantity => 1,
522
+ :trial_ends_at => nil,
523
+ :ends_at => nil,
524
+ })
525
+
526
+ # Subscription is incomplete
527
+ expect(user.subscriptions.incomplete.exists?).to be true
528
+ expect(user.subscriptions.active.exists?).to be false
529
+ expect(user.subscriptions.on_trial.exists?).to be false
530
+ expect(user.subscriptions.not_on_trial.exists?).to be true
531
+ expect(user.subscriptions.recurring.exists?).to be true
532
+ expect(user.subscriptions.cancelled.exists?).to be false
533
+ expect(user.subscriptions.not_cancelled.exists?).to be true
534
+ expect(user.subscriptions.on_grace_period.exists?).to be false
535
+ expect(user.subscriptions.not_on_grace_period.exists?).to be true
536
+ expect(user.subscriptions.ended.exists?).to be false
537
+
538
+ # Activate.
539
+ subscription.update({:stripe_status => 'active'})
540
+
541
+ expect(user.subscriptions.incomplete.exists?).to be false
542
+ expect(user.subscriptions.active.exists?).to be true
543
+ expect(user.subscriptions.on_trial.exists?).to be false
544
+ expect(user.subscriptions.not_on_trial.exists?).to be true
545
+ expect(user.subscriptions.recurring.exists?).to be true
546
+ expect(user.subscriptions.cancelled.exists?).to be false
547
+ expect(user.subscriptions.not_cancelled.exists?).to be true
548
+ expect(user.subscriptions.on_grace_period.exists?).to be false
549
+ expect(user.subscriptions.not_on_grace_period.exists?).to be true
550
+ expect(user.subscriptions.ended.exists?).to be false
551
+
552
+ # Put on trial.
553
+ subscription.update({:trial_ends_at => Time.now + 1.day})
554
+
555
+ expect(user.subscriptions.incomplete.exists?).to be false
556
+ expect(user.subscriptions.active.exists?).to be true
557
+ expect(user.subscriptions.on_trial.exists?).to be true
558
+ expect(user.subscriptions.not_on_trial.exists?).to be false
559
+ expect(user.subscriptions.recurring.exists?).to be false
560
+ expect(user.subscriptions.cancelled.exists?).to be false
561
+ expect(user.subscriptions.not_cancelled.exists?).to be true
562
+ expect(user.subscriptions.on_grace_period.exists?).to be false
563
+ expect(user.subscriptions.not_on_grace_period.exists?).to be true
564
+ expect(user.subscriptions.ended.exists?).to be false
565
+
566
+ # Put on grace period.
567
+ subscription.update({:ends_at => Time.now + 1.day})
568
+
569
+ expect(user.subscriptions.incomplete.exists?).to be false
570
+ expect(user.subscriptions.active.exists?).to be true
571
+ expect(user.subscriptions.on_trial.exists?).to be true
572
+ expect(user.subscriptions.not_on_trial.exists?).to be false
573
+ expect(user.subscriptions.recurring.exists?).to be false
574
+ expect(user.subscriptions.cancelled.exists?).to be true
575
+ expect(user.subscriptions.not_cancelled.exists?).to be false
576
+ expect(user.subscriptions.on_grace_period.exists?).to be true
577
+ expect(user.subscriptions.not_on_grace_period.exists?).to be false
578
+ expect(user.subscriptions.ended.exists?).to be false
579
+
580
+ # End subscription.
581
+ subscription.update({:ends_at => Time.now - 1.day})
582
+
583
+ expect(user.subscriptions.incomplete.exists?).to be false
584
+ expect(user.subscriptions.active.exists?).to be false
585
+ expect(user.subscriptions.on_trial.exists?).to be true
586
+ expect(user.subscriptions.not_on_trial.exists?).to be false
587
+ expect(user.subscriptions.recurring.exists?).to be false
588
+ expect(user.subscriptions.cancelled.exists?).to be true
589
+ expect(user.subscriptions.not_cancelled.exists?).to be false
590
+ expect(user.subscriptions.on_grace_period.exists?).to be false
591
+ expect(user.subscriptions.not_on_grace_period.exists?).to be true
592
+ expect(user.subscriptions.ended.exists?).to be true
593
+
594
+ # Enable past_due as active state.
595
+ expect(subscription.active).to be false
596
+ expect(user.subscriptions.active.exists?).to be false
597
+
598
+ Reji.keep_past_due_subscriptions_active
599
+
600
+ subscription.update({
601
+ :ends_at => nil,
602
+ :stripe_status => 'past_due',
603
+ })
604
+
605
+ expect(subscription.active).to be true
606
+ expect(user.subscriptions.active.exists?).to be true
607
+
608
+ # Reset deactivate past due state to default to not conflict with other tests.
609
+ Reji.deactivate_past_due = true
610
+ end
611
+
612
+ it 'test_retrieve_the_latest_payment_for_a_subscription' do
613
+ user = create_customer('retrieve_the_latest_payment_for_a_subscription')
614
+
615
+ begin
616
+ user.new_subscription('main', @plan_id).create('pm_card_threeDSecure2Required')
617
+
618
+ raise RSpec::Expectations::ExpectationNotMetError.new('Expected exception PaymentActionRequiredError was not thrown.')
619
+ rescue Reji::PaymentActionRequiredError => e
620
+ subscription = user.subscription('main')
621
+
622
+ payment = subscription.latest_payment
623
+
624
+ expect(payment).to be_an_instance_of Reji::Payment
625
+ expect(payment.requires_action).to be true
626
+ end
627
+ end
628
+
629
+ it 'test_subscriptions_with_tax_rates_can_be_created' do
630
+ user = create_customer('subscriptions_with_tax_rates_can_be_created')
631
+ user.tax_rates = [@tax_rate_id]
632
+
633
+ subscription = user.new_subscription('main', @plan_id).create('pm_card_visa')
634
+ stripe_subscription = subscription.as_stripe_subscription
635
+
636
+ expect([stripe_subscription.default_tax_rates.first.id]).to eq([@tax_rate_id])
637
+ end
638
+
639
+ it 'test_subscriptions_with_options_can_be_created' do
640
+ user = create_customer('subscriptions_with_options_can_be_created')
641
+
642
+ backdate_start_date = (Time.now - 1.month).to_i
643
+ subscription = user.new_subscription('main', @plan_id).create(
644
+ 'pm_card_visa', {}, {:backdate_start_date => backdate_start_date}
645
+ )
646
+ stripe_subscription = subscription.as_stripe_subscription
647
+
648
+ expect(stripe_subscription.start_date).to eq(backdate_start_date)
649
+ end
650
+ end