stripe-ruby-mock 2.5.8 → 3.0.0

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +6 -6
  4. data/README.md +8 -8
  5. data/lib/stripe_mock.rb +3 -0
  6. data/lib/stripe_mock/api/errors.rb +31 -28
  7. data/lib/stripe_mock/api/webhooks.rb +3 -0
  8. data/lib/stripe_mock/data.rb +140 -13
  9. data/lib/stripe_mock/instance.rb +10 -3
  10. data/lib/stripe_mock/request_handlers/balance_transactions.rb +2 -2
  11. data/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +1 -1
  12. data/lib/stripe_mock/request_handlers/helpers/token_helpers.rb +1 -1
  13. data/lib/stripe_mock/request_handlers/invoices.rb +6 -1
  14. data/lib/stripe_mock/request_handlers/payment_intents.rb +175 -0
  15. data/lib/stripe_mock/request_handlers/payment_methods.rb +138 -0
  16. data/lib/stripe_mock/request_handlers/products.rb +1 -0
  17. data/lib/stripe_mock/request_handlers/setup_intents.rb +93 -0
  18. data/lib/stripe_mock/request_handlers/subscriptions.rb +17 -4
  19. data/lib/stripe_mock/request_handlers/validators/param_validators.rb +95 -9
  20. data/lib/stripe_mock/test_strategies/base.rb +42 -8
  21. data/lib/stripe_mock/test_strategies/live.rb +23 -12
  22. data/lib/stripe_mock/test_strategies/mock.rb +6 -2
  23. data/lib/stripe_mock/version.rb +1 -1
  24. data/lib/stripe_mock/webhook_fixtures/charge.failed.json +166 -38
  25. data/lib/stripe_mock/webhook_fixtures/customer.created.json +1 -0
  26. data/lib/stripe_mock/webhook_fixtures/customer.updated.json +1 -0
  27. data/lib/stripe_mock/webhook_fixtures/product.created.json +34 -0
  28. data/lib/stripe_mock/webhook_fixtures/product.deleted.json +34 -0
  29. data/lib/stripe_mock/webhook_fixtures/product.updated.json +38 -0
  30. data/spec/instance_spec.rb +6 -6
  31. data/spec/server_spec.rb +2 -1
  32. data/spec/shared_stripe_examples/account_examples.rb +1 -1
  33. data/spec/shared_stripe_examples/balance_transaction_examples.rb +3 -3
  34. data/spec/shared_stripe_examples/bank_examples.rb +3 -3
  35. data/spec/shared_stripe_examples/card_examples.rb +4 -4
  36. data/spec/shared_stripe_examples/charge_examples.rb +9 -22
  37. data/spec/shared_stripe_examples/coupon_examples.rb +1 -1
  38. data/spec/shared_stripe_examples/customer_examples.rb +44 -30
  39. data/spec/shared_stripe_examples/dispute_examples.rb +2 -2
  40. data/spec/shared_stripe_examples/error_mock_examples.rb +8 -7
  41. data/spec/shared_stripe_examples/external_account_examples.rb +3 -3
  42. data/spec/shared_stripe_examples/invoice_examples.rb +41 -39
  43. data/spec/shared_stripe_examples/invoice_item_examples.rb +1 -1
  44. data/spec/shared_stripe_examples/payment_intent_examples.rb +137 -0
  45. data/spec/shared_stripe_examples/payment_method_examples.rb +185 -0
  46. data/spec/shared_stripe_examples/payout_examples.rb +2 -2
  47. data/spec/shared_stripe_examples/plan_examples.rb +135 -92
  48. data/spec/shared_stripe_examples/product_examples.rb +155 -0
  49. data/spec/shared_stripe_examples/refund_examples.rb +25 -21
  50. data/spec/shared_stripe_examples/setup_intent_examples.rb +68 -0
  51. data/spec/shared_stripe_examples/subscription_examples.rb +281 -310
  52. data/spec/shared_stripe_examples/subscription_items_examples.rb +3 -2
  53. data/spec/shared_stripe_examples/transfer_examples.rb +6 -6
  54. data/spec/shared_stripe_examples/webhook_event_examples.rb +11 -11
  55. data/spec/spec_helper.rb +3 -4
  56. data/spec/stripe_mock_spec.rb +2 -2
  57. data/spec/support/shared_contexts/stripe_validator_spec.rb +8 -0
  58. data/spec/support/stripe_examples.rb +3 -0
  59. data/stripe-ruby-mock.gemspec +2 -2
  60. metadata +47 -27
  61. data/spec/shared_stripe_examples/product_example.rb +0 -65
