stripe-ruby-mock 3.0.1 → 3.1.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +2 -5
  3. data/CHANGELOG.md +28 -15
  4. data/Gemfile +1 -0
  5. data/lib/stripe_mock.rb +4 -0
  6. data/lib/stripe_mock/api/client.rb +1 -1
  7. data/lib/stripe_mock/api/instance.rb +1 -1
  8. data/lib/stripe_mock/api/webhooks.rb +2 -0
  9. data/lib/stripe_mock/client.rb +2 -1
  10. data/lib/stripe_mock/data.rb +127 -25
  11. data/lib/stripe_mock/data/list.rb +31 -6
  12. data/lib/stripe_mock/instance.rb +7 -2
  13. data/lib/stripe_mock/request_handlers/account_links.rb +15 -0
  14. data/lib/stripe_mock/request_handlers/charges.rb +6 -4
  15. data/lib/stripe_mock/request_handlers/checkout_session.rb +16 -0
  16. data/lib/stripe_mock/request_handlers/customers.rb +22 -13
  17. data/lib/stripe_mock/request_handlers/ephemeral_key.rb +1 -1
  18. data/lib/stripe_mock/request_handlers/express_login_links.rb +15 -0
  19. data/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +12 -7
  20. data/lib/stripe_mock/request_handlers/invoices.rb +4 -3
  21. data/lib/stripe_mock/request_handlers/payment_methods.rb +8 -5
  22. data/lib/stripe_mock/request_handlers/prices.rb +44 -0
  23. data/lib/stripe_mock/request_handlers/sources.rb +12 -6
  24. data/lib/stripe_mock/request_handlers/subscriptions.rb +29 -19
  25. data/lib/stripe_mock/request_handlers/tokens.rb +6 -4
  26. data/lib/stripe_mock/request_handlers/validators/param_validators.rb +32 -0
  27. data/lib/stripe_mock/test_strategies/base.rb +26 -0
  28. data/lib/stripe_mock/version.rb +1 -1
  29. data/lib/stripe_mock/webhook_fixtures/balance.available.json +6 -0
  30. data/lib/stripe_mock/webhook_fixtures/payment_intent.payment_failed.json +186 -0
  31. data/lib/stripe_mock/webhook_fixtures/payment_intent.succeeded.json +164 -0
  32. data/spec/instance_spec.rb +4 -6
  33. data/spec/list_spec.rb +23 -0
  34. data/spec/server_spec.rb +4 -2
  35. data/spec/shared_stripe_examples/account_link_examples.rb +16 -0
  36. data/spec/shared_stripe_examples/balance_examples.rb +6 -0
  37. data/spec/shared_stripe_examples/card_token_examples.rb +17 -21
  38. data/spec/shared_stripe_examples/checkout_examples.rb +20 -1
  39. data/spec/shared_stripe_examples/customer_examples.rb +11 -13
  40. data/spec/shared_stripe_examples/express_login_link_examples.rb +12 -0
  41. data/spec/shared_stripe_examples/invoice_examples.rb +8 -8
  42. data/spec/shared_stripe_examples/payment_method_examples.rb +332 -68
  43. data/spec/shared_stripe_examples/price_examples.rb +183 -0
  44. data/spec/shared_stripe_examples/subscription_examples.rb +115 -8
  45. data/spec/spec_helper.rb +4 -0
  46. data/spec/stripe_mock_spec.rb +2 -2
  47. data/spec/support/stripe_examples.rb +5 -1
  48. data/stripe-ruby-mock.gemspec +6 -1
  49. metadata +25 -11
@@ -17,26 +17,29 @@ module StripeMock
17
17
  end
18
18
 
19
19
  def retrieve_customer_subscription(route, method_url, params, headers)
20
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
20
21
  route =~ method_url
21
22
 
22
- customer = assert_existence :customer, $1, customers[$1]
23
+ customer = assert_existence :customer, $1, customers[stripe_account][$1]
23
24
  subscription = get_customer_subscription(customer, $2)
24
25
 
25
26
  assert_existence :subscription, $2, subscription
26
27
  end
27
28
 
28
29
  def retrieve_customer_subscriptions(route, method_url, params, headers)
30
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
29
31
  route =~ method_url
30
32
 
31
- customer = assert_existence :customer, $1, customers[$1]
33
+ customer = assert_existence :customer, $1, customers[stripe_account][$1]
32
34
  customer[:subscriptions]
33
35
  end
34
36
 
