reji 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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