@@ -3,8 +3,8 @@ module StripeMock
3
3
  module BalanceTransactions
4
4
 
5
5
  def BalanceTransactions.included(klass)
6
- klass.add_handler 'get /v1/balance/history/(.*)', :get_balance_transaction
7
- klass.add_handler 'get /v1/balance/history', :list_balance_transactions
6
+ klass.add_handler 'get /v1/balance_transactions/(.*)', :get_balance_transaction
7
+ klass.add_handler 'get /v1/balance_transactions', :list_balance_transactions
8
8
  end
9
9
 
10
10
  def get_balance_transaction(route, method_url, params, headers)
@@ -32,7 +32,7 @@ module StripeMock
32
32
  start_time = options[:current_period_start] || now
33
33
  params = { customer: cus[:id], current_period_start: start_time, created: created_time }
34
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/
35
+ keys_to_merge = /application_fee_percent|quantity|metadata|tax_percent|billing|days_until_due|default_tax_rates/
36
36
  params.merge! options.select {|k,v| k =~ keys_to_merge}
37
37
 
38
38
  if options[:cancel_at_period_end] == true
@@ -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', http_status: 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
@@ -99,7 +99,12 @@ module StripeMock
99
99
  invoice_lines = []
100
100
 
101
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])
102
+ unused_amount = (
103
+ subscription[:plan][:amount].to_f *
104
+ subscription[:quantity] *
105
+ (subscription[:current_period_end] - subscription_proration_date.to_i) / (subscription[:current_period_end] - subscription[:current_period_start])
106
+ ).ceil
107
+
103
108
  invoice_lines << Data.mock_line_item(
104
109
  id: new_id('ii'),
105
110
  amount: -unused_amount,
@@ -0,0 +1,175 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module PaymentIntents
4
+ ALLOWED_PARAMS = [:description, :metadata, :receipt_email, :shipping, :destination, :payment_method, :payment_method_types, :setup_future_usage, :transfer_data, :amount, :currency]
5
+
6
+ def PaymentIntents.included(klass)
7
+ klass.add_handler 'post /v1/payment_intents', :new_payment_intent
8
+ klass.add_handler 'get /v1/payment_intents', :get_payment_intents
9
+ klass.add_handler 'get /v1/payment_intents/(.*)', :get_payment_intent
10
+ klass.add_handler 'post /v1/payment_intents/(.*)/confirm', :confirm_payment_intent
11
+ klass.add_handler 'post /v1/payment_intents/(.*)/capture', :capture_payment_intent
12
+ klass.add_handler 'post /v1/payment_intents/(.*)/cancel', :cancel_payment_intent
13
+ klass.add_handler 'post /v1/payment_intents/(.*)', :update_payment_intent
14
+ end
15
+
16
+ def new_payment_intent(route, method_url, params, headers)
17
+ id = new_id('pi')
18
+
19
+ ensure_payment_intent_required_params(params)
20
+ status = case params[:amount]
21
+ when 3184 then 'requires_action'
22
+ when 3178 then 'requires_payment_method'
23
+ else
24
+ 'succeeded'
25
+ end
26
+ last_payment_error = params[:amount] == 3178 ? last_payment_error_generator(code: 'card_declined', decline_code: 'insufficient_funds', message: 'Not enough funds.') : nil
27
+ payment_intents[id] = Data.mock_payment_intent(
28
+ params.merge(
29
+ id: id,
30
+ status: status,
31
+ last_payment_error: last_payment_error
32
+ )
33
+ )
34
+
35
+ if params[:confirm] && status == 'succeeded'
36
+ payment_intents[id] = succeeded_payment_intent(payment_intents[id])
37
+ end
38
+
39
+ payment_intents[id].clone
40
+ end
41
+
42
+ def update_payment_intent(route, method_url, params, headers)
43
+ route =~ method_url
44
+ id = $1
45
+
46
+ payment_intent = assert_existence :payment_intent, id, payment_intents[id]
47
+ payment_intents[id] = Util.rmerge(payment_intent, params.select{ |k,v| ALLOWED_PARAMS.include?(k)})
48
+ end
49
+
50
+ def get_payment_intents(route, method_url, params, headers)
51
+ params[:offset] ||= 0
52
+ params[:limit] ||= 10
53
+
54
+ clone = payment_intents.clone
55
+
56
+ if params[:customer]
57
+ clone.delete_if { |k,v| v[:customer] != params[:customer] }
58
+ end
59
+
60
+ Data.mock_list_object(clone.values, params)
61
+ end
62
+
63
+ def get_payment_intent(route, method_url, params, headers)
64
+ route =~ method_url
65
+ payment_intent_id = $1 || params[:payment_intent]
66
+ payment_intent = assert_existence :payment_intent, payment_intent_id, payment_intents[payment_intent_id]
67
+
68
+ payment_intent = payment_intent.clone
69
+ payment_intent
70
+ end
71
+
72
+ def capture_payment_intent(route, method_url, params, headers)
73
+ route =~ method_url
74
+ payment_intent = assert_existence :payment_intent, $1, payment_intents[$1]
75
+
76
+ succeeded_payment_intent(payment_intent)
77
+ end
78
+
79
+ def confirm_payment_intent(route, method_url, params, headers)
80
+ route =~ method_url
81
+ payment_intent = assert_existence :payment_intent, $1, payment_intents[$1]
82
+
83
+ succeeded_payment_intent(payment_intent)
84
+ end
85
+
86
+ def cancel_payment_intent(route, method_url, params, headers)
87
+ route =~ method_url
88
+ payment_intent = assert_existence :payment_intent, $1, payment_intents[$1]
89
+
90
+ payment_intent[:status] = 'canceled'
91
+ payment_intent
92
+ end
93
+
94
+ private
95
+
96
+ def ensure_payment_intent_required_params(params)
97
+ if params[:amount].nil?
98
+ require_param(:amount)
99
+ elsif params[:currency].nil?
100
+ require_param(:currency)
101
+ elsif non_integer_charge_amount?(params)
102
+ raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400)
103
+ elsif non_positive_charge_amount?(params)
104
+ raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', http_status: 400)
105
+ end
106
+ end
107
+
108
+ def non_integer_charge_amount?(params)
109
+ params[:amount] && !params[:amount].is_a?(Integer)
110
+ end
111
+
112
+ def non_positive_charge_amount?(params)
113
+ params[:amount] && params[:amount] < 1
114
+ end
115
+
116
+ def last_payment_error_generator(code: nil, message: nil, decline_code: nil)
117
+ {
118
+ code: code,
119
+ doc_url: "https://stripe.com/docs/error-codes/payment-intent-authentication-failure",
120
+ message: message,
121
+ decline_code: decline_code,
122
+ payment_method: {
123
+ id: "pm_1EwXFA2eZvKYlo2C0tlY091l",
124
+ object: "payment_method",
125
+ billing_details: {
126
+ address: {
127
+ city: nil,
128
+ country: nil,
129
+ line1: nil,
130
+ line2: nil,
131
+ postal_code: nil,
132
+ state: nil
133
+ },
134
+ email: nil,
135
+ name: "seller_08072019090000",
136
+ phone: nil
137
+ },
138
+ card: {
139
+ brand: "visa",
140
+ checks: {
141
+ address_line1_check: nil,
142
+ address_postal_code_check: nil,
143
+ cvc_check: "unchecked"
144
+ },
145
+ country: "US",
146
+ exp_month: 12,
147
+ exp_year: 2021,
148
+ fingerprint: "LQBhEmJnItuj3mxf",
149
+ funding: "credit",
150
+ generated_from: nil,
151
+ last4: "1629",
152
+ three_d_secure_usage: {
153
+ supported: true
154
+ },
155
+ wallet: nil
156
+ },
157
+ created: 1563208900,
158
+ customer: nil,
159
+ livemode: false,
160
+ metadata: {},
161
+ type: "card"
162
+ },
163
+ type: "invalid_request_error"
164
+ }
165
+ end
166
+
167
+ def succeeded_payment_intent(payment_intent)
168
+ payment_intent[:status] = 'succeeded'
169
+ btxn = new_balance_transaction('txn', { source: payment_intent[:id] })
170
+ payment_intent[:charges][:data] << Data.mock_charge(balance_transaction: btxn)
171
+ payment_intent
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,138 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module PaymentMethods
4
+
5
+ ALLOWED_PARAMS = [:customer, :type]
6
+
7
+ def PaymentMethods.included(klass)
8
+ klass.add_handler 'post /v1/payment_methods', :new_payment_method
9
+ klass.add_handler 'get /v1/payment_methods/(.*)', :get_payment_method
10
+ klass.add_handler 'get /v1/payment_methods', :get_payment_methods
11
+ klass.add_handler 'post /v1/payment_methods/(.*)/attach', :attach_payment_method
12
+ klass.add_handler 'post /v1/payment_methods/(.*)/detach', :detach_payment_method
13
+ klass.add_handler 'post /v1/payment_methods/(.*)', :update_payment_method
14
+ end
15
+
16
+ # post /v1/payment_methods
17
+ def new_payment_method(route, method_url, params, headers)
18
+ id = new_id('pm')
19
+
20
+ ensure_payment_method_required_params(params)
21
+
22
+ payment_methods[id] = Data.mock_payment_method(
23
+ params.merge(
24
+ id: id
25
+ )
26
+ )
27
+
28
+ payment_methods[id].clone
29
+ end
30
+
31
+ #
32
+ # params: {:type=>"card", :customer=>"test_cus_3"}
33
+ #
34
+ # get /v1/payment_methods/:id
35
+ def get_payment_method(route, method_url, params, headers)
36
+ id = method_url.match(route)[1] || params[:payment_method]
37
+ payment_method = assert_existence :payment_method, id, payment_methods[id]
38
+
39
+ payment_method.clone
40
+ end
41
+
42
+ # get /v1/payment_methods
43
+ def get_payment_methods(route, method_url, params, headers)
44
+ params[:offset] ||= 0
45
+ params[:limit] ||= 10
46
+
47
+ clone = payment_methods.clone
48
+
49
+ if params[:customer]
50
+ clone.delete_if { |_k, v| v[:customer] != params[:customer] }
51
+ end
52
+
53
+ Data.mock_list_object(clone.values, params)
54
+ end
55
+
56
+ #
57
+ # params: {:customer=>"test_cus_3"}
58
+ #
59
+ def attach_payment_method(route, method_url, params, headers)
60
+ route =~ method_url
61
+ id = $1
62
+ payment_methods[id].merge!(params)
63
+ payment_methods[id].clone
64
+ end
65
+
66
+ def detach_payment_method(route, method_url, params, headers)
67
+
68
+ end
69
+
70
+ def get_payment_method(route, method_url, params, headers)
71
+ route =~ method_url
72
+ id = $1
73
+ assert_existence(:payment_method, $1, payment_methods[id])
74
+ end
75
+
76
+ # post /v1/payment_methods/:id/attach
77
+ def attach_payment_method(route, method_url, params, headers)
78
+ allowed_params = [:customer]
79
+
80
+ id = method_url.match(route)[1]
81
+
82
+ assert_existence :customer, params[:customer], customers[params[:customer]]
83
+
84
+ payment_method = assert_existence :payment_method, id, payment_methods[id]
85
+ payment_methods[id] = Util.rmerge(payment_method, params.select { |k, _v| allowed_params.include?(k) })
86
+ payment_methods[id].clone
87
+ end
88
+
89
+ # post /v1/payment_methods/:id/detach
90
+ def detach_payment_method(route, method_url, params, headers)
91
+ id = method_url.match(route)[1]
92
+
93
+ payment_method = assert_existence :payment_method, id, payment_methods[id]
94
+ payment_method[:customer] = nil
95
+
96
+ payment_method.clone
97
+ end
98
+
99
+ # post /v1/payment_methods/:id
100
+ def update_payment_method(route, method_url, params, headers)
101
+ allowed_params = [:billing_details, :card, :metadata]
102
+
103
+ id = method_url.match(route)[1]
104
+
105
+ payment_method = assert_existence :payment_method, id, payment_methods[id]
106
+
107
+ if payment_method[:customer].nil?
108
+ raise Stripe::InvalidRequestError.new(
109
+ 'You must save this PaymentMethod to a customer before you can update it.',
110
+ http_status: 400
111
+ )
112
+ end
113
+
114
+ payment_methods[id] =
115
+ Util.rmerge(payment_method, params.select { |k, _v| allowed_params.include?(k)} )
116
+
117
+ payment_methods[id].clone
118
+ end
119
+
120
+ private
121
+
122
+ def ensure_payment_method_required_params(params)
123
+ require_param(:type) if params[:type].nil?
124
+
125
+ if invalid_type?(params[:type])
126
+ raise Stripe::InvalidRequestError.new(
127
+ 'Invalid type: must be one of card or card_present',
128
+ http_status: 400
129
+ )
130
+ end
131
+ end
132
+
133
+ def invalid_type?(type)
134
+ !['card', 'card_present'].include?(type)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -11,6 +11,7 @@ module StripeMock
11
11
 
