stripe-ruby-mock 2.4.0 → 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 -14
- data/lib/stripe_mock/api/instance.rb +6 -6
- data/lib/stripe_mock/api/webhooks.rb +5 -1
- data/lib/stripe_mock/client.rb +18 -2
- data/lib/stripe_mock/data/list.rb +14 -2
- data/lib/stripe_mock/data.rb +217 -56
- data/lib/stripe_mock/instance.rb +93 -8
- data/lib/stripe_mock/request_handlers/accounts.rb +34 -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 +25 -17
- data/lib/stripe_mock/request_handlers/coupons.rb +5 -4
- data/lib/stripe_mock/request_handlers/customers.rb +26 -8
- 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 +55 -15
- data/lib/stripe_mock/request_handlers/helpers/token_helpers.rb +2 -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/refunds.rb +10 -7
- data/lib/stripe_mock/request_handlers/subscription_items.rb +36 -0
- data/lib/stripe_mock/request_handlers/subscriptions.rb +95 -34
- 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 +10 -1
- data/lib/stripe_mock/server.rb +14 -1
- data/lib/stripe_mock/test_strategies/base.rb +7 -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.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 +13 -0
- data/spec/instance_spec.rb +54 -4
- data/spec/integration_examples/prepare_error_examples.rb +6 -6
- data/spec/list_spec.rb +16 -3
- data/spec/readme_spec.rb +2 -0
- data/spec/server_spec.rb +6 -2
- data/spec/shared_stripe_examples/account_examples.rb +35 -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 +10 -3
- data/spec/shared_stripe_examples/charge_examples.rb +73 -17
- data/spec/shared_stripe_examples/coupon_examples.rb +8 -2
- data/spec/shared_stripe_examples/customer_examples.rb +54 -2
- 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 +3 -3
- 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 +37 -5
- data/spec/shared_stripe_examples/product_example.rb +65 -0
- data/spec/shared_stripe_examples/recipient_examples.rb +7 -7
- data/spec/shared_stripe_examples/refund_examples.rb +17 -11
- data/spec/shared_stripe_examples/subscription_examples.rb +463 -33
- 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 +7 -1
- data/spec/util_spec.rb +35 -1
- data/stripe-ruby-mock.gemspec +1 -1
- metadata +34 -7
- data/ChangeLog.rdoc +0 -4
@@ -1,6 +1,7 @@
|
|
1
1
|
module StripeMock
|
2
2
|
module RequestHandlers
|
3
3
|
module Accounts
|
4
|
+
VALID_START_YEAR = 2009
|
4
5
|
|
5
6
|
def Accounts.included(klass)
|
6
7
|
klass.add_handler 'post /v1/accounts', :new_account
|
@@ -26,8 +27,14 @@ module StripeMock
|
|
26
27
|
|
27
28
|
def update_account(route, method_url, params, headers)
|
28
29
|
route =~ method_url
|
29
|
-
assert_existence :account, $1, accounts[$1]
|
30
|
-
|
30
|
+
account = assert_existence :account, $1, accounts[$1]
|
31
|
+
account.merge!(params)
|
32
|
+
if blank_value?(params[:tos_acceptance], :date)
|
33
|
+
raise Stripe::InvalidRequestError.new("Invalid integer: ", "tos_acceptance[date]", http_status: 400)
|
34
|
+
elsif params[:tos_acceptance] && params[:tos_acceptance][:date]
|
35
|
+
validate_acceptance_date(params[:tos_acceptance][:date])
|
36
|
+
end
|
37
|
+
account
|
31
38
|
end
|
32
39
|
|
33
40
|
def list_accounts(route, method_url, params, headers)
|
@@ -49,6 +56,31 @@ module StripeMock
|
|
49
56
|
accounts[acc[:id]] = acc
|
50
57
|
end
|
51
58
|
end
|
59
|
+
|
60
|
+
# Checks if setting a blank value
|
61
|
+
#
|
62
|
+
# returns true if the key is included in the hash
|
63
|
+
# and its value is empty or nil
|
64
|
+
def blank_value?(hash, key)
|
65
|
+
if hash.key?(key)
|
66
|
+
value = hash[key]
|
67
|
+
return true if value.nil? || "" == value
|
68
|
+
end
|
69
|
+
false
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate_acceptance_date(unix_date)
|
73
|
+
unix_now = Time.now.strftime("%s").to_i
|
74
|
+
formatted_date = Time.at(unix_date)
|
75
|
+
|
76
|
+
return if formatted_date.year >= VALID_START_YEAR && unix_now >= unix_date
|
77
|
+
|
78
|
+
raise Stripe::InvalidRequestError.new(
|
79
|
+
"ToS acceptance date is not valid. Dates are expected to be integers, measured in seconds, not in the future, and after 2009",
|
80
|
+
"tos_acceptance[date]",
|
81
|
+
http_status: 400
|
82
|
+
)
|
83
|
+
end
|
52
84
|
end
|
53
85
|
end
|
54
86
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module StripeMock
|
2
|
+
module RequestHandlers
|
3
|
+
module Balance
|
4
|
+
|
5
|
+
def Balance.included(klass)
|
6
|
+
klass.add_handler 'get /v1/balance', :get_balance
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_balance(route, method_url, params, headers)
|
10
|
+
route =~ method_url
|
11
|
+
|
12
|
+
return_balance = Data.mock_balance(account_balance)
|
13
|
+
return_balance
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -9,11 +9,27 @@ module StripeMock
|
|
9
9
|
|
10
10
|
def get_balance_transaction(route, method_url, params, headers)
|
11
11
|
route =~ method_url
|
12
|
-
assert_existence :balance_transaction, $1, balance_transactions[$1]
|
12
|
+
assert_existence :balance_transaction, $1, hide_additional_attributes(balance_transactions[$1])
|
13
13
|
end
|
14
14
|
|
15
15
|
def list_balance_transactions(route, method_url, params, headers)
|
16
|
-
|
16
|
+
values = balance_transactions.values
|
17
|
+
if params.has_key?(:transfer)
|
18
|
+
# If transfer supplied as params, need to filter the btxns returned to only include those with the specified transfer id
|
19
|
+
values = values.select{|btxn| btxn[:transfer] == params[:transfer]}
|
20
|
+
end
|
21
|
+
Data.mock_list_object(values.map{|btxn| hide_additional_attributes(btxn)}, params)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def hide_additional_attributes(btxn)
|
27
|
+
# For automatic Stripe transfers, the transfer attribute on balance_transaction stores the transfer which
|
28
|
+
# included this balance_transaction. However, it is not exposed as a field returned on a balance_transaction.
|
29
|
+
# Therefore, need to not show this attribute if it exists.
|
30
|
+
if !btxn.nil?
|
31
|
+
btxn.reject{|k,v| k == :transfer }
|
32
|
+
end
|
17
33
|
end
|
18
34
|
|
19
35
|
end
|
@@ -13,9 +13,12 @@ module StripeMock
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def new_charge(route, method_url, params, headers)
|
16
|
-
if
|
17
|
-
|
18
|
-
|
16
|
+
if headers && headers[:idempotency_key]
|
17
|
+
params[:idempotency_key] = headers[:idempotency_key]
|
18
|
+
if charges.any?
|
19
|
+
original_charge = charges.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]}
|
20
|
+
return charges[original_charge[:id]] if original_charge
|
21
|
+
end
|
19
22
|
end
|
20
23
|
|
21
24
|
id = new_id('ch')
|
@@ -31,7 +34,7 @@ module StripeMock
|
|
31
34
|
params[:source] = get_card_or_bank_by_token(params[:source])
|
32
35
|
end
|
33
36
|
elsif params[:source][:id]
|
34
|
-
raise Stripe::InvalidRequestError.new("Invalid token id: #{params[:source]}", 'card', 400)
|
37
|
+
raise Stripe::InvalidRequestError.new("Invalid token id: #{params[:source]}", 'card', http_status: 400)
|
35
38
|
end
|
36
39
|
elsif params[:customer]
|
37
40
|
customer = customers[params[:customer]]
|
@@ -41,7 +44,7 @@ module StripeMock
|
|
41
44
|
end
|
42
45
|
|
43
46
|
ensure_required_params(params)
|
44
|
-
bal_trans_params = { amount: params[:amount], source: params[:
|
47
|
+
bal_trans_params = { amount: params[:amount], source: id, application_fee: params[:application_fee] }
|
45
48
|
|
46
49
|
balance_transaction_id = new_balance_transaction('txn', bal_trans_params)
|
47
50
|
|
@@ -49,12 +52,13 @@ module StripeMock
|
|
49
52
|
params.merge :id => id,
|
50
53
|
:balance_transaction => balance_transaction_id)
|
51
54
|
|
55
|
+
charge = charges[id].clone
|
52
56
|
if params[:expand] == ['balance_transaction']
|
53
|
-
|
57
|
+
charge[:balance_transaction] =
|
54
58
|
balance_transactions[balance_transaction_id]
|
55
59
|
end
|
56
60
|
|
57
|
-
|
61
|
+
charge
|
58
62
|
end
|
59
63
|
|
60
64
|
def update_charge(route, method_url, params, headers)
|
@@ -65,7 +69,7 @@ module StripeMock
|
|
65
69
|
allowed = allowed_params(params)
|
66
70
|
disallowed = params.keys - allowed
|
67
71
|
if disallowed.count > 0
|
68
|
-
raise Stripe::InvalidRequestError.new("Received unknown parameters: #{disallowed.join(', ')}" , '', 400)
|
72
|
+
raise Stripe::InvalidRequestError.new("Received unknown parameters: #{disallowed.join(', ')}" , '', http_status: 400)
|
69
73
|
end
|
70
74
|
|
71
75
|
charges[id] = Util.rmerge(charge, params)
|
@@ -87,7 +91,15 @@ module StripeMock
|
|
87
91
|
def get_charge(route, method_url, params, headers)
|
88
92
|
route =~ method_url
|
89
93
|
charge_id = $1 || params[:charge]
|
90
|
-
assert_existence :charge, charge_id, charges[charge_id]
|
94
|
+
charge = assert_existence :charge, charge_id, charges[charge_id]
|
95
|
+
|
96
|
+
charge = charge.clone
|
97
|
+
if params[:expand] == ['balance_transaction']
|
98
|
+
balance_transaction = balance_transactions[charge[:balance_transaction]]
|
99
|
+
charge[:balance_transaction] = balance_transaction
|
100
|
+
end
|
101
|
+
|
102
|
+
charge
|
91
103
|
end
|
92
104
|
|
93
105
|
def capture_charge(route, method_url, params, headers)
|
@@ -130,11 +142,11 @@ module StripeMock
|
|
130
142
|
elsif params[:currency].nil?
|
131
143
|
require_param(:currency)
|
132
144
|
elsif non_integer_charge_amount?(params)
|
133
|
-
raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', 400)
|
145
|
+
raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400)
|
134
146
|
elsif non_positive_charge_amount?(params)
|
135
|
-
raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', 400)
|
147
|
+
raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', http_status: 400)
|
136
148
|
elsif params[:source].nil? && params[:customer].nil?
|
137
|
-
raise Stripe::InvalidRequestError.new('Must provide source or customer.', nil)
|
149
|
+
raise Stripe::InvalidRequestError.new('Must provide source or customer.', http_status: nil)
|
138
150
|
end
|
139
151
|
end
|
140
152
|
|
@@ -146,12 +158,8 @@ module StripeMock
|
|
146
158
|
params[:amount] && params[:amount] < 1
|
147
159
|
end
|
148
160
|
|
149
|
-
def require_param(param)
|
150
|
-
raise Stripe::InvalidRequestError.new("Missing required param: #{param}", param.to_s, 400)
|
151
|
-
end
|
152
|
-
|
153
161
|
def allowed_params(params)
|
154
|
-
allowed = [:description, :metadata, :receipt_email, :fraud_details, :shipping]
|
162
|
+
allowed = [:description, :metadata, :receipt_email, :fraud_details, :shipping, :destination]
|
155
163
|
|
156
164
|
# This is a workaround for the way the Stripe API sends params even when they aren't modified.
|
157
165
|
# Stipe will include those params even when they aren't modified.
|
@@ -11,18 +11,19 @@ module StripeMock
|
|
11
11
|
|
12
12
|
def new_coupon(route, method_url, params, headers)
|
13
13
|
params[:id] ||= new_id('coupon')
|
14
|
-
raise Stripe::InvalidRequestError.new('Missing required param: duration', 'coupon', 400) unless params[:duration]
|
15
|
-
|
14
|
+
raise Stripe::InvalidRequestError.new('Missing required param: duration', 'coupon', http_status: 400) unless params[:duration]
|
15
|
+
raise Stripe::InvalidRequestError.new('You must pass currency when passing amount_off', 'coupon', http_status: 400) if params[:amount_off] && !params[:currency]
|
16
|
+
coupons[ params[:id] ] = Data.mock_coupon({amount_off: nil, percent_off:nil}.merge(params))
|
16
17
|
end
|
17
18
|
|
18
19
|
def get_coupon(route, method_url, params, headers)
|
19
20
|
route =~ method_url
|
20
|
-
assert_existence :
|
21
|
+
assert_existence :coupon, $1, coupons[$1]
|
21
22
|
end
|
22
23
|
|
23
24
|
def delete_coupon(route, method_url, params, headers)
|
24
25
|
route =~ method_url
|
25
|
-
assert_existence :
|
26
|
+
assert_existence :coupon, $1, coupons.delete($1)
|
26
27
|
end
|
27
28
|
|
28
29
|
def list_coupons(route, method_url, params, headers)
|
@@ -8,6 +8,7 @@ module StripeMock
|
|
8
8
|
klass.add_handler 'get /v1/customers/([^/]*)', :get_customer
|
9
9
|
klass.add_handler 'delete /v1/customers/([^/]*)', :delete_customer
|
10
10
|
klass.add_handler 'get /v1/customers', :list_customers
|
11
|
+
klass.add_handler 'delete /v1/customers/([^/]*)/discount', :delete_customer_discount
|
11
12
|
end
|
12
13
|
|
13
14
|
def new_customer(route, method_url, params, headers)
|
@@ -18,7 +19,7 @@ module StripeMock
|
|
18
19
|
new_card =
|
19
20
|
if params[:source].is_a?(Hash)
|
20
21
|
unless params[:source][:object] && params[:source][:number] && params[:source][:exp_month] && params[:source][:exp_year]
|
21
|
-
raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, 400)
|
22
|
+
raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, http_status: 400)
|
22
23
|
end
|
23
24
|
card_from_params(params[:source])
|
24
25
|
else
|
@@ -35,22 +36,22 @@ module StripeMock
|
|
35
36
|
plan = assert_existence :plan, plan_id, plans[plan_id]
|
36
37
|
|
37
38
|
if params[:default_source].nil? && params[:trial_end].nil? && plan[:trial_period_days].nil? && plan[:amount] != 0
|
38
|
-
raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, 400)
|
39
|
+
raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, http_status: 400)
|
39
40
|
end
|
40
41
|
|
41
42
|
subscription = Data.mock_subscription({ id: new_id('su') })
|
42
|
-
subscription
|
43
|
+
subscription = resolve_subscription_changes(subscription, [plan], customers[ params[:id] ], params)
|
43
44
|
add_subscription_to_customer(customers[ params[:id] ], subscription)
|
44
45
|
subscriptions[subscription[:id]] = subscription
|
45
46
|
elsif params[:trial_end]
|
46
|
-
raise Stripe::InvalidRequestError.new('Received unknown parameter: trial_end', nil, 400)
|
47
|
+
raise Stripe::InvalidRequestError.new('Received unknown parameter: trial_end', nil, http_status: 400)
|
47
48
|
end
|
48
49
|
|
49
50
|
if params[:coupon]
|
50
51
|
coupon = coupons[ params[:coupon] ]
|
51
52
|
assert_existence :coupon, params[:coupon], coupon
|
52
53
|
|
53
|
-
|
54
|
+
add_coupon_to_object(customers[params[:id]], coupon)
|
54
55
|
end
|
55
56
|
|
56
57
|
customers[ params[:id] ]
|
@@ -77,7 +78,7 @@ module StripeMock
|
|
77
78
|
new_card = get_card_or_bank_by_token(params.delete(:source))
|
78
79
|
elsif params[:source].is_a?(Hash)
|
79
80
|
unless params[:source][:object] && params[:source][:number] && params[:source][:exp_month] && params[:source][:exp_year]
|
80
|
-
raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, 400)
|
81
|
+
raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, http_status: 400)
|
81
82
|
end
|
82
83
|
new_card = card_from_params(params.delete(:source))
|
83
84
|
end
|
@@ -89,7 +90,7 @@ module StripeMock
|
|
89
90
|
coupon = coupons[ params[:coupon] ]
|
90
91
|
assert_existence :coupon, params[:coupon], coupon
|
91
92
|
|
92
|
-
|
93
|
+
add_coupon_to_object(cus, coupon)
|
93
94
|
end
|
94
95
|
|
95
96
|
cus
|
@@ -107,13 +108,30 @@ module StripeMock
|
|
107
108
|
|
108
109
|
def get_customer(route, method_url, params, headers)
|
109
110
|
route =~ method_url
|
110
|
-
assert_existence :customer, $1, customers[$1]
|
111
|
+
customer = assert_existence :customer, $1, customers[$1]
|
112
|
+
|
113
|
+
customer = customer.clone
|
114
|
+
if params[:expand] == ['default_source']
|
115
|
+
customer[:default_source] = customer[:sources][:data].detect do |source|
|
116
|
+
source[:id] == customer[:default_source]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
customer
|
111
121
|
end
|
112
122
|
|
113
123
|
def list_customers(route, method_url, params, headers)
|
114
124
|
Data.mock_list_object(customers.values, params)
|
115
125
|
end
|
116
126
|
|
127
|
+
def delete_customer_discount(route, method_url, params, headers)
|
128
|
+
route =~ method_url
|
129
|
+
cus = assert_existence :customer, $1, customers[$1]
|
130
|
+
|
131
|
+
cus[:discount] = nil
|
132
|
+
|
133
|
+
cus
|
134
|
+
end
|
117
135
|
end
|
118
136
|
end
|
119
137
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module StripeMock
|
2
|
+
module RequestHandlers
|
3
|
+
module EphemeralKey
|
4
|
+
def self.included(klass)
|
5
|
+
klass.add_handler 'post /v1/ephemeral_keys', :create_ephemeral_key
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_ephemeral_key(route, method_url, params, headers)
|
9
|
+
Data.mock_ephemeral_key(params)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module StripeMock
|
2
|
+
module RequestHandlers
|
3
|
+
module ExternalAccounts
|
4
|
+
|
5
|
+
def ExternalAccounts.included(klass)
|
6
|
+
klass.add_handler 'get /v1/accounts/(.*)/external_accounts', :retrieve_external_accounts
|
7
|
+
klass.add_handler 'post /v1/accounts/(.*)/external_accounts', :create_external_account
|
8
|
+
klass.add_handler 'post /v1/accounts/(.*)/external_accounts/(.*)/verify', :verify_external_account
|
9
|
+
klass.add_handler 'get /v1/accounts/(.*)/external_accounts/(.*)', :retrieve_external_account
|
10
|
+
klass.add_handler 'delete /v1/accounts/(.*)/external_accounts/(.*)', :delete_external_account
|
11
|
+
klass.add_handler 'post /v1/accounts/(.*)/external_accounts/(.*)', :update_external_account
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_external_account(route, method_url, params, headers)
|
15
|
+
route =~ method_url
|
16
|
+
add_external_account_to(:account, $1, params, accounts)
|
17
|
+
end
|
18
|
+
|
19
|
+
def retrieve_external_accounts(route, method_url, params, headers)
|
20
|
+
route =~ method_url
|
21
|
+
retrieve_object_cards(:account, $1, accounts)
|
22
|
+
end
|
23
|
+
|
24
|
+
def retrieve_external_account(route, method_url, params, headers)
|
25
|
+
route =~ method_url
|
26
|
+
account = assert_existence :account, $1, accounts[$1]
|
27
|
+
|
28
|
+
assert_existence :card, $2, get_card(account, $2)
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete_external_account(route, method_url, params, headers)
|
32
|
+
route =~ method_url
|
33
|
+
delete_card_from(:account, $1, $2, accounts)
|
34
|
+
end
|
35
|
+
|
36
|
+
def update_external_account(route, method_url, params, headers)
|
37
|
+
route =~ method_url
|
38
|
+
account = assert_existence :account, $1, accounts[$1]
|
39
|
+
|
40
|
+
card = assert_existence :card, $2, get_card(account, $2)
|
41
|
+
card.merge!(params)
|
42
|
+
card
|
43
|
+
end
|
44
|
+
|
45
|
+
def verify_external_account(route, method_url, params, headers)
|
46
|
+
route =~ method_url
|
47
|
+
account = assert_existence :account, $1, accounts[$1]
|
48
|
+
|
49
|
+
external_account = assert_existence :bank_account, $2, verify_bank_account(account, $2)
|
50
|
+
external_account
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -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,20 +6,49 @@ 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
|
@@ -28,13 +57,17 @@ module StripeMock
|
|
28
57
|
def add_subscription_to_customer(cus, sub)
|
29
58
|
if sub[:trial_end].nil? || sub[:trial_end] == "now"
|
30
59
|
id = new_id('ch')
|
31
|
-
charges[id] = Data.mock_charge(
|
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
|
+
)
|
32
65
|
end
|
33
66
|
|
34
67
|
if cus[:currency].nil?
|
35
|
-
cus[:currency] = sub[:plan][:currency]
|
36
|
-
elsif cus[:currency] != sub[:plan][:currency]
|
37
|
-
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)
|
38
71
|
end
|
39
72
|
cus[:subscriptions][:total_count] = (cus[:subscriptions][:total_count] || 0) + 1
|
40
73
|
cus[:subscriptions][:data].unshift sub
|
@@ -50,6 +83,8 @@ module StripeMock
|
|
50
83
|
# `intervals` is set to 1 when calculating current_period_end from current_period_start & plan
|
51
84
|
# `intervals` is set to 2 when calculating Stripe::Invoice.upcoming end from current_period_start & plan
|
52
85
|
def get_ending_time(start_time, plan, intervals = 1)
|
86
|
+
return start_time unless plan
|
87
|
+
|
53
88
|
case plan[:interval]
|
54
89
|
when "week"
|
55
90
|
start_time + (604800 * (plan[:interval_count] || 1) * intervals)
|
@@ -65,15 +100,20 @@ module StripeMock
|
|
65
100
|
def verify_trial_end(trial_end)
|
66
101
|
if trial_end != "now"
|
67
102
|
if !trial_end.is_a? Integer
|
68
|
-
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)
|
69
104
|
elsif trial_end < Time.now.utc.to_i
|
70
|
-
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)
|
71
106
|
elsif trial_end > Time.now.utc.to_i + 31557600*5 # five years
|
72
|
-
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)
|
73
108
|
end
|
74
109
|
end
|
75
110
|
end
|
76
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
|
77
117
|
end
|
78
118
|
end
|
79
119
|
end
|