35
37
  def create_customer_subscription(route, method_url, params, headers)
38
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
36
39
  route =~ method_url
37
40
 
38
41
  subscription_plans = get_subscription_plans_from_params(params)
39
- customer = assert_existence :customer, $1, customers[$1]
42
+ customer = assert_existence :customer, $1, customers[stripe_account][$1]
40
43
 
41
44
  if params[:source]
42
45
  new_card = get_card_by_token(params.delete(:source))
@@ -73,10 +76,10 @@ module StripeMock
73
76
  end
74
77
 
75
78
  def create_subscription(route, method_url, params, headers)
79
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
76
80
  if headers && headers[:idempotency_key]
77
81
  if subscriptions.any?
78
82
  original_subscription = subscriptions.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]}
79
- puts original_subscription
80
83
  return subscriptions[original_subscription[:id]] if original_subscription
81
84
  end
82
85
  end
@@ -86,15 +89,7 @@ module StripeMock
86
89
 
87
90
  customer = params[:customer]
88
91
  customer_id = customer.is_a?(Stripe::Customer) ? customer[:id] : customer.to_s
89
- customer = assert_existence :customer, customer_id, customers[customer_id]
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
92
+ customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id]
98
93
 
99
94
  if params[:source]
100
95
  new_card = get_card_by_token(params.delete(:source))
@@ -102,7 +97,7 @@ module StripeMock
102
97
  customer[:default_source] = new_card[:id]
103
98
  end
104
99
 
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 enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval)
100
+ 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 enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan expand)
106
101
  unknown_params = params.keys - allowed_params.map(&:to_sym)
107
102
  if unknown_params.length > 0
108
103
  raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400)
@@ -133,6 +128,10 @@ module StripeMock
133
128
  end
134
129
  end
135
130
 
131
+ if params[:trial_period_days]
132
+ subscription[:status] = 'trialing'
133
+ end
134
+
136
135
  if params[:cancel_at_period_end]
137
136
  subscription[:cancel_at_period_end] = true
138
137
  subscription[:canceled_at] = Time.now.utc.to_i
@@ -151,14 +150,16 @@ module StripeMock
151
150
  end
152
151
 
153
152
  def retrieve_subscriptions(route, method_url, params, headers)
153
+ # stripe_account = headers && headers[:stripe_account] || Stripe.api_key
154
154
  route =~ method_url
155
155
 
156
156
  Data.mock_list_object(subscriptions.values, params)
157
- #customer = assert_existence :customer, $1, customers[$1]
157
+ #customer = assert_existence :customer, $1, customers[stripe_account][$1]
158
158
  #customer[:subscriptions]
159
159
  end
160
160
 
161
161
  def update_subscription(route, method_url, params, headers)
162
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
162
163
  route =~ method_url
163
164
 
164
165
  subscription_id = $2 ? $2 : $1
@@ -166,7 +167,7 @@ module StripeMock
166
167
  verify_active_status(subscription)
167
168
 
168
169
  customer_id = subscription[:customer]
169
- customer = assert_existence :customer, customer_id, customers[customer_id]
170
+ customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id]
170
171
 
171
172
  if params[:source]
172
173
  new_card = get_card_by_token(params.delete(:source))
@@ -197,6 +198,10 @@ module StripeMock
197
198
  end
198
199
  end
199
200
 
201
+ if params[:trial_period_days]
202
+ subscription[:status] = 'trialing'
203
+ end
204
+
200
205
  if params[:cancel_at_period_end]
201
206
  subscription[:cancel_at_period_end] = true
202
207
  subscription[:canceled_at] = Time.now.utc.to_i
@@ -222,13 +227,14 @@ module StripeMock
222
227
  end
223
228
 
224
229
  def cancel_subscription(route, method_url, params, headers)
230
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
225
231
  route =~ method_url
226
232
 
227
233
  subscription_id = $2 ? $2 : $1
228
234
  subscription = assert_existence :subscription, subscription_id, subscriptions[subscription_id]
229
235
 
230
236
  customer_id = subscription[:customer]
231
- customer = assert_existence :customer, customer_id, customers[customer_id]
237
+ customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id]
232
238
 
233
239
  cancel_params = { canceled_at: Time.now.utc.to_i }
234
240
  cancelled_at_period_end = (params[:at_period_end] == true)
@@ -257,14 +263,17 @@ module StripeMock
257
263
  elsif params[:items]
258
264
  items = params[:items]
