stripe-ruby-mock 2.3.1 → 2.5.8
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.
- checksums.yaml +4 -4
- data/.env +2 -0
- data/.travis.yml +8 -4
- data/README.md +12 -5
- data/lib/stripe_mock/api/account_balance.rb +14 -0
- data/lib/stripe_mock/api/client.rb +4 -4
- data/lib/stripe_mock/api/conversion_rate.rb +14 -0
- data/lib/stripe_mock/api/errors.rb +25 -13
- data/lib/stripe_mock/api/instance.rb +16 -6
- data/lib/stripe_mock/api/webhooks.rb +8 -1
- data/lib/stripe_mock/client.rb +18 -2
- data/lib/stripe_mock/data/list.rb +14 -2
- data/lib/stripe_mock/data.rb +398 -58
- data/lib/stripe_mock/instance.rb +105 -9
- data/lib/stripe_mock/request_handlers/accounts.rb +41 -2
- data/lib/stripe_mock/request_handlers/balance.rb +17 -0
- data/lib/stripe_mock/request_handlers/balance_transactions.rb +18 -2
- data/lib/stripe_mock/request_handlers/charges.rb +44 -33
- data/lib/stripe_mock/request_handlers/country_spec.rb +22 -0
- data/lib/stripe_mock/request_handlers/coupons.rb +5 -4
- data/lib/stripe_mock/request_handlers/customers.rb +29 -11
- data/lib/stripe_mock/request_handlers/ephemeral_key.rb +13 -0
- data/lib/stripe_mock/request_handlers/external_accounts.rb +55 -0
- data/lib/stripe_mock/request_handlers/helpers/bank_account_helpers.rb +1 -1
- data/lib/stripe_mock/request_handlers/helpers/card_helpers.rb +9 -7
- data/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb +10 -6
- data/lib/stripe_mock/request_handlers/helpers/external_account_helpers.rb +49 -0
- data/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +59 -16
- data/lib/stripe_mock/request_handlers/helpers/token_helpers.rb +3 -2
- data/lib/stripe_mock/request_handlers/invoices.rb +93 -14
- data/lib/stripe_mock/request_handlers/orders.rb +5 -5
- data/lib/stripe_mock/request_handlers/payouts.rb +32 -0
- data/lib/stripe_mock/request_handlers/plans.rb +1 -0
- data/lib/stripe_mock/request_handlers/products.rb +43 -0
- data/lib/stripe_mock/request_handlers/recipients.rb +12 -0
- data/lib/stripe_mock/request_handlers/refunds.rb +91 -0
- data/lib/stripe_mock/request_handlers/subscription_items.rb +36 -0
- data/lib/stripe_mock/request_handlers/subscriptions.rb +101 -39
- data/lib/stripe_mock/request_handlers/tax_rates.rb +36 -0
- data/lib/stripe_mock/request_handlers/tokens.rb +9 -3
- data/lib/stripe_mock/request_handlers/transfers.rb +11 -5
- data/lib/stripe_mock/request_handlers/validators/param_validators.rb +15 -1
- data/lib/stripe_mock/server.rb +14 -1
- data/lib/stripe_mock/test_strategies/base.rb +10 -5
- data/lib/stripe_mock/test_strategies/live.rb +5 -0
- data/lib/stripe_mock/test_strategies/mock.rb +8 -0
- data/lib/stripe_mock/util.rb +8 -2
- data/lib/stripe_mock/version.rb +1 -1
- data/lib/stripe_mock/webhook_fixtures/account.external_account.created.json +27 -0
- data/lib/stripe_mock/webhook_fixtures/account.external_account.deleted.json +27 -0
- data/lib/stripe_mock/webhook_fixtures/account.external_account.updated.json +27 -0
- data/lib/stripe_mock/webhook_fixtures/account.updated.json +1 -1
- data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_reinstated.json +88 -0
- data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_withdrawn.json +88 -0
- data/lib/stripe_mock/webhook_fixtures/charge.updated.json +58 -0
- data/lib/stripe_mock/webhook_fixtures/customer.subscription.created.json +40 -10
- data/lib/stripe_mock/webhook_fixtures/customer.subscription.deleted.json +39 -10
- data/lib/stripe_mock/webhook_fixtures/customer.subscription.trial_will_end.json +39 -10
- data/lib/stripe_mock/webhook_fixtures/customer.subscription.updated.json +40 -11
- data/lib/stripe_mock/webhook_fixtures/invoice.created.json +3 -2
- data/lib/stripe_mock/webhook_fixtures/invoice.payment_failed.json +1 -1
- data/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json +92 -85
- data/lib/stripe_mock/webhook_fixtures/invoice.updated.json +3 -2
- data/lib/stripe_mock/webhook_fixtures/plan.created.json +1 -1
- data/lib/stripe_mock/webhook_fixtures/plan.deleted.json +1 -1
- data/lib/stripe_mock/webhook_fixtures/plan.updated.json +1 -1
- data/lib/stripe_mock.rb +15 -0
- data/spec/api/instance_spec.rb +30 -0
- data/spec/instance_spec.rb +54 -4
- data/spec/integration_examples/prepare_error_examples.rb +6 -6
- data/spec/list_spec.rb +27 -10
- data/spec/readme_spec.rb +2 -0
- data/spec/server_spec.rb +7 -3
- data/spec/shared_stripe_examples/account_examples.rb +46 -0
- data/spec/shared_stripe_examples/balance_examples.rb +11 -0
- data/spec/shared_stripe_examples/balance_transaction_examples.rb +28 -0
- data/spec/shared_stripe_examples/bank_examples.rb +28 -1
- data/spec/shared_stripe_examples/card_examples.rb +23 -5
- data/spec/shared_stripe_examples/card_token_examples.rb +1 -0
- data/spec/shared_stripe_examples/charge_examples.rb +131 -26
- data/spec/shared_stripe_examples/country_specs_examples.rb +18 -0
- data/spec/shared_stripe_examples/coupon_examples.rb +8 -2
- data/spec/shared_stripe_examples/customer_examples.rb +90 -0
- data/spec/shared_stripe_examples/dispute_examples.rb +19 -8
- data/spec/shared_stripe_examples/ephemeral_key_examples.rb +17 -0
- data/spec/shared_stripe_examples/error_mock_examples.rb +15 -5
- data/spec/shared_stripe_examples/external_account_examples.rb +170 -0
- data/spec/shared_stripe_examples/extra_features_examples.rb +2 -0
- data/spec/shared_stripe_examples/invoice_examples.rb +314 -51
- data/spec/shared_stripe_examples/payout_examples.rb +68 -0
- data/spec/shared_stripe_examples/plan_examples.rb +47 -4
- data/spec/shared_stripe_examples/product_example.rb +65 -0
- data/spec/shared_stripe_examples/recipient_examples.rb +13 -7
- data/spec/shared_stripe_examples/refund_examples.rb +453 -84
- data/spec/shared_stripe_examples/subscription_examples.rb +477 -32
- data/spec/shared_stripe_examples/subscription_items_examples.rb +75 -0
- data/spec/shared_stripe_examples/tax_rate_examples.rb +42 -0
- data/spec/shared_stripe_examples/transfer_examples.rb +72 -23
- data/spec/shared_stripe_examples/webhook_event_examples.rb +74 -5
- data/spec/spec_helper.rb +7 -6
- data/spec/stripe_mock_spec.rb +16 -3
- data/spec/support/stripe_examples.rb +8 -1
- data/spec/util_spec.rb +35 -1
- data/stripe-ruby-mock.gemspec +1 -1
- metadata +44 -8
- data/ChangeLog.rdoc +0 -4
@@ -3,7 +3,7 @@ module StripeMock
|
|
3
3
|
module Helpers
|
4
4
|
|
5
5
|
def verify_bank_account(object, bank_account_id, class_name='Customer')
|
6
|
-
bank_accounts = object[:bank_accounts] || object[:sources]
|
6
|
+
bank_accounts = object[:external_accounts] || object[:bank_accounts] || object[:sources]
|
7
7
|
bank_account = bank_accounts[:data].find{|acc| acc[:id] == bank_account_id }
|
8
8
|
return if bank_account.nil?
|
9
9
|
bank_account['status'] = 'verified'
|
@@ -3,15 +3,15 @@ module StripeMock
|
|
3
3
|
module Helpers
|
4
4
|
|
5
5
|
def get_card(object, card_id, class_name='Customer')
|
6
|
-
cards = object[:cards] || object[:sources]
|
6
|
+
cards = object[:cards] || object[:sources] || object[:external_accounts]
|
7
7
|
card = cards[:data].find{|cc| cc[:id] == card_id }
|
8
8
|
if card.nil?
|
9
9
|
if class_name == 'Recipient'
|
10
10
|
msg = "#{class_name} #{object[:id]} does not have a card with ID #{card_id}"
|
11
|
-
raise Stripe::InvalidRequestError.new(msg, 'card', 404)
|
11
|
+
raise Stripe::InvalidRequestError.new(msg, 'card', http_status: 404)
|
12
12
|
else
|
13
13
|
msg = "There is no source with ID #{card_id}"
|
14
|
-
raise Stripe::InvalidRequestError.new(msg, 'id', 404)
|
14
|
+
raise Stripe::InvalidRequestError.new(msg, 'id', http_status: 404)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
card
|
@@ -36,7 +36,7 @@ module StripeMock
|
|
36
36
|
|
37
37
|
def add_card_to_object(type, card, object, replace_current=false)
|
38
38
|
card[type] = object[:id]
|
39
|
-
cards_or_sources = object[:cards] || object[:sources]
|
39
|
+
cards_or_sources = object[:cards] || object[:sources] || object[:external_accounts]
|
40
40
|
|
41
41
|
is_customer = object.has_key?(:sources)
|
42
42
|
|
@@ -58,7 +58,7 @@ module StripeMock
|
|
58
58
|
|
59
59
|
def retrieve_object_cards(type, type_id, objects)
|
60
60
|
resource = assert_existence type, type_id, objects[type_id]
|
61
|
-
cards = resource[:cards] || resource[:sources]
|
61
|
+
cards = resource[:cards] || resource[:sources] || resource[:external_accounts]
|
62
62
|
|
63
63
|
Data.mock_list_object(cards[:data])
|
64
64
|
end
|
@@ -69,7 +69,7 @@ module StripeMock
|
|
69
69
|
assert_existence :card, card_id, get_card(resource, card_id)
|
70
70
|
|
71
71
|
card = { id: card_id, deleted: true }
|
72
|
-
cards_or_sources = resource[:cards] || resource[:sources]
|
72
|
+
cards_or_sources = resource[:cards] || resource[:sources] || resource[:external_accounts]
|
73
73
|
cards_or_sources[:data].reject!{|cc|
|
74
74
|
cc[:id] == card[:id]
|
75
75
|
}
|
@@ -77,6 +77,7 @@ module StripeMock
|
|
77
77
|
is_customer = resource.has_key?(:sources)
|
78
78
|
new_default = cards_or_sources[:data].count > 0 ? cards_or_sources[:data].first[:id] : nil
|
79
79
|
resource[:default_card] = new_default unless is_customer
|
80
|
+
resource[:sources][:total_count] = cards_or_sources[:data].count if is_customer
|
80
81
|
resource[:default_source] = new_default if is_customer
|
81
82
|
card
|
82
83
|
end
|
@@ -96,13 +97,14 @@ module StripeMock
|
|
96
97
|
get_bank_by_token(params[:source])
|
97
98
|
end
|
98
99
|
end
|
100
|
+
source[:metadata].merge!(params[:metadata]) if params[:metadata]
|
99
101
|
add_source_to_object(type, source, resource)
|
100
102
|
end
|
101
103
|
|
102
104
|
def add_card_to(type, type_id, params, objects)
|
103
105
|
resource = assert_existence type, type_id, objects[type_id]
|
104
106
|
|
105
|
-
card = card_from_params(params[:card] || params[:source])
|
107
|
+
card = card_from_params(params[:card] || params[:source] || params[:external_accounts])
|
106
108
|
add_card_to_object(type, card, resource)
|
107
109
|
end
|
108
110
|
|
@@ -1,13 +1,17 @@
|
|
1
1
|
module StripeMock
|
2
2
|
module RequestHandlers
|
3
3
|
module Helpers
|
4
|
+
def add_coupon_to_object(object, coupon)
|
5
|
+
discount_attrs = {}.tap do |attrs|
|
6
|
+
attrs[object[:object]] = object[:id]
|
7
|
+
attrs[:coupon] = coupon
|
8
|
+
attrs[:start] = Time.now.to_i
|
9
|
+
attrs[:end] = (DateTime.now >> coupon[:duration_in_months].to_i).to_time.to_i if coupon[:duration] == 'repeating'
|
10
|
+
end
|
4
11
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
customer
|
12
|
+
object[:discount] = Stripe::Discount.construct_from(discount_attrs)
|
13
|
+
object
|
9
14
|
end
|
10
|
-
|
11
15
|
end
|
12
16
|
end
|
13
|
-
end
|
17
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module StripeMock
|
2
|
+
module RequestHandlers
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
def add_external_account_to(type, type_id, params, objects)
|
6
|
+
resource = assert_existence type, type_id, objects[type_id]
|
7
|
+
|
8
|
+
source =
|
9
|
+
if params[:card]
|
10
|
+
card_from_params(params[:card])
|
11
|
+
elsif params[:bank_account]
|
12
|
+
bank_from_params(params[:bank_account])
|
13
|
+
else
|
14
|
+
begin
|
15
|
+
get_card_by_token(params[:external_account])
|
16
|
+
rescue Stripe::InvalidRequestError
|
17
|
+
bank_from_params(params[:external_account])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
add_external_account_to_object(type, source, resource)
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_external_account_to_object(type, source, object, replace_current=false)
|
24
|
+
source[type] = object[:id]
|
25
|
+
accounts = object[:external_accounts]
|
26
|
+
|
27
|
+
if replace_current && accounts[:data]
|
28
|
+
accounts[:data].delete_if {|source| source[:id] == object[:default_source]}
|
29
|
+
object[:default_source] = source[:id]
|
30
|
+
accounts[:data] = [source]
|
31
|
+
else
|
32
|
+
accounts[:total_count] = (accounts[:total_count] || 0) + 1
|
33
|
+
(accounts[:data] ||= []) << source
|
34
|
+
end
|
35
|
+
object[:default_source] = source[:id] if object[:default_source].nil?
|
36
|
+
|
37
|
+
source
|
38
|
+
end
|
39
|
+
|
40
|
+
def bank_from_params(attrs_or_token)
|
41
|
+
if attrs_or_token.is_a? Hash
|
42
|
+
attrs_or_token = generate_bank_token(attrs_or_token)
|
43
|
+
end
|
44
|
+
get_bank_by_token(attrs_or_token)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -6,32 +6,68 @@ module StripeMock
|
|
6
6
|
customer[:subscriptions][:data].find{|sub| sub[:id] == sub_id }
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
9
|
+
def resolve_subscription_changes(subscription, plans, customer, options = {})
|
10
|
+
subscription.merge!(custom_subscription_params(plans, customer, options))
|
11
|
+
items = options[:items]
|
12
|
+
items = items.values if items.respond_to?(:values)
|
13
|
+
subscription[:items][:data] = plans.map do |plan|
|
14
|
+
if items && items.size == plans.size
|
15
|
+
quantity = items &&
|
16
|
+
items.detect { |item| item[:plan] == plan[:id] }[:quantity] || 1
|
17
|
+
Data.mock_subscription_item({ plan: plan, quantity: quantity })
|
18
|
+
else
|
19
|
+
Data.mock_subscription_item({ plan: plan })
|
20
|
+
end
|
21
|
+
end
|
22
|
+
subscription
|
23
|
+
end
|
24
|
+
|
25
|
+
def custom_subscription_params(plans, cus, options = {})
|
10
26
|
verify_trial_end(options[:trial_end]) if options[:trial_end]
|
11
27
|
|
12
|
-
|
13
|
-
|
14
|
-
|
28
|
+
plan = plans.first if plans.size == 1
|
29
|
+
|
30
|
+
now = Time.now.utc.to_i
|
31
|
+
created_time = options[:created] || now
|
32
|
+
start_time = options[:current_period_start] || now
|
33
|
+
params = { customer: cus[:id], current_period_start: start_time, created: created_time }
|
34
|
+
params.merge!({ :plan => (plans.size == 1 ? plans.first : nil) })
|
35
|
+
keys_to_merge = /application_fee_percent|quantity|metadata|tax_percent|billing|days_until_due/
|
36
|
+
params.merge! options.select {|k,v| k =~ keys_to_merge}
|
37
|
+
|
38
|
+
if options[:cancel_at_period_end] == true
|
39
|
+
params.merge!(cancel_at_period_end: true, canceled_at: now)
|
40
|
+
elsif options[:cancel_at_period_end] == false
|
41
|
+
params.merge!(cancel_at_period_end: false, canceled_at: nil)
|
42
|
+
end
|
43
|
+
|
15
44
|
# TODO: Implement coupon logic
|
16
45
|
|
17
|
-
if (plan[:trial_period_days]
|
18
|
-
end_time = get_ending_time(start_time, plan)
|
19
|
-
params.merge!({status: 'active', current_period_end: end_time, trial_start: nil, trial_end: nil})
|
46
|
+
if (((plan && plan[:trial_period_days]) || 0) == 0 && options[:trial_end].nil?) || options[:trial_end] == "now"
|
47
|
+
end_time = options[:billing_cycle_anchor] || get_ending_time(start_time, plan)
|
48
|
+
params.merge!({status: 'active', current_period_end: end_time, trial_start: nil, trial_end: nil, billing_cycle_anchor: options[:billing_cycle_anchor]})
|
20
49
|
else
|
21
50
|
end_time = options[:trial_end] || (Time.now.utc.to_i + plan[:trial_period_days]*86400)
|
22
|
-
params.merge!({status: 'trialing', current_period_end: end_time, trial_start: start_time, trial_end: end_time})
|
51
|
+
params.merge!({status: 'trialing', current_period_end: end_time, trial_start: start_time, trial_end: end_time, billing_cycle_anchor: nil})
|
23
52
|
end
|
24
53
|
|
25
54
|
params
|
26
55
|
end
|
27
56
|
|
28
57
|
def add_subscription_to_customer(cus, sub)
|
29
|
-
|
30
|
-
|
58
|
+
if sub[:trial_end].nil? || sub[:trial_end] == "now"
|
59
|
+
id = new_id('ch')
|
60
|
+
charges[id] = Data.mock_charge(
|
61
|
+
:id => id,
|
62
|
+
:customer => cus[:id],
|
63
|
+
:amount => (sub[:plan] ? sub[:plan][:amount] : total_items_amount(sub[:items][:data]))
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
31
67
|
if cus[:currency].nil?
|
32
|
-
cus[:currency] = sub[:plan][:currency]
|
33
|
-
elsif cus[:currency] != sub[:plan][:currency]
|
34
|
-
raise Stripe::InvalidRequestError.new( "Can't combine currencies on a single customer. This customer has had a subscription, coupon, or invoice item with currency #{cus[:currency]}", 'currency', 400)
|
68
|
+
cus[:currency] = sub[:items][:data][0][:plan][:currency]
|
69
|
+
elsif cus[:currency] != sub[:items][:data][0][:plan][:currency]
|
70
|
+
raise Stripe::InvalidRequestError.new( "Can't combine currencies on a single customer. This customer has had a subscription, coupon, or invoice item with currency #{cus[:currency]}", 'currency', http_status: 400)
|
35
71
|
end
|
36
72
|
cus[:subscriptions][:total_count] = (cus[:subscriptions][:total_count] || 0) + 1
|
37
73
|
cus[:subscriptions][:data].unshift sub
|
@@ -47,6 +83,8 @@ module StripeMock
|
|
47
83
|
# `intervals` is set to 1 when calculating current_period_end from current_period_start & plan
|
48
84
|
# `intervals` is set to 2 when calculating Stripe::Invoice.upcoming end from current_period_start & plan
|
49
85
|
def get_ending_time(start_time, plan, intervals = 1)
|
86
|
+
return start_time unless plan
|
87
|
+
|
50
88
|
case plan[:interval]
|
51
89
|
when "week"
|
52
90
|
start_time + (604800 * (plan[:interval_count] || 1) * intervals)
|
@@ -62,15 +100,20 @@ module StripeMock
|
|
62
100
|
def verify_trial_end(trial_end)
|
63
101
|
if trial_end != "now"
|
64
102
|
if !trial_end.is_a? Integer
|
65
|
-
raise Stripe::InvalidRequestError.new('Invalid timestamp: must be an integer', nil, 400)
|
103
|
+
raise Stripe::InvalidRequestError.new('Invalid timestamp: must be an integer', nil, http_status: 400)
|
66
104
|
elsif trial_end < Time.now.utc.to_i
|
67
|
-
raise Stripe::InvalidRequestError.new('Invalid timestamp: must be an integer Unix timestamp in the future', nil, 400)
|
105
|
+
raise Stripe::InvalidRequestError.new('Invalid timestamp: must be an integer Unix timestamp in the future', nil, http_status: 400)
|
68
106
|
elsif trial_end > Time.now.utc.to_i + 31557600*5 # five years
|
69
|
-
raise Stripe::InvalidRequestError.new('Invalid timestamp: can be no more than five years in the future', nil, 400)
|
107
|
+
raise Stripe::InvalidRequestError.new('Invalid timestamp: can be no more than five years in the future', nil, http_status: 400)
|
70
108
|
end
|
71
109
|
end
|
72
110
|
end
|
73
111
|
|
112
|
+
def total_items_amount(items)
|
113
|
+
total = 0
|
114
|
+
items.each { |i| total += (i[:quantity] || 1) * i[:plan][:amount] }
|
115
|
+
total
|
116
|
+
end
|
74
117
|
end
|
75
118
|
end
|
76
119
|
end
|
@@ -28,14 +28,15 @@ module StripeMock
|
|
28
28
|
if token.nil? || @card_tokens[token].nil?
|
29
29
|
# TODO: Make this strict
|
30
30
|
msg = "Invalid token id: #{token}"
|
31
|
-
raise Stripe::InvalidRequestError.new(msg, 'tok', 404)
|
31
|
+
raise Stripe::InvalidRequestError.new(msg, 'tok', http_status: 404)
|
32
32
|
else
|
33
33
|
@card_tokens.delete(token)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
37
|
def get_card_or_bank_by_token(token)
|
38
|
-
|
38
|
+
token_id = token['id'] || token
|
39
|
+
@card_tokens[token_id] || @bank_tokens[token_id] || raise(Stripe::InvalidRequestError.new("Invalid token id: #{token_id}", 'tok', http_status: 404))
|
39
40
|
end
|
40
41
|
|
41
42
|
end
|
@@ -52,29 +52,98 @@ module StripeMock
|
|
52
52
|
def pay_invoice(route, method_url, params, headers)
|
53
53
|
route =~ method_url
|
54
54
|
assert_existence :invoice, $1, invoices[$1]
|
55
|
-
invoices[$1]
|
55
|
+
charge = invoice_charge(invoices[$1])
|
56
|
+
invoices[$1].merge!(:paid => true, :attempted => true, :charge => charge[:id])
|
56
57
|
end
|
57
58
|
|
58
59
|
def upcoming_invoice(route, method_url, params, headers)
|
59
60
|
route =~ method_url
|
60
|
-
raise Stripe::InvalidRequestError.new('Missing required param: customer', nil, 400) if params[:customer].nil?
|
61
|
+
raise Stripe::InvalidRequestError.new('Missing required param: customer', nil, http_status: 400) if params[:customer].nil?
|
62
|
+
raise Stripe::InvalidRequestError.new('When previewing changes to a subscription, you must specify either `subscription` or `subscription_items`', nil, http_status: 400) if !params[:subscription_proration_date].nil? && params[:subscription].nil? && params[:subscription_plan].nil?
|
63
|
+
raise Stripe::InvalidRequestError.new('Cannot specify proration date without specifying a subscription', nil, http_status: 400) if !params[:subscription_proration_date].nil? && params[:subscription].nil?
|
61
64
|
|
62
65
|
customer = customers[params[:customer]]
|
63
66
|
assert_existence :customer, params[:customer], customer
|
64
67
|
|
65
|
-
raise Stripe::InvalidRequestError.new("No upcoming invoices for customer: #{customer[:id]}", nil, 404) if customer[:subscriptions][:data].length == 0
|
68
|
+
raise Stripe::InvalidRequestError.new("No upcoming invoices for customer: #{customer[:id]}", nil, http_status: 404) if customer[:subscriptions][:data].length == 0
|
66
69
|
|
67
|
-
|
68
|
-
|
70
|
+
subscription =
|
71
|
+
if params[:subscription]
|
72
|
+
customer[:subscriptions][:data].select{|s|s[:id] == params[:subscription]}.first
|
73
|
+
else
|
74
|
+
customer[:subscriptions][:data].min_by { |sub| sub[:current_period_end] }
|
75
|
+
end
|
69
76
|
|
70
|
-
|
71
|
-
|
72
|
-
|
77
|
+
if params[:subscription_proration_date] && !((subscription[:current_period_start]..subscription[:current_period_end]) === params[:subscription_proration_date])
|
78
|
+
raise Stripe::InvalidRequestError.new('Cannot specify proration date outside of current subscription period', nil, http_status: 400)
|
79
|
+
end
|
80
|
+
|
81
|
+
prorating = false
|
82
|
+
subscription_proration_date = nil
|
83
|
+
subscription_plan_id = params[:subscription_plan] || subscription[:plan][:id]
|
84
|
+
subscription_quantity = params[:subscription_quantity] || subscription[:quantity]
|
85
|
+
if subscription_plan_id != subscription[:plan][:id] || subscription_quantity != subscription[:quantity]
|
86
|
+
prorating = true
|
87
|
+
invoice_date = Time.now.to_i
|
88
|
+
subscription_plan = assert_existence :plan, subscription_plan_id, plans[subscription_plan_id.to_s]
|
89
|
+
preview_subscription = Data.mock_subscription
|
90
|
+
preview_subscription = resolve_subscription_changes(preview_subscription, [subscription_plan], customer, { trial_end: params[:subscription_trial_end] })
|
91
|
+
preview_subscription[:id] = subscription[:id]
|
92
|
+
preview_subscription[:quantity] = subscription_quantity
|
93
|
+
subscription_proration_date = params[:subscription_proration_date] || Time.now
|
94
|
+
else
|
95
|
+
preview_subscription = subscription
|
96
|
+
invoice_date = subscription[:current_period_end]
|
97
|
+
end
|
98
|
+
|
99
|
+
invoice_lines = []
|
100
|
+
|
101
|
+
if prorating
|
102
|
+
unused_amount = subscription[:plan][:amount] * subscription[:quantity] * (subscription[:current_period_end] - subscription_proration_date.to_i) / (subscription[:current_period_end] - subscription[:current_period_start])
|
103
|
+
invoice_lines << Data.mock_line_item(
|
104
|
+
id: new_id('ii'),
|
105
|
+
amount: -unused_amount,
|
106
|
+
description: 'Unused time',
|
107
|
+
plan: subscription[:plan],
|
108
|
+
period: {
|
109
|
+
start: subscription_proration_date.to_i,
|
110
|
+
end: subscription[:current_period_end]
|
111
|
+
},
|
112
|
+
quantity: subscription[:quantity],
|
113
|
+
proration: true
|
114
|
+
)
|
115
|
+
|
116
|
+
preview_plan = assert_existence :plan, params[:subscription_plan], plans[params[:subscription_plan]]
|
117
|
+
if preview_plan[:interval] == subscription[:plan][:interval] && preview_plan[:interval_count] == subscription[:plan][:interval_count] && params[:subscription_trial_end].nil?
|
118
|
+
remaining_amount = preview_plan[:amount] * subscription_quantity * (subscription[:current_period_end] - subscription_proration_date.to_i) / (subscription[:current_period_end] - subscription[:current_period_start])
|
119
|
+
invoice_lines << Data.mock_line_item(
|
120
|
+
id: new_id('ii'),
|
121
|
+
amount: remaining_amount,
|
122
|
+
description: 'Remaining time',
|
123
|
+
plan: preview_plan,
|
124
|
+
period: {
|
125
|
+
start: subscription_proration_date.to_i,
|
126
|
+
end: subscription[:current_period_end]
|
127
|
+
},
|
128
|
+
quantity: subscription_quantity,
|
129
|
+
proration: true
|
130
|
+
)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
subscription_line = get_mock_subscription_line_item(preview_subscription)
|
135
|
+
invoice_lines << subscription_line
|
136
|
+
|
137
|
+
Data.mock_invoice(invoice_lines,
|
138
|
+
id: new_id('in'),
|
73
139
|
customer: customer[:id],
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
140
|
+
discount: customer[:discount],
|
141
|
+
created: invoice_date,
|
142
|
+
starting_balance: customer[:account_balance],
|
143
|
+
subscription: preview_subscription[:id],
|
144
|
+
period_start: prorating ? invoice_date : preview_subscription[:current_period_start],
|
145
|
+
period_end: prorating ? invoice_date : preview_subscription[:current_period_end],
|
146
|
+
next_payment_attempt: preview_subscription[:current_period_end] + 3600 )
|
78
147
|
end
|
79
148
|
|
80
149
|
private
|
@@ -84,15 +153,25 @@ module StripeMock
|
|
84
153
|
id: subscription[:id],
|
85
154
|
type: "subscription",
|
86
155
|
plan: subscription[:plan],
|
87
|
-
amount: subscription[:plan][:amount],
|
156
|
+
amount: subscription[:status] == 'trialing' ? 0 : subscription[:plan][:amount] * subscription[:quantity],
|
88
157
|
discountable: true,
|
89
|
-
quantity:
|
158
|
+
quantity: subscription[:quantity],
|
90
159
|
period: {
|
91
160
|
start: subscription[:current_period_end],
|
92
161
|
end: get_ending_time(subscription[:current_period_start], subscription[:plan], 2)
|
93
162
|
})
|
94
163
|
end
|
95
164
|
|
165
|
+
## charge the customer on the invoice, if one does not exist, create
|
166
|
+
#anonymous charge
|
167
|
+
def invoice_charge(invoice)
|
168
|
+
begin
|
169
|
+
new_charge(nil, nil, {customer: invoice[:customer]["id"], amount: invoice[:amount_due], currency: StripeMock.default_currency}, nil)
|
170
|
+
rescue Stripe::InvalidRequestError
|
171
|
+
new_charge(nil, nil, {source: generate_card_token, amount: invoice[:amount_due], currency: StripeMock.default_currency}, nil)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
96
175
|
end
|
97
176
|
end
|
98
177
|
end
|
@@ -15,16 +15,16 @@ module StripeMock
|
|
15
15
|
order_items = []
|
16
16
|
|
17
17
|
unless params[:currency].to_s.size == 3
|
18
|
-
raise Stripe::InvalidRequestError.new('You must supply a currency', nil, 400)
|
18
|
+
raise Stripe::InvalidRequestError.new('You must supply a currency', nil, http_status: 400)
|
19
19
|
end
|
20
20
|
|
21
21
|
if params[:items]
|
22
22
|
unless params[:items].is_a? Array
|
23
|
-
raise Stripe::InvalidRequestError.new('You must supply a list of items', nil, 400)
|
23
|
+
raise Stripe::InvalidRequestError.new('You must supply a list of items', nil, http_status: 400)
|
24
24
|
end
|
25
25
|
|
26
26
|
unless params[:items].first.is_a? Hash
|
27
|
-
raise Stripe::InvalidRequestError.new('You must supply an item', nil, 400)
|
27
|
+
raise Stripe::InvalidRequestError.new('You must supply an item', nil, http_status: 400)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -45,7 +45,7 @@ module StripeMock
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
if
|
48
|
+
if %w(created paid canceled fulfilled returned).include? params[:status]
|
49
49
|
order[:status] = params[:status]
|
50
50
|
end
|
51
51
|
order
|
@@ -61,7 +61,7 @@ module StripeMock
|
|
61
61
|
order = assert_existence :order, $1, orders[$1]
|
62
62
|
|
63
63
|
if params[:source].blank? && params[:customer].blank?
|
64
|
-
raise Stripe::InvalidRequestError.new('You must supply a source or customer', nil, 400)
|
64
|
+
raise Stripe::InvalidRequestError.new('You must supply a source or customer', nil, http_status: 400)
|
65
65
|
end
|
66
66
|
|
67
67
|
charge_id = new_id('ch')
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module StripeMock
|
2
|
+
module RequestHandlers
|
3
|
+
module Payouts
|
4
|
+
|
5
|
+
def Payouts.included(klass)
|
6
|
+
klass.add_handler 'post /v1/payouts', :new_payout
|
7
|
+
klass.add_handler 'get /v1/payouts', :list_payouts
|
8
|
+
klass.add_handler 'get /v1/payouts/(.*)', :get_payout
|
9
|
+
end
|
10
|
+
|
11
|
+
def new_payout(route, method_url, params, headers)
|
12
|
+
id = new_id('po')
|
13
|
+
|
14
|
+
unless params[:amount].is_a?(Integer) || (params[:amount].is_a?(String) && /^\d+$/.match(params[:amount]))
|
15
|
+
raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400)
|
16
|
+
end
|
17
|
+
|
18
|
+
payouts[id] = Data.mock_payout(params.merge :id => id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def list_payouts(route, method_url, params, headers)
|
22
|
+
Data.mock_list_object(payouts.clone.values, params)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_payout(route, method_url, params, headers)
|
26
|
+
route =~ method_url
|
27
|
+
assert_existence :payout, $1, payouts[$1]
|
28
|
+
payouts[$1] ||= Data.mock_payout(:id => $1)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module StripeMock
|
2
|
+
module RequestHandlers
|
3
|
+
module Products
|
4
|
+
def self.included(base)
|
5
|
+
base.add_handler 'post /v1/products', :create_product
|
6
|
+
base.add_handler 'get /v1/products/(.*)', :retrieve_product
|
7
|
+
base.add_handler 'post /v1/products/(.*)', :update_product
|
8
|
+
base.add_handler 'get /v1/products', :list_products
|
9
|
+
base.add_handler 'delete /v1/products/(.*)', :destroy_product
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_product(_route, _method_url, params, _headers)
|
13
|
+
params[:id] ||= new_id('prod')
|
14
|
+
products[params[:id]] = Data.mock_product(params)
|
15
|
+
end
|
16
|
+
|
17
|
+
def retrieve_product(route, method_url, _params, _headers)
|
18
|
+
id = method_url.match(route).captures.first
|
19
|
+
assert_existence :product, id, products[id]
|
20
|
+
end
|
21
|
+
|
22
|
+
def update_product(route, method_url, params, _headers)
|
23
|
+
id = method_url.match(route).captures.first
|
24
|
+
product = assert_existence :product, id, products[id]
|
25
|
+
|
26
|
+
product.merge!(params)
|
27
|
+
end
|
28
|
+
|
29
|
+
def list_products(_route, _method_url, params, _headers)
|
30
|
+
limit = params[:limit] || 10
|
31
|
+
Data.mock_list_object(products.values.take(limit), params)
|
32
|
+
end
|
33
|
+
|
34
|
+
def destroy_product(route, method_url, _params, _headers)
|
35
|
+
id = method_url.match(route).captures.first
|
36
|
+
assert_existence :product, id, products[id]
|
37
|
+
|
38
|
+
products.delete(id)
|
39
|
+
{ id: id, object: 'product', deleted: true }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -12,6 +12,18 @@ module StripeMock
|
|
12
12
|
params[:id] ||= new_id('rp')
|
13
13
|
cards = []
|
14
14
|
|
15
|
+
if params[:name].nil?
|
16
|
+
raise StripeMock::StripeMockError.new("Missing required parameter name for recipients.")
|
17
|
+
end
|
18
|
+
|
19
|
+
if params[:type].nil?
|
20
|
+
raise StripeMock::StripeMockError.new("Missing required parameter type for recipients.")
|
21
|
+
end
|
22
|
+
|
23
|
+
unless %w(individual corporation).include?(params[:type])
|
24
|
+
raise StripeMock::StripeMockError.new("Type must be either individual or corporation..")
|
25
|
+
end
|
26
|
+
|
15
27
|
if params[:bank_account]
|
16
28
|
params[:active_account] = get_bank_by_token(params.delete(:bank_account))
|
17
29
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module StripeMock
|
2
|
+
module RequestHandlers
|
3
|
+
module Refunds
|
4
|
+
|
5
|
+
def Refunds.included(klass)
|
6
|
+
klass.add_handler 'post /v1/refunds', :new_refund
|
7
|
+
klass.add_handler 'get /v1/refunds', :get_refunds
|
8
|
+
klass.add_handler 'get /v1/refunds/(.*)', :get_refund
|
9
|
+
klass.add_handler 'post /v1/refunds/(.*)', :update_refund
|
10
|
+
end
|
11
|
+
|
12
|
+
def new_refund(route, method_url, params, headers)
|
13
|
+
if headers && headers[:idempotency_key]
|
14
|
+
params[:idempotency_key] = headers[:idempotency_key]
|
15
|
+
if refunds.any?
|
16
|
+
original_refund = refunds.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]}
|
17
|
+
return refunds[original_refund[:id]] if original_refund
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
charge = assert_existence :charge, params[:charge], charges[params[:charge]]
|
22
|
+
params[:amount] ||= charge[:amount]
|
23
|
+
id = new_id('re')
|
24
|
+
bal_trans_params = {
|
25
|
+
amount: params[:amount] * -1,
|
26
|
+
source: id,
|
27
|
+
type: 'refund'
|
28
|
+
}
|
29
|
+
balance_transaction_id = new_balance_transaction('txn', bal_trans_params)
|
30
|
+
refund = Data.mock_refund params.merge(
|
31
|
+
:balance_transaction => balance_transaction_id,
|
32
|
+
:id => id,
|
33
|
+
:charge => charge[:id],
|
34
|
+
)
|
35
|
+
add_refund_to_charge(refund, charge)
|
36
|
+
refunds[id] = refund
|
37
|
+
|
38
|
+
if params[:expand] == ['balance_transaction']
|
39
|
+
refunds[id][:balance_transaction] =
|
40
|
+
balance_transactions[balance_transaction_id]
|
41
|
+
end
|
42
|
+
refund
|
43
|
+
end
|
44
|
+
|
45
|
+
def update_refund(route, method_url, params, headers)
|
46
|
+
route =~ method_url
|
47
|
+
id = $1
|
48
|
+
|
49
|
+
refund = assert_existence :refund, id, refunds[id]
|
50
|
+
allowed = allowed_refund_params(params)
|
51
|
+
disallowed = params.keys - allowed
|
52
|
+
if disallowed.count > 0
|
53
|
+
raise Stripe::InvalidRequestError.new("Received unknown parameters: #{disallowed.join(', ')}" , '', http_status: 400)
|
54
|
+
end
|
55
|
+
|
56
|
+
refunds[id] = Util.rmerge(refund, params)
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_refunds(route, method_url, params, headers)
|
60
|
+
params[:offset] ||= 0
|
61
|
+
params[:limit] ||= 10
|
62
|
+
|
63
|
+
clone = refunds.clone
|
64
|
+
|
65
|
+
Data.mock_list_object(clone.values, params)
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_refund(route, method_url, params, headers)
|
69
|
+
route =~ method_url
|
70
|
+
refund_id = $1 || params[:refund]
|
71
|
+
assert_existence :refund, refund_id, refunds[refund_id]
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def ensure_refund_required_params(params)
|
77
|
+
if non_integer_charge_amount?(params)
|
78
|
+
raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400)
|
79
|
+
elsif non_positive_charge_amount?(params)
|
80
|
+
raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', http_status: 400)
|
81
|
+
elsif params[:charge].nil?
|
82
|
+
raise Stripe::InvalidRequestError.new('Must provide the identifier of the charge to refund.', nil)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def allowed_refund_params(params)
|
87
|
+
[:metadata]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|