stripe-ruby-mock 2.5.6 → 3.1.0.rc2

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 (97) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -1
  3. data/.travis.yml +6 -9
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile +1 -0
  6. data/README.md +17 -11
  7. data/lib/stripe_mock.rb +11 -0
  8. data/lib/stripe_mock/api/client.rb +1 -1
  9. data/lib/stripe_mock/api/errors.rb +34 -28
  10. data/lib/stripe_mock/api/instance.rb +1 -1
  11. data/lib/stripe_mock/api/webhooks.rb +7 -0
  12. data/lib/stripe_mock/client.rb +2 -1
  13. data/lib/stripe_mock/data.rb +323 -13
  14. data/lib/stripe_mock/data/list.rb +42 -9
  15. data/lib/stripe_mock/instance.rb +52 -5
  16. data/lib/stripe_mock/request_handlers/account_links.rb +15 -0
  17. data/lib/stripe_mock/request_handlers/balance_transactions.rb +2 -2
  18. data/lib/stripe_mock/request_handlers/charges.rb +13 -12
  19. data/lib/stripe_mock/request_handlers/checkout.rb +15 -0
  20. data/lib/stripe_mock/request_handlers/checkout_session.rb +16 -0
  21. data/lib/stripe_mock/request_handlers/customers.rb +35 -18
  22. data/lib/stripe_mock/request_handlers/ephemeral_key.rb +1 -1
  23. data/lib/stripe_mock/request_handlers/express_login_links.rb +15 -0
  24. data/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb +14 -10
  25. data/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +22 -7
  26. data/lib/stripe_mock/request_handlers/helpers/token_helpers.rb +1 -1
  27. data/lib/stripe_mock/request_handlers/invoices.rb +11 -5
  28. data/lib/stripe_mock/request_handlers/payment_intents.rb +182 -0
  29. data/lib/stripe_mock/request_handlers/payment_methods.rb +120 -0
  30. data/lib/stripe_mock/request_handlers/plans.rb +1 -1
  31. data/lib/stripe_mock/request_handlers/prices.rb +44 -0
  32. data/lib/stripe_mock/request_handlers/products.rb +44 -0
  33. data/lib/stripe_mock/request_handlers/refunds.rb +6 -3
  34. data/lib/stripe_mock/request_handlers/setup_intents.rb +93 -0
  35. data/lib/stripe_mock/request_handlers/sources.rb +12 -6
  36. data/lib/stripe_mock/request_handlers/subscription_items.rb +36 -0
  37. data/lib/stripe_mock/request_handlers/subscriptions.rb +82 -41
  38. data/lib/stripe_mock/request_handlers/tax_rates.rb +36 -0
  39. data/lib/stripe_mock/request_handlers/tokens.rb +8 -6
  40. data/lib/stripe_mock/request_handlers/validators/param_validators.rb +130 -9
  41. data/lib/stripe_mock/test_strategies/base.rb +68 -8
  42. data/lib/stripe_mock/test_strategies/live.rb +23 -12
  43. data/lib/stripe_mock/test_strategies/mock.rb +6 -2
  44. data/lib/stripe_mock/version.rb +1 -1
  45. data/lib/stripe_mock/webhook_fixtures/balance.available.json +6 -0
  46. data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_reinstated.json +88 -0
  47. data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_withdrawn.json +88 -0
  48. data/lib/stripe_mock/webhook_fixtures/charge.failed.json +166 -38
  49. data/lib/stripe_mock/webhook_fixtures/customer.created.json +1 -0
  50. data/lib/stripe_mock/webhook_fixtures/customer.updated.json +1 -0
  51. data/lib/stripe_mock/webhook_fixtures/invoice.created.json +2 -1
  52. data/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json +1 -1
  53. data/lib/stripe_mock/webhook_fixtures/invoice.updated.json +2 -1
  54. data/lib/stripe_mock/webhook_fixtures/payment_intent.payment_failed.json +186 -0
  55. data/lib/stripe_mock/webhook_fixtures/payment_intent.succeeded.json +164 -0
  56. data/lib/stripe_mock/webhook_fixtures/product.created.json +34 -0
  57. data/lib/stripe_mock/webhook_fixtures/product.deleted.json +34 -0
  58. data/lib/stripe_mock/webhook_fixtures/product.updated.json +38 -0
  59. data/spec/instance_spec.rb +10 -12
  60. data/spec/list_spec.rb +38 -0
  61. data/spec/server_spec.rb +6 -3
  62. data/spec/shared_stripe_examples/account_examples.rb +1 -1
  63. data/spec/shared_stripe_examples/account_link_examples.rb +16 -0
  64. data/spec/shared_stripe_examples/balance_examples.rb +6 -0
  65. data/spec/shared_stripe_examples/balance_transaction_examples.rb +3 -3
  66. data/spec/shared_stripe_examples/bank_examples.rb +3 -3
  67. data/spec/shared_stripe_examples/card_examples.rb +4 -4
  68. data/spec/shared_stripe_examples/card_token_examples.rb +17 -21
  69. data/spec/shared_stripe_examples/charge_examples.rb +32 -36
  70. data/spec/shared_stripe_examples/checkout_examples.rb +38 -0
  71. data/spec/shared_stripe_examples/coupon_examples.rb +1 -1
  72. data/spec/shared_stripe_examples/customer_examples.rb +103 -53
  73. data/spec/shared_stripe_examples/dispute_examples.rb +2 -2
  74. data/spec/shared_stripe_examples/error_mock_examples.rb +8 -7
  75. data/spec/shared_stripe_examples/express_login_link_examples.rb +12 -0
  76. data/spec/shared_stripe_examples/external_account_examples.rb +3 -3
  77. data/spec/shared_stripe_examples/invoice_examples.rb +43 -41
  78. data/spec/shared_stripe_examples/invoice_item_examples.rb +1 -1
  79. data/spec/shared_stripe_examples/payment_intent_examples.rb +147 -0
  80. data/spec/shared_stripe_examples/payment_method_examples.rb +449 -0
  81. data/spec/shared_stripe_examples/payout_examples.rb +2 -2
  82. data/spec/shared_stripe_examples/plan_examples.rb +135 -77
  83. data/spec/shared_stripe_examples/price_examples.rb +183 -0
  84. data/spec/shared_stripe_examples/product_examples.rb +155 -0
  85. data/spec/shared_stripe_examples/refund_examples.rb +41 -31
  86. data/spec/shared_stripe_examples/setup_intent_examples.rb +68 -0
  87. data/spec/shared_stripe_examples/subscription_examples.rb +546 -295
  88. data/spec/shared_stripe_examples/subscription_items_examples.rb +76 -0
  89. data/spec/shared_stripe_examples/tax_rate_examples.rb +42 -0
  90. data/spec/shared_stripe_examples/transfer_examples.rb +9 -9
  91. data/spec/shared_stripe_examples/webhook_event_examples.rb +11 -11
  92. data/spec/spec_helper.rb +7 -4
  93. data/spec/stripe_mock_spec.rb +4 -4
  94. data/spec/support/shared_contexts/stripe_validator_spec.rb +8 -0
  95. data/spec/support/stripe_examples.rb +12 -2
  96. data/stripe-ruby-mock.gemspec +8 -3
  97. metadata +81 -32