259
265
  items = items.values if items.respond_to?(:values)
260
- items.map { |item| item[:plan].to_s if item[:plan] }
266
+ items.map { |item| item[:plan] ? item[:plan] : item[:price] }
261
267
  else
262
268
  []
263
269
  end
270
+ plan_ids.compact!
264
271
  plan_ids.each do |plan_id|
265
272
  assert_existence :plan, plan_id, plans[plan_id]
273
+ rescue Stripe::InvalidRequestError
274
+ assert_existence :price, plan_id, prices[plan_id]
266
275
  end
267
- plan_ids.map { |plan_id| plans[plan_id] }
276
+ plan_ids.map { |plan_id| plans[plan_id] || prices[plan_id]}
268
277
  end
269
278
 
270
279
  # Ensure customer has card to charge unless one of the following criterias is met:
@@ -276,6 +285,7 @@ module StripeMock
276
285
  return if customer[:invoice_settings][:default_payment_method]
277
286
  return if customer[:trial_end]
278
287
  return if params[:trial_end]
288
+ return if subscription[:default_payment_method]
279
289
 
280
290
  plan_trial_period_days = plan[:trial_period_days] || 0
281
291
  plan_has_trial = plan_trial_period_days != 0 || plan[:amount] == 0 || plan[:trial_end]
@@ -8,6 +8,8 @@ module StripeMock
8
8
  end
9
9
 
10
10
  def create_token(route, method_url, params, headers)
11
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
12
+
11
13
  if params[:customer].nil? && params[:card].nil? && params[:bank_account].nil?
12
14
  raise Stripe::InvalidRequestError.new('You must supply either a card, customer, or bank account to create a token.', nil, http_status: 400)
13
15
  end
@@ -15,13 +17,13 @@ module StripeMock
15
17
  cus_id = params[:customer]
16
18
 
17
19
  if cus_id && params[:source]
18
- customer = assert_existence :customer, cus_id, customers[cus_id]
20
+ customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id]
19
21
 
20
22
  # params[:card] is an id; grab it from the db
21
23
  customer_card = get_card(customer, params[:source])
22
24
  assert_existence :card, params[:source], customer_card
23
25
  elsif params[:card].is_a?(String)
24
- customer = assert_existence :customer, cus_id, customers[cus_id]
26
+ customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id]
25
27
 
26
28
  # params[:card] is an id; grab it from the db
27
29
  customer_card = get_card(customer, params[:card])
@@ -32,7 +34,7 @@ module StripeMock
32
34
  params[:card][:last4] = params[:card][:number][-4,4]
33
35
  customer_card = params[:card]
34
36
  elsif params[:bank_account].is_a?(String)
35
- customer = assert_existence :customer, cus_id, customers[cus_id]
37
+ customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id]
36
38
 
37
39
  # params[:bank_account] is an id; grab it from the db
38
40
  bank_account = verify_bank_account(customer, params[:bank_account])
@@ -41,7 +43,7 @@ module StripeMock
41
43
  # params[:card] is a hash of cc info; "Sanitize" the card number
42
44
  bank_account = params[:bank_account]
43
45
  else
44
- customer = assert_existence :customer, cus_id, customers[cus_id]
46
+ customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id] || customers[Stripe.api_key][cus_id]
45
47
  customer_card = get_card(customer, customer[:default_source])
46
48
  end
47
49
 
@@ -110,9 +110,41 @@ module StripeMock
110
110
 
111
111
  end
112
112
 
113
+ def validate_create_price_params(params)
114
+ price_id = params[:id].to_s
115
+ product_id = params[:product]
116
+
117
+ @base_strategy.create_price_params.keys.each do |attr_name|
118
+ message = "Missing required param: #{attr_name}."
119
+ raise Stripe::InvalidRequestError.new(message, attr_name) if params[attr_name].nil?
120
+ end
121
+
122
+ if prices[price_id]
123
+ message = already_exists_message(Stripe::Price)
124
+ raise Stripe::InvalidRequestError.new(message, :id)
125
+ end
126
+
127
+ unless products[product_id]
128
+ message = not_found_message(Stripe::Product, product_id)
129
+ raise Stripe::InvalidRequestError.new(message, :product)
130
+ end
131
+
132
+ unless SUPPORTED_CURRENCIES.include?(params[:currency])
133
+ message = invalid_currency_message(params[:currency])
134
+ raise Stripe::InvalidRequestError.new(message, :currency)
135
+ end
136
+ end
137
+
138
+ def validate_list_prices_params(params)
139
+ if params[:lookup_keys] && !params[:lookup_keys].is_a?(Array)
140
+ raise Stripe::InvalidRequestError.new('Invalid array', :lookup_keys)
141
+ end
142
+ end
143
+
113
144
  def require_param(param_name)
