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
@@ -28,7 +28,7 @@ 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
|
@@ -36,7 +36,7 @@ module StripeMock
|
|
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', 404))
|
39
|
+
@card_tokens[token_id] || @bank_tokens[token_id] || raise(Stripe::InvalidRequestError.new("Invalid token id: #{token_id}", 'tok', http_status: 404))
|
40
40
|
end
|
41
41
|
|
42
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
|
@@ -10,12 +10,15 @@ module StripeMock
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def new_refund(route, method_url, params, headers)
|
13
|
-
if
|
14
|
-
|
15
|
-
|
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
|
16
19
|
end
|
17
20
|
|
18
|
-
charge =
|
21
|
+
charge = assert_existence :charge, params[:charge], charges[params[:charge]]
|
19
22
|
params[:amount] ||= charge[:amount]
|
20
23
|
id = new_id('re')
|
21
24
|
bal_trans_params = {
|
@@ -47,7 +50,7 @@ module StripeMock
|
|
47
50
|
allowed = allowed_refund_params(params)
|
48
51
|
disallowed = params.keys - allowed
|
49
52
|
if disallowed.count > 0
|
50
|
-
raise Stripe::InvalidRequestError.new("Received unknown parameters: #{disallowed.join(', ')}" , '', 400)
|
53
|
+
raise Stripe::InvalidRequestError.new("Received unknown parameters: #{disallowed.join(', ')}" , '', http_status: 400)
|
51
54
|
end
|
52
55
|
|
53
56
|
refunds[id] = Util.rmerge(refund, params)
|
@@ -72,9 +75,9 @@ module StripeMock
|
|
72
75
|
|
73
76
|
def ensure_refund_required_params(params)
|
74
77
|
if non_integer_charge_amount?(params)
|
75
|
-
raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', 400)
|
78
|
+
raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400)
|
76
79
|
elsif non_positive_charge_amount?(params)
|
77
|
-
raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', 400)
|
80
|
+
raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', http_status: 400)
|
78
81
|
elsif params[:charge].nil?
|
79
82
|
raise Stripe::InvalidRequestError.new('Must provide the identifier of the charge to refund.', nil)
|
80
83
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module StripeMock
|
2
|
+
module RequestHandlers
|
3
|
+
module SubscriptionItems
|
4
|
+
|
5
|
+
def SubscriptionItems.included(klass)
|
6
|
+
klass.add_handler 'get /v1/subscription_items', :retrieve_subscription_items
|
7
|
+
klass.add_handler 'post /v1/subscription_items/([^/]*)', :update_subscription_item
|
8
|
+
klass.add_handler 'post /v1/subscription_items', :create_subscription_items
|
9
|
+
end
|
10
|
+
|
11
|
+
def retrieve_subscription_items(route, method_url, params, headers)
|
12
|
+
route =~ method_url
|
13
|
+
|
14
|
+
require_param(:subscription) unless params[:subscription]
|
15
|
+
|
16
|
+
Data.mock_list_object(subscriptions_items, params)
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_subscription_items(route, method_url, params, headers)
|
20
|
+
params[:id] ||= new_id('si')
|
21
|
+
|
22
|
+
require_param(:subscription) unless params[:subscription]
|
23
|
+
require_param(:plan) unless params[:plan]
|
24
|
+
|
25
|
+
subscriptions_items[params[:id]] = Data.mock_subscription_item(params.merge(plan: plans[params[:plan]]))
|
26
|
+
end
|
27
|
+
|
28
|
+
def update_subscription_item(route, method_url, params, headers)
|
29
|
+
route =~ method_url
|
30
|
+
|
31
|
+
subscription_item = assert_existence :subscription_item, $1, subscriptions_items[$1]
|
32
|
+
subscription_item.merge!(params.merge(plan: plans[params[:plan]]))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -35,9 +35,7 @@ module StripeMock
|
|
35
35
|
def create_customer_subscription(route, method_url, params, headers)
|
36
36
|
route =~ method_url
|
37
37
|
|
38
|
-
|
39
|
-
plan = assert_existence :plan, plan_id, plans[plan_id]
|
40
|
-
|
38
|
+
subscription_plans = get_subscription_plans_from_params(params)
|
41
39
|
customer = assert_existence :customer, $1, customers[$1]
|
42
40
|
|
43
41
|
if params[:source]
|
@@ -46,11 +44,12 @@ module StripeMock
|
|
46
44
|
customer[:default_source] = new_card[:id]
|
47
45
|
end
|
48
46
|
|
49
|
-
# Ensure customer has card to charge if plan has no trial and is not free
|
50
|
-
verify_card_present(customer, plan, params)
|
51
|
-
|
52
47
|
subscription = Data.mock_subscription({ id: (params[:id] || new_id('su')) })
|
53
|
-
subscription
|
48
|
+
subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params)
|
49
|
+
|
50
|
+
# Ensure customer has card to charge if plan has no trial and is not free
|
51
|
+
# Note: needs updating for subscriptions with multiple plans
|
52
|
+
verify_card_present(customer, subscription_plans.first, subscription, params)
|
54
53
|
|
55
54
|
if params[:coupon]
|
56
55
|
coupon_id = params[:coupon]
|
@@ -61,9 +60,9 @@ module StripeMock
|
|
61
60
|
coupon = coupons[coupon_id]
|
62
61
|
|
63
62
|
if coupon
|
64
|
-
subscription
|
63
|
+
add_coupon_to_object(subscription, coupon)
|
65
64
|
else
|
66
|
-
raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', 400)
|
65
|
+
raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400)
|
67
66
|
end
|
68
67
|
end
|
69
68
|
|
@@ -74,25 +73,50 @@ module StripeMock
|
|
74
73
|
end
|
75
74
|
|
76
75
|
def create_subscription(route, method_url, params, headers)
|
76
|
+
if headers && headers[:idempotency_key]
|
77
|
+
if subscriptions.any?
|
78
|
+
original_subscription = subscriptions.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]}
|
79
|
+
puts original_subscription
|
80
|
+
return subscriptions[original_subscription[:id]] if original_subscription
|
81
|
+
end
|
82
|
+
end
|
77
83
|
route =~ method_url
|
78
84
|
|
79
|
-
|
80
|
-
plan = assert_existence :plan, plan_id, plans[plan_id]
|
85
|
+
subscription_plans = get_subscription_plans_from_params(params)
|
81
86
|
|
82
|
-
|
87
|
+
customer = params[:customer]
|
88
|
+
customer_id = customer.is_a?(Stripe::Customer) ? customer[:id] : customer.to_s
|
83
89
|
customer = assert_existence :customer, customer_id, customers[customer_id]
|
84
90
|
|
91
|
+
if subscription_plans && customer
|
92
|
+
subscription_plans.each do |plan|
|
93
|
+
unless customer[:currency].to_s == plan[:currency].to_s
|
94
|
+
raise Stripe::InvalidRequestError.new("Customer's currency of #{customer[:currency]} does not match plan's currency of #{plan[:currency]}", 'currency', http_status: 400)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
85
99
|
if params[:source]
|
86
100
|
new_card = get_card_by_token(params.delete(:source))
|
87
101
|
add_card_to_object(:customer, new_card, customer)
|
88
102
|
customer[:default_source] = new_card[:id]
|
89
103
|
end
|
90
104
|
|
91
|
-
|
92
|
-
|
105
|
+
allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key)
|
106
|
+
unknown_params = params.keys - allowed_params.map(&:to_sym)
|
107
|
+
if unknown_params.length > 0
|
108
|
+
raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400)
|
109
|
+
end
|
93
110
|
|
94
111
|
subscription = Data.mock_subscription({ id: (params[:id] || new_id('su')) })
|
95
|
-
subscription
|
112
|
+
subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params)
|
113
|
+
if headers[:idempotency_key]
|
114
|
+
subscription[:idempotency_key] = headers[:idempotency_key]
|
115
|
+
end
|
116
|
+
|
117
|
+
# Ensure customer has card to charge if plan has no trial and is not free
|
118
|
+
# Note: needs updating for subscriptions with multiple plans
|
119
|
+
verify_card_present(customer, subscription_plans.first, subscription, params)
|
96
120
|
|
97
121
|
if params[:coupon]
|
98
122
|
coupon_id = params[:coupon]
|
@@ -103,9 +127,9 @@ module StripeMock
|
|
103
127
|
coupon = coupons[coupon_id]
|
104
128
|
|
105
129
|
if coupon
|
106
|
-
subscription
|
130
|
+
add_coupon_to_object(subscription, coupon)
|
107
131
|
else
|
108
|
-
raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', 400)
|
132
|
+
raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400)
|
109
133
|
end
|
110
134
|
end
|
111
135
|
|
@@ -145,11 +169,12 @@ module StripeMock
|
|
145
169
|
customer[:default_source] = new_card[:id]
|
146
170
|
end
|
147
171
|
|
148
|
-
|
149
|
-
plan_name =
|
150
|
-
params[:plan].is_a?(String) ? params[:plan] : subscription[:plan][:id]
|
172
|
+
subscription_plans = get_subscription_plans_from_params(params)
|
151
173
|
|
152
|
-
|
174
|
+
# subscription plans are not being updated but load them for the response
|
175
|
+
if subscription_plans.empty?
|
176
|
+
subscription_plans = subscription[:items][:data].map { |item| item[:plan] }
|
177
|
+
end
|
153
178
|
|
154
179
|
if params[:coupon]
|
155
180
|
coupon_id = params[:coupon]
|
@@ -159,17 +184,14 @@ module StripeMock
|
|
159
184
|
|
160
185
|
coupon = coupons[coupon_id]
|
161
186
|
if coupon
|
162
|
-
subscription
|
187
|
+
add_coupon_to_object(subscription, coupon)
|
163
188
|
elsif coupon_id == ""
|
164
|
-
subscription[:discount] =
|
189
|
+
subscription[:discount] = nil
|
165
190
|
else
|
166
|
-
raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', 400)
|
191
|
+
raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400)
|
167
192
|
end
|
168
193
|
end
|
169
|
-
|
170
|
-
assert_existence :plan, plan_name, plan
|
171
|
-
params[:plan] = plan if params[:plan]
|
172
|
-
verify_card_present(customer, plan)
|
194
|
+
verify_card_present(customer, subscription_plans.first, subscription)
|
173
195
|
|
174
196
|
if subscription[:cancel_at_period_end]
|
175
197
|
subscription[:cancel_at_period_end] = false
|
@@ -177,7 +199,8 @@ module StripeMock
|
|
177
199
|
end
|
178
200
|
|
179
201
|
params[:current_period_start] = subscription[:current_period_start]
|
180
|
-
|
202
|
+
params[:trial_end] = params[:trial_end] || subscription[:trial_end]
|
203
|
+
subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params)
|
181
204
|
|
182
205
|
# delete the old subscription, replace with the new subscription
|
183
206
|
customer[:subscriptions][:data].reject! { |sub| sub[:id] == subscription[:id] }
|
@@ -216,11 +239,49 @@ module StripeMock
|
|
216
239
|
|
217
240
|
private
|
218
241
|
|
219
|
-
def
|
220
|
-
|
221
|
-
|
222
|
-
|
242
|
+
def get_subscription_plans_from_params(params)
|
243
|
+
plan_ids = if params[:plan]
|
244
|
+
[params[:plan].to_s]
|
245
|
+
elsif params[:items]
|
246
|
+
items = params[:items]
|
247
|
+
items = items.values if items.respond_to?(:values)
|
248
|
+
items.map { |item| item[:plan].to_s if item[:plan] }
|
249
|
+
else
|
250
|
+
[]
|
251
|
+
end
|
252
|
+
plan_ids.each do |plan_id|
|
253
|
+
assert_existence :plan, plan_id, plans[plan_id]
|
223
254
|
end
|
255
|
+
plan_ids.map { |plan_id| plans[plan_id] }
|
256
|
+
end
|
257
|
+
|
258
|
+
# Ensure customer has card to charge unless one of the following criterias is met:
|
259
|
+
# 1) is in trial
|
260
|
+
# 2) is free
|
261
|
+
# 3) has billing set to send invoice
|
262
|
+
def verify_card_present(customer, plan, subscription, params={})
|
263
|
+
return if customer[:default_source]
|
264
|
+
return if customer[:trial_end]
|
265
|
+
return if params[:trial_end]
|
266
|
+
|
267
|
+
plan_trial_period_days = plan[:trial_period_days] || 0
|
268
|
+
plan_has_trial = plan_trial_period_days != 0 || plan[:amount] == 0 || plan[:trial_end]
|
269
|
+
return if plan && plan_has_trial
|
270
|
+
|
271
|
+
return if subscription && subscription[:trial_end] && subscription[:trial_end] != 'now'
|
272
|
+
|
273
|
+
if subscription[:items]
|
274
|
+
trial = subscription[:items][:data].none? do |item|
|
275
|
+
plan = item[:plan]
|
276
|
+
(plan[:trial_period_days].nil? || plan[:trial_period_days] == 0) &&
|
277
|
+
(plan[:trial_end].nil? || plan[:trial_end] == 'now')
|
278
|
+
end
|
279
|
+
return if trial
|
280
|
+
end
|
281
|
+
|
282
|
+
return if params[:billing] == 'send_invoice'
|
283
|
+
|
284
|
+
raise Stripe::InvalidRequestError.new('You must supply a valid card xoxo', nil, http_status: 400)
|
224
285
|
end
|
225
286
|
|
226
287
|
def verify_active_status(subscription)
|
@@ -228,7 +289,7 @@ module StripeMock
|
|
228
289
|
|
229
290
|
if status == 'canceled'
|
230
291
|
message = "No such subscription: #{id}"
|
231
|
-
raise Stripe::InvalidRequestError.new(message, 'subscription', 404)
|
292
|
+
raise Stripe::InvalidRequestError.new(message, 'subscription', http_status: 404)
|
232
293
|
end
|
233
294
|
end
|
234
295
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module StripeMock
|
2
|
+
module RequestHandlers
|
3
|
+
module TaxRates
|
4
|
+
def TaxRates.included(klass)
|
5
|
+
klass.add_handler 'post /v1/tax_rates', :new_tax_rate
|
6
|
+
klass.add_handler 'post /v1/tax_rates/([^/]*)', :update_tax_rate
|
7
|
+
klass.add_handler 'get /v1/tax_rates/([^/]*)', :get_tax_rate
|
8
|
+
klass.add_handler 'get /v1/tax_rates', :list_tax_rates
|
9
|
+
end
|
10
|
+
|
11
|
+
def update_tax_rate(route, method_url, params, headers)
|
12
|
+
route =~ method_url
|
13
|
+
rate = assert_existence :tax_rate, $1, tax_rates[$1]
|
14
|
+
rate.merge!(params)
|
15
|
+
rate
|
16
|
+
end
|
17
|
+
|
18
|
+
def new_tax_rate(route, method_url, params, headers)
|
19
|
+
params[:id] ||= new_id('txr')
|
20
|
+
tax_rates[ params[:id] ] = Data.mock_tax_rate(params)
|
21
|
+
tax_rates[ params[:id] ]
|
22
|
+
end
|
23
|
+
|
24
|
+
def list_tax_rates(route, method_url, params, headers)
|
25
|
+
Data.mock_list_object(tax_rates.values, params)
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_tax_rate(route, method_url, params, headers)
|
29
|
+
route =~ method_url
|
30
|
+
tax_rate = assert_existence :tax_rate, $1, tax_rates[$1]
|
31
|
+
tax_rate.clone
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -9,7 +9,7 @@ module StripeMock
|
|
9
9
|
|
10
10
|
def create_token(route, method_url, params, headers)
|
11
11
|
if params[:customer].nil? && params[:card].nil? && params[:bank_account].nil?
|
12
|
-
raise Stripe::InvalidRequestError.new('You must supply either a card, customer, or bank account to create a token.', nil, 400)
|
12
|
+
raise Stripe::InvalidRequestError.new('You must supply either a card, customer, or bank account to create a token.', nil, http_status: 400)
|
13
13
|
end
|
14
14
|
|
15
15
|
cus_id = params[:customer]
|
@@ -31,6 +31,12 @@ module StripeMock
|
|
31
31
|
params[:card][:fingerprint] = StripeMock::Util.fingerprint(params[:card][:number])
|
32
32
|
params[:card][:last4] = params[:card][:number][-4,4]
|
33
33
|
customer_card = params[:card]
|
34
|
+
elsif params[:bank_account].is_a?(String)
|
35
|
+
customer = assert_existence :customer, cus_id, customers[cus_id]
|
36
|
+
|
37
|
+
# params[:bank_account] is an id; grab it from the db
|
38
|
+
bank_account = verify_bank_account(customer, params[:bank_account])
|
39
|
+
assert_existence :bank_account, params[:bank_account], bank_account
|
34
40
|
elsif params[:bank_account]
|
35
41
|
# params[:card] is a hash of cc info; "Sanitize" the card number
|
36
42
|
bank_account = params[:bank_account]
|
@@ -40,12 +46,12 @@ module StripeMock
|
|
40
46
|
end
|
41
47
|
|
42
48
|
if bank_account
|
43
|
-
token_id = generate_bank_token(bank_account)
|
49
|
+
token_id = generate_bank_token(bank_account.dup)
|
44
50
|
bank_account = @bank_tokens[token_id]
|
45
51
|
|
46
52
|
Data.mock_bank_account_token(params.merge :id => token_id, :bank_account => bank_account)
|
47
53
|
else
|
48
|
-
token_id = generate_card_token(customer_card)
|
54
|
+
token_id = generate_card_token(customer_card.dup)
|
49
55
|
card = @card_tokens[token_id]
|
50
56
|
|
51
57
|
Data.mock_card_token(params.merge :id => token_id, :card => card)
|