@@ -6,7 +6,7 @@ module StripeMock
6
6
  end
7
7
 
8
8
  def create_ephemeral_key(route, method_url, params, headers)
9
- Data.mock_ephemeral_key(params)
9
+ Data.mock_ephemeral_key(**params)
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,15 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module ExpressLoginLinks
4
+
5
+ def ExpressLoginLinks.included(klass)
6
+ klass.add_handler 'post /v1/accounts/(.*)/login_links', :new_account_login_link
7
+ end
8
+
9
+ def new_account_login_link(route, method_url, params, headers)
10
+ route =~ method_url
11
+ Data.mock_express_login_link(params)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,18 +1,22 @@
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
- def add_coupon_to_customer(customer, coupon)
6
- customer[:discount] = {
7
- coupon: coupon,
8
- customer: customer[:id],
9
- start: Time.now.to_i,
10
- }
11
- customer[:discount][:end] = (DateTime.now >> coupon[:duration_in_months]).to_time.to_i if coupon[:duration].to_sym == :repeating && coupon[:duration_in_months]
12
-
13
- customer
12
+ object[:discount] = Stripe::Discount.construct_from(discount_attrs)
13
+ object
14
14
  end
15
15
 
16
+ def delete_coupon_from_object(object)
17
+ object[:discount] = nil
18
+ object
19
+ end
16
20
  end