114
145
  raise Stripe::InvalidRequestError.new("Missing required param: #{param_name}.", param_name.to_s, http_status: 400)
115
146
  end
147
+
116
148
  end
117
149
  end
118
150
  end
@@ -41,6 +41,15 @@ module StripeMock
41
41
  }.merge(params)
42
42
  end
43
43
 
44
+ def create_price(params={})
45
+ Stripe::Price.create create_price_params(params)
46
+ end
47
+
48
+ def create_price_params(params={})
49
+ {
50
+ :currency => StripeMock.default_currency,
51
+ }.merge(params)
52
+ end
44
53
 
45
54
  def list_subscriptions(limit)
46
55
  Stripe::Subscription.list(limit: limit)
@@ -97,10 +106,27 @@ module StripeMock
97
106
  }.merge(params)
98
107
  end
99
108
 
109
+ def create_checkout_session_params(params = {})
110
+ {
111
+ payment_method_types: ['card'],
112
+ line_items: [{
113
+ name: 'T-shirt',
114
+ quantity: 1,
115
+ amount: 500,
116
+ currency: 'usd',
117
+ }],
118
+ }.merge(params)
119
+ end
120
+
121
+
100
122
  def create_coupon(params = {})
101
123
  Stripe::Coupon.create create_coupon_params(params)
102
124
  end
103
125
 
126
+ def create_checkout_session(params = {})
127
+ Stripe::Checkout::Session.create create_checkout_session_params(params)
128
+ end
129
+
104
130
  def delete_all_coupons
105
131
  coupons = Stripe::Coupon.list
106
132
  coupons.data.map(&:delete) if coupons.data.count > 0
@@ -1,4 +1,4 @@
1
1
  module StripeMock
2
2
  # stripe-ruby-mock version
3
- VERSION = "3.0.1"
3
+ VERSION = "3.1.0.rc2"
4
4
  end
@@ -18,6 +18,12 @@
18
18
  "currency": "usd"
19
19
  }
20
20
  ],
21
+ "instant_available": [
22
+ {
23
+ "amount": 0,
24
+ "currency": "usd"
25
+ }
26
+ ],
21
27
  "livemode": false,
22
28
  "object": "balance"
23
29
  }
