reji 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +73 -0
- data/.rubocop_todo.yml +31 -0
- data/Appraisals +2 -0
- data/Gemfile +1 -1
- data/README.md +41 -17
- data/Rakefile +8 -2
- data/app/controllers/reji/payment_controller.rb +4 -4
- data/app/controllers/reji/webhook_controller.rb +51 -62
- data/app/views/payment.html.erb +4 -4
- data/app/views/receipt.html.erb +16 -16
- data/config/routes.rb +2 -0
- data/gemfiles/rails_5.0.gemfile +9 -9
- data/gemfiles/rails_5.1.gemfile +7 -9
- data/gemfiles/rails_5.2.gemfile +7 -9
- data/gemfiles/rails_6.0.gemfile +7 -9
- data/lib/generators/reji/install/install_generator.rb +20 -24
- data/lib/generators/reji/install/templates/reji.rb +2 -2
- data/lib/reji.rb +12 -8
- data/lib/reji/concerns/manages_customer.rb +25 -29
- data/lib/reji/concerns/manages_invoices.rb +37 -44
- data/lib/reji/concerns/manages_payment_methods.rb +45 -62
- data/lib/reji/concerns/manages_subscriptions.rb +13 -13
- data/lib/reji/concerns/performs_charges.rb +7 -7
- data/lib/reji/concerns/prorates.rb +1 -1
- data/lib/reji/configuration.rb +2 -2
- data/lib/reji/engine.rb +2 -0
- data/lib/reji/errors.rb +9 -9
- data/lib/reji/invoice.rb +57 -56
- data/lib/reji/invoice_line_item.rb +21 -23
- data/lib/reji/payment.rb +9 -5
- data/lib/reji/payment_method.rb +8 -4
- data/lib/reji/subscription.rb +165 -183
- data/lib/reji/subscription_builder.rb +41 -49
- data/lib/reji/subscription_item.rb +26 -26
- data/lib/reji/tax.rb +8 -10
- data/lib/reji/version.rb +1 -1
- data/reji.gemspec +5 -4
- data/spec/dummy/app/models/user.rb +3 -7
- data/spec/dummy/application.rb +3 -7
- data/spec/dummy/db/schema.rb +3 -4
- data/spec/feature/charges_spec.rb +1 -1
- data/spec/feature/customer_spec.rb +1 -1
- data/spec/feature/invoices_spec.rb +6 -6
- data/spec/feature/multiplan_subscriptions_spec.rb +51 -53
- data/spec/feature/payment_methods_spec.rb +25 -25
- data/spec/feature/pending_updates_spec.rb +26 -26
- data/spec/feature/subscriptions_spec.rb +78 -78
- data/spec/feature/webhooks_spec.rb +72 -72
- data/spec/spec_helper.rb +2 -2
- data/spec/support/feature_helpers.rb +6 -12
- data/spec/unit/customer_spec.rb +13 -13
- data/spec/unit/invoice_line_item_spec.rb +12 -14
- data/spec/unit/invoice_spec.rb +7 -9
- data/spec/unit/payment_spec.rb +3 -3
- data/spec/unit/subscription_spec.rb +29 -30
- metadata +26 -11
- data/Gemfile.lock +0 -133
@@ -55,7 +55,7 @@ describe 'charges', type: :request do
|
|
55
55
|
begin
|
56
56
|
user.charge(1000, 'pm_card_threeDSecure2Required')
|
57
57
|
|
58
|
-
raise RSpec::Expectations::ExpectationNotMetError
|
58
|
+
raise RSpec::Expectations::ExpectationNotMetError, 'Expected exception PaymentActionRequiredError was not thrown.'
|
59
59
|
rescue Reji::PaymentActionRequiredError => e
|
60
60
|
# Assert that the payment needs an extra action.
|
61
61
|
expect(e.payment.requires_action).to be true
|
@@ -7,7 +7,7 @@ describe 'customer', type: :request do
|
|
7
7
|
user = create_customer('customers_in_stripe_can_be_updated')
|
8
8
|
user.create_as_stripe_customer
|
9
9
|
|
10
|
-
customer = user.update_stripe_customer({:
|
10
|
+
customer = user.update_stripe_customer({ description: 'Van Cam' })
|
11
11
|
|
12
12
|
expect(customer.description).to eq('Van Cam')
|
13
13
|
end
|
@@ -6,9 +6,9 @@ describe 'invoices', type: :request do
|
|
6
6
|
it 'test_require_stripe_customer_for_invoicing' do
|
7
7
|
user = create_customer('require_stripe_customer_for_invoicing')
|
8
8
|
|
9
|
-
expect
|
9
|
+
expect do
|
10
10
|
user.invoice
|
11
|
-
|
11
|
+
end.to raise_error(Reji::InvalidCustomerError)
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'test_invoicing_fails_with_nothing_to_invoice' do
|
@@ -53,9 +53,9 @@ describe 'invoices', type: :request do
|
|
53
53
|
user.update_default_payment_method('pm_card_visa')
|
54
54
|
invoice = user.invoice_for('Rails Reji', 1000)
|
55
55
|
|
56
|
-
expect
|
56
|
+
expect do
|
57
57
|
other_user.find_invoice(invoice.id)
|
58
|
-
|
58
|
+
end.to raise_error(Reji::InvalidInvoiceError, "The invoice `#{invoice.id}` does not belong to this customer `#{other_user.stripe_id}`.")
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'test_find_invoice_by_id_or_fail' do
|
@@ -66,8 +66,8 @@ describe 'invoices', type: :request do
|
|
66
66
|
user.update_default_payment_method('pm_card_visa')
|
67
67
|
invoice = user.invoice_for('Rails Reji', 1000)
|
68
68
|
|
69
|
-
expect
|
69
|
+
expect do
|
70
70
|
other_user.find_invoice_or_fail(invoice.id)
|
71
|
-
|
71
|
+
end.to raise_error(Reji::AccessDeniedHttpError)
|
72
72
|
end
|
73
73
|
end
|
@@ -10,47 +10,47 @@ describe 'multiplan subscriptions', type: :request do
|
|
10
10
|
@premium_plan_id = "#{stripe_prefix}monthly-20-premium-#{SecureRandom.hex(5)}"
|
11
11
|
|
12
12
|
Stripe::Product.create({
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
13
|
+
id: @product_id,
|
14
|
+
name: 'Rails Reji Test Product',
|
15
|
+
type: 'service',
|
16
16
|
})
|
17
17
|
|
18
18
|
Stripe::Plan.create({
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
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
26
|
})
|
27
27
|
|
28
28
|
Stripe::Plan.create({
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
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
36
|
})
|
37
37
|
|
38
38
|
Stripe::Plan.create({
|
39
|
-
:
|
40
|
-
:
|
41
|
-
:
|
42
|
-
:
|
43
|
-
:
|
44
|
-
:
|
45
|
-
:
|
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
46
|
})
|
47
47
|
|
48
48
|
@tax_rate_id = Stripe::TaxRate.create({
|
49
|
-
:
|
50
|
-
:
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
49
|
+
display_name: 'VAT',
|
50
|
+
description: 'VAT Belgium',
|
51
|
+
jurisdiction: 'BE',
|
52
|
+
percentage: 21,
|
53
|
+
inclusive: false,
|
54
54
|
}).id
|
55
55
|
end
|
56
56
|
|
@@ -64,7 +64,7 @@ describe 'multiplan subscriptions', type: :request do
|
|
64
64
|
it 'test_customers_can_have_multiplan_subscriptions' do
|
65
65
|
user = create_customer('customers_can_have_multiplan_subscriptions')
|
66
66
|
|
67
|
-
user.plan_tax_rates = {@other_plan_id => [@tax_rate_id]}
|
67
|
+
user.plan_tax_rates = { @other_plan_id => [@tax_rate_id] }
|
68
68
|
|
69
69
|
subscription = user.new_subscription('main', [@plan_id, @other_plan_id])
|
70
70
|
.plan(@premium_plan_id, 5)
|
@@ -124,11 +124,11 @@ describe 'multiplan subscriptions', type: :request do
|
|
124
124
|
it 'test_customers_cannot_remove_the_last_plan' do
|
125
125
|
user = create_customer('customers_cannot_remove_the_last_plan')
|
126
126
|
|
127
|
-
subscription =
|
127
|
+
subscription = create_subscription_with_single_plan(user)
|
128
128
|
|
129
|
-
expect
|
129
|
+
expect do
|
130
130
|
subscription.remove_plan(@plan_id)
|
131
|
-
|
131
|
+
end.to raise_error(Reji::SubscriptionUpdateFailureError)
|
132
132
|
end
|
133
133
|
|
134
134
|
it 'test_multiplan_subscriptions_can_be_resumed' do
|
@@ -152,11 +152,11 @@ describe 'multiplan subscriptions', type: :request do
|
|
152
152
|
it 'test_plan_is_required_when_updating_quantities_for_multiplan_subscriptions' do
|
153
153
|
user = create_customer('plan_is_required_when_updating_quantities_for_multiplan_subscriptions')
|
154
154
|
|
155
|
-
subscription =
|
155
|
+
subscription = create_subscription_with_multiple_plans(user)
|
156
156
|
|
157
|
-
expect
|
157
|
+
expect do
|
158
158
|
subscription.update_quantity(5)
|
159
|
-
|
159
|
+
end.to raise_error(ArgumentError)
|
160
160
|
end
|
161
161
|
|
162
162
|
it 'test_subscription_item_quantities_can_be_updated' do
|
@@ -227,7 +227,7 @@ describe 'multiplan subscriptions', type: :request do
|
|
227
227
|
|
228
228
|
subscription = user.new_subscription('main', @plan_id).create('pm_card_visa')
|
229
229
|
|
230
|
-
item = subscription.items.first.swap(@other_plan_id, {:
|
230
|
+
item = subscription.items.first.swap(@other_plan_id, { quantity: 3 })
|
231
231
|
|
232
232
|
expect(subscription.items.count).to eq(1)
|
233
233
|
expect(subscription.stripe_plan).to eq(@other_plan_id)
|
@@ -279,39 +279,37 @@ describe 'multiplan subscriptions', type: :request do
|
|
279
279
|
expect(user.upcoming_invoice.raw_total).to eq(2000)
|
280
280
|
end
|
281
281
|
|
282
|
-
protected
|
283
|
-
|
284
282
|
# Create a subscription with a single plan.
|
285
|
-
def create_subscription_with_single_plan(user)
|
283
|
+
protected def create_subscription_with_single_plan(user)
|
286
284
|
subscription = user.subscriptions.create({
|
287
|
-
:
|
288
|
-
:
|
289
|
-
:
|
290
|
-
:
|
291
|
-
:
|
285
|
+
name: 'main',
|
286
|
+
stripe_id: 'sub_foo',
|
287
|
+
stripe_plan: @plan_id,
|
288
|
+
quantity: 1,
|
289
|
+
stripe_status: 'active',
|
292
290
|
})
|
293
291
|
|
294
292
|
subscription.items.create({
|
295
|
-
:
|
296
|
-
:
|
297
|
-
:
|
293
|
+
stripe_id: 'it_foo',
|
294
|
+
stripe_plan: @plan_id,
|
295
|
+
quantity: 1,
|
298
296
|
})
|
299
297
|
|
300
298
|
subscription
|
301
299
|
end
|
302
300
|
|
303
301
|
# Create a subscription with multiple plans.
|
304
|
-
def create_subscription_with_multiple_plans(user)
|
305
|
-
subscription =
|
302
|
+
protected def create_subscription_with_multiple_plans(user)
|
303
|
+
subscription = create_subscription_with_single_plan(user)
|
306
304
|
|
307
305
|
subscription.stripe_plan = nil
|
308
306
|
subscription.quantity = nil
|
309
307
|
subscription.save
|
310
308
|
|
311
309
|
subscription.items.new({
|
312
|
-
:
|
313
|
-
:
|
314
|
-
:
|
310
|
+
stripe_id: 'it_foo',
|
311
|
+
stripe_plan: @other_plan_id,
|
312
|
+
quantity: 1,
|
315
313
|
})
|
316
314
|
|
317
315
|
subscription
|
@@ -3,13 +3,13 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe 'payment_methods', type: :request do
|
6
|
-
it '
|
6
|
+
it 'test_we_can_start_a_new_setup_intent_session' do
|
7
7
|
user = create_customer('we_can_start_a_new_setup_intent_session')
|
8
8
|
setup_intent = user.create_setup_intent
|
9
9
|
expect(setup_intent).to be_an_instance_of(Stripe::SetupIntent)
|
10
10
|
end
|
11
11
|
|
12
|
-
it '
|
12
|
+
it 'test_we_can_add_payment_methods' do
|
13
13
|
user = create_customer('we_can_add_payment_methods')
|
14
14
|
user.create_as_stripe_customer
|
15
15
|
|
@@ -18,34 +18,34 @@ describe 'payment_methods', type: :request do
|
|
18
18
|
expect(payment_method).to be_an_instance_of(Reji::PaymentMethod)
|
19
19
|
expect(payment_method.card.brand).to eq('visa')
|
20
20
|
expect(payment_method.card.last4).to eq('4242')
|
21
|
-
expect(user.
|
22
|
-
expect(user.
|
21
|
+
expect(user.payment_method?).to be true
|
22
|
+
expect(user.default_payment_method?).to be false
|
23
23
|
end
|
24
24
|
|
25
|
-
it '
|
25
|
+
it 'test_we_can_remove_payment_methods' do
|
26
26
|
user = create_customer('we_can_remove_payment_methods')
|
27
27
|
user.create_as_stripe_customer
|
28
28
|
|
29
29
|
payment_method = user.add_payment_method('pm_card_visa')
|
30
30
|
|
31
31
|
expect(user.payment_methods.count).to eq(1)
|
32
|
-
expect(user.
|
32
|
+
expect(user.payment_method?).to be true
|
33
33
|
|
34
34
|
user.remove_payment_method(payment_method.as_stripe_payment_method)
|
35
35
|
|
36
36
|
expect(user.payment_methods.count).to eq(0)
|
37
|
-
expect(user.
|
37
|
+
expect(user.payment_method?).to be false
|
38
38
|
end
|
39
39
|
|
40
|
-
it '
|
40
|
+
it 'test_we_can_remove_the_default_payment_method' do
|
41
41
|
user = create_customer('we_can_remove_the_default_payment_method')
|
42
42
|
user.create_as_stripe_customer
|
43
43
|
|
44
44
|
payment_method = user.update_default_payment_method('pm_card_visa')
|
45
45
|
|
46
46
|
expect(user.payment_methods.count).to eq(1)
|
47
|
-
expect(user.
|
48
|
-
expect(user.
|
47
|
+
expect(user.payment_method?).to be true
|
48
|
+
expect(user.default_payment_method?).to be true
|
49
49
|
|
50
50
|
user.remove_payment_method(payment_method.as_stripe_payment_method)
|
51
51
|
|
@@ -53,11 +53,11 @@ describe 'payment_methods', type: :request do
|
|
53
53
|
expect(user.default_payment_method).to be_nil
|
54
54
|
expect(user.card_brand).to be_nil
|
55
55
|
expect(user.card_last_four).to be_nil
|
56
|
-
expect(user.
|
57
|
-
expect(user.
|
56
|
+
expect(user.payment_method?).to be false
|
57
|
+
expect(user.default_payment_method?).to be false
|
58
58
|
end
|
59
59
|
|
60
|
-
it '
|
60
|
+
it 'test_we_can_set_a_default_payment_method' do
|
61
61
|
user = create_customer('we_can_set_a_default_payment_method')
|
62
62
|
user.create_as_stripe_customer
|
63
63
|
|
@@ -66,7 +66,7 @@ describe 'payment_methods', type: :request do
|
|
66
66
|
expect(payment_method).to be_an_instance_of(Reji::PaymentMethod)
|
67
67
|
expect(payment_method.card.brand).to eq('visa')
|
68
68
|
expect(payment_method.card.last4).to eq('4242')
|
69
|
-
expect(user.
|
69
|
+
expect(user.default_payment_method?).to be true
|
70
70
|
|
71
71
|
payment_method = user.default_payment_method
|
72
72
|
|
@@ -75,10 +75,10 @@ describe 'payment_methods', type: :request do
|
|
75
75
|
expect(payment_method.card.last4).to eq('4242')
|
76
76
|
end
|
77
77
|
|
78
|
-
it '
|
78
|
+
it 'test_legacy_we_can_retrieve_an_old_default_source_as_a_default_payment_method' do
|
79
79
|
user = create_customer('we_can_retrieve_an_old_default_source_as_a_default_payment_method')
|
80
80
|
customer = user.create_as_stripe_customer
|
81
|
-
card = Stripe::Customer.create_source(customer.id, {:
|
81
|
+
card = Stripe::Customer.create_source(customer.id, { source: 'tok_visa' }, user.stripe_options)
|
82
82
|
customer.default_source = card.id
|
83
83
|
customer.save
|
84
84
|
|
@@ -89,15 +89,15 @@ describe 'payment_methods', type: :request do
|
|
89
89
|
expect(payment_method.last4).to eq('4242')
|
90
90
|
end
|
91
91
|
|
92
|
-
it '
|
92
|
+
it 'test_we_can_retrieve_all_payment_methods' do
|
93
93
|
user = create_customer('we_can_retrieve_all_payment_methods')
|
94
94
|
customer = user.create_as_stripe_customer
|
95
95
|
|
96
96
|
payment_method = Stripe::PaymentMethod.retrieve('pm_card_visa', user.stripe_options)
|
97
|
-
payment_method.attach({:
|
97
|
+
payment_method.attach({ customer: customer.id })
|
98
98
|
|
99
99
|
payment_method = Stripe::PaymentMethod.retrieve('pm_card_mastercard', user.stripe_options)
|
100
|
-
payment_method.attach({:
|
100
|
+
payment_method.attach({ customer: customer.id })
|
101
101
|
|
102
102
|
payment_methods = user.payment_methods
|
103
103
|
|
@@ -106,14 +106,14 @@ describe 'payment_methods', type: :request do
|
|
106
106
|
expect(payment_methods.last.card.brand).to eq('visa')
|
107
107
|
end
|
108
108
|
|
109
|
-
it '
|
109
|
+
it 'test_we_can_sync_the_default_payment_method_from_stripe' do
|
110
110
|
user = create_customer('we_can_sync_the_payment_method_from_stripe')
|
111
111
|
customer = user.create_as_stripe_customer
|
112
112
|
|
113
113
|
payment_method = Stripe::PaymentMethod.retrieve('pm_card_visa', user.stripe_options)
|
114
|
-
payment_method.attach({:
|
114
|
+
payment_method.attach({ customer: customer.id })
|
115
115
|
|
116
|
-
customer.invoice_settings = {:
|
116
|
+
customer.invoice_settings = { default_payment_method: payment_method.id }
|
117
117
|
|
118
118
|
customer.save
|
119
119
|
|
@@ -126,15 +126,15 @@ describe 'payment_methods', type: :request do
|
|
126
126
|
expect(user.card_last_four).to eq('4242')
|
127
127
|
end
|
128
128
|
|
129
|
-
it '
|
129
|
+
it 'test_we_delete_all_payment_methods' do
|
130
130
|
user = create_customer('we_delete_all_payment_methods')
|
131
131
|
customer = user.create_as_stripe_customer
|
132
132
|
|
133
133
|
payment_method = Stripe::PaymentMethod.retrieve('pm_card_visa', user.stripe_options)
|
134
|
-
payment_method.attach({:
|
134
|
+
payment_method.attach({ customer: customer.id })
|
135
135
|
|
136
136
|
payment_method = Stripe::PaymentMethod.retrieve('pm_card_mastercard', user.stripe_options)
|
137
|
-
payment_method.attach({:
|
137
|
+
payment_method.attach({ customer: customer.id })
|
138
138
|
|
139
139
|
payment_methods = user.payment_methods
|
140
140
|
|
@@ -10,39 +10,39 @@ describe 'pending updates', type: :request do
|
|
10
10
|
@premium_plan_id = "#{stripe_prefix}monthly-20-premium-#{SecureRandom.hex(5)}"
|
11
11
|
|
12
12
|
Stripe::Product.create({
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
13
|
+
id: @product_id,
|
14
|
+
name: 'Rails Reji Test Product',
|
15
|
+
type: 'service',
|
16
16
|
})
|
17
17
|
|
18
18
|
Stripe::Plan.create({
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
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
26
|
})
|
27
27
|
|
28
28
|
Stripe::Plan.create({
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
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
36
|
})
|
37
37
|
|
38
38
|
Stripe::Plan.create({
|
39
|
-
:
|
40
|
-
:
|
41
|
-
:
|
42
|
-
:
|
43
|
-
:
|
44
|
-
:
|
45
|
-
:
|
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
46
|
})
|
47
47
|
end
|
48
48
|
|
@@ -65,8 +65,8 @@ describe 'pending updates', type: :request do
|
|
65
65
|
# Attempt to swap and pay with a faulty card.
|
66
66
|
subscription = subscription.error_if_payment_fails.swap_and_invoice(@premium_plan_id)
|
67
67
|
|
68
|
-
raise RSpec::Expectations::ExpectationNotMetError
|
69
|
-
rescue Stripe::CardError =>
|
68
|
+
raise RSpec::Expectations::ExpectationNotMetError, 'Expected exception PaymentFailureError was not thrown.'
|
69
|
+
rescue Stripe::CardError => _e
|
70
70
|
# Assert that the plan was not swapped.
|
71
71
|
expect(subscription.stripe_plan).to eq(@plan_id)
|
72
72
|
|