17
21
  end
18
- end
22
+ end
@@ -8,13 +8,16 @@ module StripeMock
8
8
 
9
9
  def resolve_subscription_changes(subscription, plans, customer, options = {})
10
10
  subscription.merge!(custom_subscription_params(plans, customer, options))
11
+ items = options[:items]
12
+ items = items.values if items.respond_to?(:values)
11
13
  subscription[:items][:data] = plans.map do |plan|
12
- if options[:items] && options[:items].size == plans.size
13
- quantity = options[:items] &&
14
- options[:items].detect { |item| item[:plan] == plan[:id] }[:quantity] || 1
15
- Data.mock_subscription_item({ plan: plan, quantity: quantity })
14
+ matching_item = items && items.detect { |item| [item[:price], item[:plan]].include? plan[:id] }
15
+ if matching_item
16
+ quantity = matching_item[:quantity] || 1
17
+ id = matching_item[:id] || new_id('si')
18
+ Data.mock_subscription_item({ plan: plan, quantity: quantity, id: id })
16
19
  else
17
- Data.mock_subscription_item({ plan: plan })
20
+ Data.mock_subscription_item({ plan: plan, id: new_id('si') })
18
21
  end
19
22
  end
20
23
  subscription
@@ -30,7 +33,15 @@ module StripeMock
30
33
  start_time = options[:current_period_start] || now
31
34
  params = { customer: cus[:id], current_period_start: start_time, created: created_time }
32
35
  params.merge!({ :plan => (plans.size == 1 ? plans.first : nil) })
33
- params.merge! options.select {|k,v| k =~ /application_fee_percent|quantity|metadata|tax_percent/}
36
+ keys_to_merge = /application_fee_percent|quantity|metadata|tax_percent|billing|days_until_due|default_tax_rates|pending_invoice_item_interval|default_payment_method|collection_method/
37
+ params.merge! options.select {|k,v| k =~ keys_to_merge}
38
+
39
+ if options[:cancel_at_period_end] == true
40
+ params.merge!(cancel_at_period_end: true, canceled_at: now)
41
+ elsif options[:cancel_at_period_end] == false
42
+ params.merge!(cancel_at_period_end: false, canceled_at: nil)
43
+ end
44
+
34
45
  # TODO: Implement coupon logic
35
46
 
36
47
  if (((plan && plan[:trial_period_days]) || 0) == 0 && options[:trial_end].nil?) || options[:trial_end] == "now"
@@ -101,7 +112,11 @@ module StripeMock
101
112
 
102
113
  def total_items_amount(items)
103
114
  total = 0
104
- items.each { |i| total += (i[:quantity] || 1) * i[:plan][:amount] }
115
+ items.each do |item|
116
+ quantity = item[:quantity] || 1
117
+ amount = item[:plan][:unit_amount] || item[:plan][:amount]
118
+ total += quantity * amount
119
+ end
105
120
  total
106
121
  end
107
122
  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', 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
@@ -56,13 +56,14 @@ module StripeMock
56
56
  invoices[$1].merge!(:paid => true, :attempted => true, :charge => charge[:id])
57
57
  end
58
58
 