@@ -0,0 +1,186 @@
1
+ {
2
+ "id": "evt_00000000000000",
3
+ "object": "event",
4
+ "api_version": "2018-02-28",
5
+ "created": 1578401135,
6
+ "data": {
7
+ "object": {
8
+ "id": "pi_00000000000000",
9
+ "object": "payment_intent",
10
+ "allowed_source_types": ["card", "sepa_debit"],
11
+ "amount": 200,
12
+ "amount_capturable": 0,
13
+ "amount_received": 0,
14
+ "application": null,
15
+ "application_fee_amount": null,
16
+ "canceled_at": null,
17
+ "cancellation_reason": null,
18
+ "capture_method": "automatic",
19
+ "charges": {
20
+ "object": "list",
21
+ "data": [
22
+ {
23
+ "id": "py_00000000000000",
24
+ "object": "charge",
25
+ "amount": 200,
26
+ "amount_refunded": 0,
27
+ "application": null,
28
+ "application_fee": null,
29
+ "application_fee_amount": null,
30
+ "balance_transaction": null,
31
+ "billing_details": {
32
+ "address": {
33
+ "city": null,
34
+ "country": null,
35
+ "line1": null,
36
+ "line2": null,
37
+ "postal_code": null,
38
+ "state": null
39
+ },
40
+ "email": "john.doe@example.com",
41
+ "name": "John Doe",
42
+ "phone": null
43
+ },
44
+ "captured": true,
45
+ "created": 1578401129,
46
+ "currency": "eur",
47
+ "customer": "cus_00000000000000",
48
+ "description": null,
49
+ "destination": "acct_00000000000000",
50
+ "dispute": null,
51
+ "disputed": false,
52
+ "failure_code": null,
53
+ "failure_message": null,
54
+ "fraud_details": {},
55
+ "invoice": null,
56
+ "livemode": false,
57
+ "metadata": {},
58
+ "on_behalf_of": null,
59
+ "order": null,
60
+ "outcome": {
61
+ "network_status": "approved_by_network",
62
+ "reason": null,
63
+ "risk_level": "not_assessed",
64
+ "seller_message": "Payment complete.",
65
+ "type": "authorized"
66
+ },
67
+ "paid": false,
68
+ "payment_intent": "pi_00000000000000",
69
+ "payment_method": "pm_00000000000000",
70
+ "payment_method_details": {
71
+ "sepa_debit": {
72
+ "bank_code": "37040044",
73
+ "branch_code": null,
74
+ "country": "DE",
75
+ "fingerprint": "00000000000000",
76
+ "last4": "3001",
77
+ "mandate": "mandate_00000000000000"
78
+ },
79
+ "type": "sepa_debit"
80
+ },
81
+ "receipt_email": null,
82
+ "receipt_number": null,
83
+ "receipt_url": "https://pay.stripe.com/receipts/acct_00000000000000/py_00000000000000/rcpt_00000000000000",
84
+ "refunded": false,
85
+ "refunds": {
86
+ "object": "list",
87
+ "data": [],
88
+ "has_more": false,
89
+ "total_count": 0,
90
+ "url": "/v1/charges/py_00000000000000/refunds"
91
+ },
92
+ "review": null,
93
+ "shipping": null,
94
+ "source": null,
95
+ "source_transfer": null,
96
+ "statement_descriptor": "ACME Corp",
97
+ "statement_descriptor_suffix": null,
98
+ "status": "failed",
99
+ "transfer_data": {
100
+ "amount": null,
101
+ "destination": "acct_00000000000000"
102
+ },
103
+ "transfer_group": "group_pi_00000000000000"
104
+ }
105
+ ],
106
+ "has_more": false,
107
+ "total_count": 1,
108
+ "url": "/v1/charges?payment_intent=pi_00000000000000"
109
+ },
110
+ "client_secret": "pi_00000000000000",
111
+ "confirmation_method": "automatic",
112
+ "created": 1578401129,
113
+ "currency": "eur",
114
+ "customer": "cus_00000000000000",
115
+ "description": null,
116
+ "invoice": null,
117
+ "last_payment_error": {
118
+ "code": "payment_intent_payment_attempt_failed",
119
+ "doc_url": "https://stripe.com/docs/error-codes/payment-intent-payment-attempt-failed",
120
+ "message": "The payment failed.",
121
+ "payment_method": {
122
+ "id": "pm_00000000000000",
123
+ "object": "payment_method",
124
+ "billing_details": {
125
+ "address": {
126
+ "city": null,
127
+ "country": null,
128
+ "line1": null,
129
+ "line2": null,
130
+ "postal_code": null,
131
+ "state": null
132
+ },
133
+ "email": "john.doe@example.com",
134
+ "name": "John Doe",
135
+ "phone": null
136
+ },
137
+ "created": 1578400666,
138
+ "customer": "cus_00000000000000",
139
+ "livemode": false,
140
+ "metadata": {},
141
+ "sepa_debit": {
142
+ "bank_code": "37040044",
143
+ "branch_code": "",
144
+ "country": "DE",
145
+ "fingerprint": "00000000000000",
146
+ "last4": "3001"
147
+ },
148
+ "type": "sepa_debit"
149
+ },
150
+ "type": "invalid_request_error"
151
+ },
152
+ "livemode": false,
153
+ "metadata": {},
154
+ "next_action": null,
155
+ "next_source_action": null,
156
+ "on_behalf_of": null,
157
+ "payment_method": null,
158
+ "payment_method_options": {
159
+ "card": {
160
+ "installments": null,
161
+ "request_three_d_secure": "automatic"
162
+ }
163
+ },
164
+ "payment_method_types": ["card", "sepa_debit"],
165
+ "receipt_email": null,
166
+ "review": null,
167
+ "setup_future_usage": null,
168
+ "shipping": null,
169
+ "source": null,
170
+ "statement_descriptor": "ACME Corp",
171
+ "statement_descriptor_suffix": null,
172
+ "status": "requires_source",
173
+ "transfer_data": {
174
+ "destination": "acct_00000000000000"
175
+ },
176
+ "transfer_group": null
177
+ }
178
+ },
179
+ "livemode": false,
180
+ "pending_webhooks": 3,
181
+ "request": {
182
+ "id": null,
183
+ "idempotency_key": null
184
+ },
185
+ "type": "payment_intent.payment_failed"
186
+ }