12
12
  def create_product(_route, _method_url, params, _headers)
13
13
  params[:id] ||= new_id('prod')
14
+ validate_create_product_params(params)
14
15
  products[params[:id]] = Data.mock_product(params)
15
16
  end
16
17
 
@@ -0,0 +1,93 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module SetupIntents
4
+ ALLOWED_PARAMS = [
5
+ :confirm,
6
+ :customer,
7
+ :description,
8
+ :metadata,
9
+ :on_behalf_of,
10
+ :payment_method,
11
+ :payment_method_options,
12
+ :payment_method_types,
13
+ :return_url,
14
+ :usage
15
+ ]
16
+
17
+ def SetupIntents.included(klass)
18
+ klass.add_handler 'post /v1/setup_intents', :new_setup_intent
19
+ klass.add_handler 'get /v1/setup_intents', :get_setup_intents
20
+ klass.add_handler 'get /v1/setup_intents/(.*)', :get_setup_intent
21
+ klass.add_handler 'post /v1/setup_intents/(.*)/confirm', :confirm_setup_intent
22
+ klass.add_handler 'post /v1/setup_intents/(.*)/cancel', :cancel_setup_intent
23
+ klass.add_handler 'post /v1/setup_intents/(.*)', :update_setup_intent
24
+ end
25
+
26
+ def new_setup_intent(route, method_url, params, headers)
27
+ id = new_id('si')
28
+
29
+ setup_intents[id] = Data.mock_setup_intent(
30
+ params.merge(
31
+ id: id
32
+ )
33
+ )
34
+
35
+ setup_intents[id].clone
36
+ end
37
+
38
+ def update_setup_intent(route, method_url, params, headers)
39
+ route =~ method_url
40
+ id = $1
41
+
42
+ setup_intent = assert_existence :setup_intent, id, setup_intents[id]
43
+ setup_intents[id] = Util.rmerge(setup_intent, params.select{ |k,v| ALLOWED_PARAMS.include?(k)})
44
+ end
45
+
46
+ def get_setup_intents(route, method_url, params, headers)
47
+ params[:offset] ||= 0
48
+ params[:limit] ||= 10
49
+
50
+ clone = setup_intents.clone
51
+
52
+ if params[:customer]
53
+ clone.delete_if { |k,v| v[:customer] != params[:customer] }
54
+ end
55
+
56
+ Data.mock_list_object(clone.values, params)
57
+ end
58
+
59
+ def get_setup_intent(route, method_url, params, headers)
60
+ route =~ method_url
61
+ setup_intent_id = $1 || params[:setup_intent]
62
+ setup_intent = assert_existence :setup_intent, setup_intent_id, setup_intents[setup_intent_id]
63
+
64
+ setup_intent = setup_intent.clone
65
+ setup_intent
66
+ end
67
+
68
+ def capture_setup_intent(route, method_url, params, headers)
69
+ route =~ method_url
70
+ setup_intent = assert_existence :setup_intent, $1, setup_intents[$1]
71
+
72
+ setup_intent[:status] = 'succeeded'
73
+ setup_intent
74
+ end
75
+
76
+ def confirm_setup_intent(route, method_url, params, headers)
77
+ route =~ method_url
78
+ setup_intent = assert_existence :setup_intent, $1, setup_intents[$1]
79
+
80
+ setup_intent[:status] = 'succeeded'
81
+ setup_intent
82
+ end
83
+
84
+ def cancel_setup_intent(route, method_url, params, headers)
85
+ route =~ method_url
86
+ setup_intent = assert_existence :setup_intent, $1, setup_intents[$1]
87
+
88
+ setup_intent[:status] = 'canceled'
89
+ setup_intent
90
+ end
91
+ end
92
+ end
93
+ end