59
- def upcoming_invoice(route, method_url, params, headers)
59
+ def upcoming_invoice(route, method_url, params, headers = {})
60
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
60
61
  route =~ method_url
61
- raise Stripe::InvalidRequestError.new('Missing required param: customer', nil, http_status: 400) if params[:customer].nil?
62
+ raise Stripe::InvalidRequestError.new('Missing required param: customer if subscription is not provided', nil, http_status: 400) if params[:customer].nil? && params[:subscription].nil?
62
63
  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
64
  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?
64
65
 
65
- customer = customers[params[:customer]]
66
+ customer = customers[stripe_account][params[:customer]]
66
67
  assert_existence :customer, params[:customer], customer
67
68
 
68
69
  raise Stripe::InvalidRequestError.new("No upcoming invoices for customer: #{customer[:id]}", nil, http_status: 404) if customer[:subscriptions][:data].length == 0
@@ -99,7 +100,12 @@ module StripeMock
99
100
  invoice_lines = []
100
101
 
101
102
  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
+ unused_amount = (
104
+ subscription[:plan][:amount].to_f *
105
+ subscription[:quantity] *
106
+ (subscription[:current_period_end] - subscription_proration_date.to_i) / (subscription[:current_period_end] - subscription[:current_period_start])
107
+ ).ceil
108
+
103
109
  invoice_lines << Data.mock_line_item(
104
110
  id: new_id('ii'),
105
111
  amount: -unused_amount,
@@ -138,7 +144,7 @@ module StripeMock
138
144
  id: new_id('in'),
139
145
  customer: customer[:id],
140
146
  discount: customer[:discount],
141
- date: invoice_date,
147
+ created: invoice_date,
142
148
  starting_balance: customer[:account_balance],
143
149
  subscription: preview_subscription[:id],
144
150
  period_start: prorating ? invoice_date : preview_subscription[:current_period_start],
@@ -0,0 +1,182 @@
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
+ when 3055 then 'requires_capture'
24
+ else
25
+ 'succeeded'
26
+ end
27
+ last_payment_error = params[:amount] == 3178 ? last_payment_error_generator(code: 'card_declined', decline_code: 'insufficient_funds', message: 'Not enough funds.') : nil
28
+ payment_intents[id] = Data.mock_payment_intent(
29
+ params.merge(
30
+ id: id,
31
+ status: status,
32
+ last_payment_error: last_payment_error
33
+ )
34
+ )
35
+
36
+ if params[:confirm] && status == 'succeeded'
37
+ payment_intents[id] = succeeded_payment_intent(payment_intents[id])
38
+ end
39
+
40
+ payment_intents[id].clone
41
+ end
42
+
43
+ def update_payment_intent(route, method_url, params, headers)
44
+ route =~ method_url
45
+ id = $1
46
+
47
+ payment_intent = assert_existence :payment_intent, id, payment_intents[id]
48
+ payment_intents[id] = Util.rmerge(payment_intent, params.select{ |k,v| ALLOWED_PARAMS.include?(k)})
49
+ end
50
+
51
+ def get_payment_intents(route, method_url, params, headers)
52
+ params[:offset] ||= 0
53
+ params[:limit] ||= 10
54
+
55
+ clone = payment_intents.clone
56
+
57
+ if params[:customer]
58
+ clone.delete_if { |k,v| v[:customer] != params[:customer] }
59
+ end
60
+
61
+ Data.mock_list_object(clone.values, params)
62
+ end
63
+
64
+ def get_payment_intent(route, method_url, params, headers)
65
+ route =~ method_url
66
+ payment_intent_id = $1 || params[:payment_intent]
67
+ payment_intent = assert_existence :payment_intent, payment_intent_id, payment_intents[payment_intent_id]
68
+
69
+ payment_intent = payment_intent.clone
70
+ payment_intent
71
+ end
72
+
73
+ def capture_payment_intent(route, method_url, params, headers)
74
+ route =~ method_url
75
+ payment_intent = assert_existence :payment_intent, $1, payment_intents[$1]
76
+
77
+ succeeded_payment_intent(payment_intent)
78
+ end
79
+
80
+ def confirm_payment_intent(route, method_url, params, headers)
81
+ route =~ method_url
82
+ payment_intent = assert_existence :payment_intent, $1, payment_intents[$1]
83
+
84
+ succeeded_payment_intent(payment_intent)
85
+ end
86
+
87
+ def cancel_payment_intent(route, method_url, params, headers)
88
+ route =~ method_url
89
+ payment_intent = assert_existence :payment_intent, $1, payment_intents[$1]
90
+
91
+ payment_intent[:status] = 'canceled'
92
+ payment_intent
93
+ end
94
+
95
+ private
96
+
97
+ def ensure_payment_intent_required_params(params)
98
+ if params[:amount].nil?
99
+ require_param(:amount)
100
+ elsif params[:currency].nil?
101
+ require_param(:currency)
102
+ elsif non_integer_charge_amount?(params)
103
+ raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400)
104
+ elsif non_positive_charge_amount?(params)
105
+ raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', http_status: 400)
106
+ end
107
+ end
108
+
109
+ def non_integer_charge_amount?(params)
110
+ params[:amount] && !params[:amount].is_a?(Integer)
111
+ end
112
+
113
+ def non_positive_charge_amount?(params)
114
+ params[:amount] && params[:amount] < 1
115
+ end
116
+
117
+ def last_payment_error_generator(code: nil, message: nil, decline_code: nil)
118
+ {
119
+ code: code,
120
+ doc_url: "https://stripe.com/docs/error-codes/payment-intent-authentication-failure",
121
+ message: message,
122
+ decline_code: decline_code,
123
+ payment_method: {
124
+ id: "pm_1EwXFA2eZvKYlo2C0tlY091l",
125
+ object: "payment_method",
126
+ billing_details: {
127
+ address: {
128
+ city: nil,
129
+ country: nil,
130
+ line1: nil,
131
+ line2: nil,
132
+ postal_code: nil,
133
+ state: nil
134
+ },
135
+ email: nil,
136
+ name: "seller_08072019090000",
137
+ phone: nil
138
+ },
139
+ card: {
140
+ brand: "visa",
141
+ checks: {
142
+ address_line1_check: nil,
143
+ address_postal_code_check: nil,
144
+ cvc_check: "unchecked"
145
+ },
146
+ country: "US",
147
+ exp_month: 12,
148
+ exp_year: 2021,
149
+ fingerprint: "LQBhEmJnItuj3mxf",
150
+ funding: "credit",
151
+ generated_from: nil,
152
+ last4: "1629",
153
+ three_d_secure_usage: {
154
+ supported: true
155
+ },
156
+ wallet: nil
157
+ },
158
+ created: 1563208900,
159
+ customer: nil,
160
+ livemode: false,
161
+ metadata: {},
162
+ type: "card"
163
+ },
164
+ type: "invalid_request_error"
165
+ }
166
+ end
167
+
168
+ def succeeded_payment_intent(payment_intent)
169
+ payment_intent[:status] = 'succeeded'
170
+ btxn = new_balance_transaction('txn', { source: payment_intent[:id] })
171
+
172
+ payment_intent[:charges][:data] << Data.mock_charge(
173
+ balance_transaction: btxn,
174
+ amount: payment_intent[:amount],
175
+ currency: payment_intent[:currency]
176
+ )
177
+
178
+ payment_intent
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,120 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module PaymentMethods
4
+ ALLOWED_PARAMS = [:customer, :type]
5
+
6
+ def PaymentMethods.included(klass)
7
+ klass.add_handler 'post /v1/payment_methods', :new_payment_method
8
+ klass.add_handler 'get /v1/payment_methods/(.*)', :get_payment_method
9
+ klass.add_handler 'get /v1/payment_methods', :get_payment_methods
10
+ klass.add_handler 'post /v1/payment_methods/(.*)/attach', :attach_payment_method
11
+ klass.add_handler 'post /v1/payment_methods/(.*)/detach', :detach_payment_method
12
+ klass.add_handler 'post /v1/payment_methods/(.*)', :update_payment_method
13
+ end
14
+
15
+ # post /v1/payment_methods
16
+ def new_payment_method(route, method_url, params, headers)
17
+ id = new_id('pm')
18
+
19
+ ensure_payment_method_required_params(params)
20
+
21
+ payment_methods[id] = Data.mock_payment_method(
22
+ params.merge(
23
+ id: id
24
+ )
25
+ )
26
+
27
+ payment_methods[id].clone
28
+ end
29
+
30
+ #
31
+ # params: {:type=>"card", :customer=>"test_cus_3"}
32
+ #
33
+ # get /v1/payment_methods/:id
34
+ def get_payment_method(route, method_url, params, headers)
35
+ id = method_url.match(route)[1] || params[:payment_method]
36
+ payment_method = assert_existence :payment_method, id, payment_methods[id]
37
+
38
+ payment_method.clone
39
+ end
40
+
41
+ # get /v1/payment_methods
42
+ def get_payment_methods(route, method_url, params, headers)
43
+ params[:offset] ||= 0
44
+ params[:limit] ||= 10
45
+
46
+ clone = payment_methods.clone
47
+
48
+ if params[:customer]
49
+ clone.delete_if { |_k, v| v[:customer] != params[:customer] }
50
+ end
51
+
52
+ Data.mock_list_object(clone.values, params)
53
+ end
54
+
55
+ # post /v1/payment_methods/:id/attach
56
+ def attach_payment_method(route, method_url, params, headers)
57
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
58
+ allowed_params = [:customer]
59
+
60
+ id = method_url.match(route)[1]
61
+
62
+ assert_existence :customer, params[:customer], customers[stripe_account][params[:customer]]
63
+
64
+ payment_method = assert_existence :payment_method, id, payment_methods[id]
65
+ payment_methods[id] = Util.rmerge(payment_method, params.select { |k, _v| allowed_params.include?(k) })
66
+ payment_methods[id].clone
67
+ end
68
+
69
+ # post /v1/payment_methods/:id/detach
70
+ def detach_payment_method(route, method_url, params, headers)
71
+ id = method_url.match(route)[1]
72
+
73
+ payment_method = assert_existence :payment_method, id, payment_methods[id]
74
+ payment_method[:customer] = nil
75
+
76
+ payment_method.clone
77
+ end
78
+
79
+ # post /v1/payment_methods/:id
80
+ def update_payment_method(route, method_url, params, headers)
81
+ allowed_params = [:billing_details, :card, :ideal, :sepa_debit, :metadata]
82
+
83
+ id = method_url.match(route)[1]
84
+
85
+ payment_method = assert_existence :payment_method, id, payment_methods[id]
86
+
87
+ if payment_method[:customer].nil?
88
+ raise Stripe::InvalidRequestError.new(
89
+ 'You must save this PaymentMethod to a customer before you can update it.',
90
+ nil,
91
+ http_status: 400
92
+ )
93
+ end
94
+
95
+ payment_methods[id] =
96
+ Util.rmerge(payment_method, params.select { |k, _v| allowed_params.include?(k)} )
97
+
98
+ payment_methods[id].clone
99
+ end
100
+
101
+ private
102
+
103
+ def ensure_payment_method_required_params(params)
104
+ require_param(:type) if params[:type].nil?
105
+
106
+ if invalid_type?(params[:type])
107
+ raise Stripe::InvalidRequestError.new(
108
+ 'Invalid type: must be one of card, ideal or sepa_debit',
109
+ nil,
110
+ http_status: 400
111
+ )
112
+ end
113
+ end
114
+
115
+ def invalid_type?(type)
116
+ !%w(card ideal sepa_debit).include?(type)
117
+ end
118
+ end
119
+ end
120
+ end