stripe 4.9.0 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (224) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +10 -0
  3. data/.rubocop.yml +28 -4
  4. data/.rubocop_todo.yml +11 -22
  5. data/.travis.yml +3 -6
  6. data/.vscode/extensions.json +7 -0
  7. data/.vscode/settings.json +8 -0
  8. data/CHANGELOG.md +102 -2
  9. data/Gemfile +2 -10
  10. data/README.md +96 -40
  11. data/Rakefile +8 -7
  12. data/VERSION +1 -1
  13. data/lib/stripe.rb +64 -85
  14. data/lib/stripe/api_operations/delete.rb +23 -1
  15. data/lib/stripe/api_operations/list.rb +0 -6
  16. data/lib/stripe/api_operations/nested_resource.rb +14 -7
  17. data/lib/stripe/api_operations/request.rb +3 -7
  18. data/lib/stripe/api_operations/save.rb +1 -3
  19. data/lib/stripe/api_resource.rb +50 -2
  20. data/lib/stripe/connection_manager.rb +141 -0
  21. data/lib/stripe/error_object.rb +94 -0
  22. data/lib/stripe/errors.rb +22 -9
  23. data/lib/stripe/list_object.rb +11 -5
  24. data/lib/stripe/multipart_encoder.rb +131 -0
  25. data/lib/stripe/object_types.rb +94 -0
  26. data/lib/stripe/resources.rb +77 -0
  27. data/lib/stripe/{account.rb → resources/account.rb} +49 -27
  28. data/lib/stripe/{account_link.rb → resources/account_link.rb} +1 -1
  29. data/lib/stripe/resources/alipay_account.rb +34 -0
  30. data/lib/stripe/{apple_pay_domain.rb → resources/apple_pay_domain.rb} +1 -1
  31. data/lib/stripe/resources/application_fee.rb +13 -0
  32. data/lib/stripe/resources/application_fee_refund.rb +30 -0
  33. data/lib/stripe/{balance.rb → resources/balance.rb} +1 -1
  34. data/lib/stripe/{balance_transaction.rb → resources/balance_transaction.rb} +1 -5
  35. data/lib/stripe/{bank_account.rb → resources/bank_account.rb} +14 -4
  36. data/lib/stripe/{bitcoin_receiver.rb → resources/bitcoin_receiver.rb} +3 -3
  37. data/lib/stripe/{bitcoin_transaction.rb → resources/bitcoin_transaction.rb} +1 -1
  38. data/lib/stripe/resources/capability.rb +33 -0
  39. data/lib/stripe/{card.rb → resources/card.rb} +12 -4
  40. data/lib/stripe/resources/charge.rb +22 -0
  41. data/lib/stripe/{checkout → resources/checkout}/session.rb +2 -2
  42. data/lib/stripe/{country_spec.rb → resources/country_spec.rb} +1 -1
  43. data/lib/stripe/{coupon.rb → resources/coupon.rb} +2 -2
  44. data/lib/stripe/resources/credit_note.rb +22 -0
  45. data/lib/stripe/resources/customer.rb +35 -0
  46. data/lib/stripe/resources/customer_balance_transaction.rb +30 -0
  47. data/lib/stripe/resources/discount.rb +7 -0
  48. data/lib/stripe/{dispute.rb → resources/dispute.rb} +9 -7
  49. data/lib/stripe/{ephemeral_key.rb → resources/ephemeral_key.rb} +5 -2
  50. data/lib/stripe/{event.rb → resources/event.rb} +1 -1
  51. data/lib/stripe/{exchange_rate.rb → resources/exchange_rate.rb} +1 -1
  52. data/lib/stripe/{file.rb → resources/file.rb} +8 -11
  53. data/lib/stripe/{file_link.rb → resources/file_link.rb} +2 -2
  54. data/lib/stripe/resources/invoice.rb +73 -0
  55. data/lib/stripe/{invoice_item.rb → resources/invoice_item.rb} +2 -2
  56. data/lib/stripe/{invoice_line_item.rb → resources/invoice_line_item.rb} +1 -1
  57. data/lib/stripe/resources/issuing/authorization.rb +33 -0
  58. data/lib/stripe/resources/issuing/card.rb +24 -0
  59. data/lib/stripe/{issuing → resources/issuing}/card_details.rb +1 -1
  60. data/lib/stripe/{issuing → resources/issuing}/cardholder.rb +2 -2
  61. data/lib/stripe/{issuing → resources/issuing}/dispute.rb +2 -2
  62. data/lib/stripe/{issuing → resources/issuing}/transaction.rb +2 -2
  63. data/lib/stripe/resources/login_link.rb +14 -0
  64. data/lib/stripe/resources/order.rb +32 -0
  65. data/lib/stripe/{order_return.rb → resources/order_return.rb} +1 -1
  66. data/lib/stripe/resources/payment_intent.rb +42 -0
  67. data/lib/stripe/resources/payment_method.rb +32 -0
  68. data/lib/stripe/resources/payout.rb +22 -0
  69. data/lib/stripe/{person.rb → resources/person.rb} +8 -3
  70. data/lib/stripe/{plan.rb → resources/plan.rb} +1 -1
  71. data/lib/stripe/{product.rb → resources/product.rb} +3 -3
  72. data/lib/stripe/resources/radar/early_fraud_warning.rb +11 -0
  73. data/lib/stripe/{radar → resources/radar}/value_list.rb +2 -2
  74. data/lib/stripe/{radar → resources/radar}/value_list_item.rb +2 -2
  75. data/lib/stripe/{recipient.rb → resources/recipient.rb} +2 -6
  76. data/lib/stripe/{recipient_transfer.rb → resources/recipient_transfer.rb} +1 -1
  77. data/lib/stripe/{refund.rb → resources/refund.rb} +1 -1
  78. data/lib/stripe/{reporting → resources/reporting}/report_run.rb +2 -2
  79. data/lib/stripe/{reporting → resources/reporting}/report_type.rb +2 -2
  80. data/lib/stripe/resources/reversal.rb +29 -0
  81. data/lib/stripe/resources/review.rb +20 -0
  82. data/lib/stripe/resources/setup_intent.rb +32 -0
  83. data/lib/stripe/{sigma → resources/sigma}/scheduled_query_run.rb +2 -2
  84. data/lib/stripe/{sku.rb → resources/sku.rb} +3 -3
  85. data/lib/stripe/{source.rb → resources/source.rb} +17 -15
  86. data/lib/stripe/{source_transaction.rb → resources/source_transaction.rb} +1 -1
  87. data/lib/stripe/resources/subscription.rb +25 -0
  88. data/lib/stripe/{subscription_item.rb → resources/subscription_item.rb} +5 -2
  89. data/lib/stripe/resources/subscription_schedule.rb +32 -0
  90. data/lib/stripe/resources/tax_id.rb +26 -0
  91. data/lib/stripe/resources/tax_rate.rb +11 -0
  92. data/lib/stripe/{terminal → resources/terminal}/connection_token.rb +2 -2
  93. data/lib/stripe/{terminal → resources/terminal}/location.rb +3 -2
  94. data/lib/stripe/{terminal → resources/terminal}/reader.rb +3 -2
  95. data/lib/stripe/{three_d_secure.rb → resources/three_d_secure.rb} +1 -1
  96. data/lib/stripe/{token.rb → resources/token.rb} +1 -1
  97. data/lib/stripe/resources/topup.rb +22 -0
  98. data/lib/stripe/resources/transfer.rb +26 -0
  99. data/lib/stripe/resources/usage_record.rb +7 -0
  100. data/lib/stripe/{usage_record_summary.rb → resources/usage_record_summary.rb} +1 -1
  101. data/lib/stripe/{webhook_endpoint.rb → resources/webhook_endpoint.rb} +2 -2
  102. data/lib/stripe/singleton_api_resource.rb +3 -1
  103. data/lib/stripe/stripe_client.rb +347 -218
  104. data/lib/stripe/stripe_object.rb +72 -59
  105. data/lib/stripe/stripe_response.rb +53 -21
  106. data/lib/stripe/util.rb +54 -109
  107. data/lib/stripe/version.rb +1 -1
  108. data/lib/stripe/webhook.rb +5 -3
  109. data/stripe.gemspec +14 -5
  110. data/test/stripe/account_link_test.rb +1 -1
  111. data/test/stripe/account_test.rb +193 -32
  112. data/test/stripe/alipay_account_test.rb +1 -1
  113. data/test/stripe/api_operations_test.rb +3 -4
  114. data/test/stripe/api_resource_test.rb +119 -30
  115. data/test/stripe/apple_pay_domain_test.rb +18 -5
  116. data/test/stripe/application_fee_refund_test.rb +1 -1
  117. data/test/stripe/application_fee_test.rb +45 -1
  118. data/test/stripe/balance_test.rb +1 -1
  119. data/test/stripe/balance_transaction_test.rb +20 -0
  120. data/test/stripe/bank_account_test.rb +1 -1
  121. data/test/stripe/capability_test.rb +45 -0
  122. data/test/stripe/charge_test.rb +13 -8
  123. data/test/stripe/checkout/session_test.rb +7 -1
  124. data/test/stripe/connection_manager_test.rb +138 -0
  125. data/test/stripe/country_spec_test.rb +1 -1
  126. data/test/stripe/coupon_test.rb +16 -6
  127. data/test/stripe/credit_note_test.rb +61 -0
  128. data/test/stripe/customer_balance_transaction_test.rb +37 -0
  129. data/test/stripe/customer_card_test.rb +1 -1
  130. data/test/stripe/customer_test.rb +151 -40
  131. data/test/stripe/dispute_test.rb +10 -1
  132. data/test/stripe/ephemeral_key_test.rb +8 -1
  133. data/test/stripe/errors_test.rb +30 -9
  134. data/test/stripe/exchange_rate_test.rb +1 -1
  135. data/test/stripe/file_link_test.rb +1 -1
  136. data/test/stripe/file_test.rb +19 -5
  137. data/test/stripe/invoice_item_test.rb +18 -7
  138. data/test/stripe/invoice_line_item_test.rb +1 -1
  139. data/test/stripe/invoice_test.rb +77 -9
  140. data/test/stripe/issuing/authorization_test.rb +33 -11
  141. data/test/stripe/issuing/card_test.rb +15 -6
  142. data/test/stripe/issuing/cardholder_test.rb +1 -1
  143. data/test/stripe/issuing/dispute_test.rb +1 -1
  144. data/test/stripe/issuing/transaction_test.rb +1 -1
  145. data/test/stripe/list_object_test.rb +1 -17
  146. data/test/stripe/login_link_test.rb +2 -2
  147. data/test/stripe/multipart_encoder_test.rb +130 -0
  148. data/test/stripe/oauth_test.rb +1 -1
  149. data/test/stripe/order_return_test.rb +1 -1
  150. data/test/stripe/order_test.rb +28 -3
  151. data/test/stripe/payment_intent_test.rb +31 -4
  152. data/test/stripe/payment_method_test.rb +84 -0
  153. data/test/stripe/payout_test.rb +8 -1
  154. data/test/stripe/person_test.rb +1 -1
  155. data/test/stripe/plan_test.rb +26 -20
  156. data/test/stripe/product_test.rb +16 -6
  157. data/test/stripe/radar/early_fraud_warning_test.rb +22 -0
  158. data/test/stripe/radar/value_list_item_test.rb +16 -6
  159. data/test/stripe/radar/value_list_test.rb +16 -6
  160. data/test/stripe/recipient_test.rb +18 -5
  161. data/test/stripe/refund_test.rb +1 -1
  162. data/test/stripe/reporting/report_run_test.rb +1 -1
  163. data/test/stripe/reporting/report_type_test.rb +1 -1
  164. data/test/stripe/reversal_test.rb +1 -1
  165. data/test/stripe/review_test.rb +1 -1
  166. data/test/stripe/setup_intent_test.rb +84 -0
  167. data/test/stripe/sigma/scheduled_query_run_test.rb +1 -1
  168. data/test/stripe/sku_test.rb +16 -6
  169. data/test/stripe/source_test.rb +14 -19
  170. data/test/stripe/source_transaction_test.rb +1 -1
  171. data/test/stripe/stripe_client_test.rb +242 -26
  172. data/test/stripe/stripe_object_test.rb +8 -36
  173. data/test/stripe/stripe_response_test.rb +71 -25
  174. data/test/stripe/subscription_item_test.rb +28 -6
  175. data/test/stripe/subscription_schedule_test.rb +19 -1
  176. data/test/stripe/subscription_test.rb +29 -9
  177. data/test/stripe/tax_id_test.rb +31 -0
  178. data/test/stripe/tax_rate_test.rb +43 -0
  179. data/test/stripe/terminal/connection_token_test.rb +1 -1
  180. data/test/stripe/terminal/location_test.rb +18 -1
  181. data/test/stripe/terminal/reader_test.rb +18 -1
  182. data/test/stripe/three_d_secure_test.rb +1 -1
  183. data/test/stripe/topup_test.rb +9 -1
  184. data/test/stripe/transfer_test.rb +46 -1
  185. data/test/stripe/usage_record_summary_test.rb +1 -1
  186. data/test/stripe/util_test.rb +1 -1
  187. data/test/stripe/webhook_endpoint_test.rb +18 -1
  188. data/test/stripe/webhook_test.rb +4 -4
  189. data/test/stripe_mock.rb +4 -3
  190. data/test/stripe_test.rb +1 -14
  191. data/test/test_helper.rb +14 -11
  192. metadata +117 -125
  193. data/lib/stripe/alipay_account.rb +0 -27
  194. data/lib/stripe/application_fee.rb +0 -23
  195. data/lib/stripe/application_fee_refund.rb +0 -22
  196. data/lib/stripe/charge.rb +0 -84
  197. data/lib/stripe/customer.rb +0 -90
  198. data/lib/stripe/invoice.rb +0 -48
  199. data/lib/stripe/issuer_fraud_record.rb +0 -9
  200. data/lib/stripe/issuing/authorization.rb +0 -22
  201. data/lib/stripe/issuing/card.rb +0 -18
  202. data/lib/stripe/login_link.rb +0 -11
  203. data/lib/stripe/order.rb +0 -31
  204. data/lib/stripe/payment_intent.rb +0 -26
  205. data/lib/stripe/payout.rb +0 -20
  206. data/lib/stripe/reversal.rb +0 -22
  207. data/lib/stripe/review.rb +0 -14
  208. data/lib/stripe/subscription.rb +0 -25
  209. data/lib/stripe/subscription_schedule.rb +0 -32
  210. data/lib/stripe/subscription_schedule_revision.rb +0 -25
  211. data/lib/stripe/topup.rb +0 -16
  212. data/lib/stripe/transfer.rb +0 -23
  213. data/lib/stripe/usage_record.rb +0 -14
  214. data/test/stripe/account_external_accounts_operations_test.rb +0 -69
  215. data/test/stripe/account_login_links_operations_test.rb +0 -21
  216. data/test/stripe/account_persons_operations_test.rb +0 -70
  217. data/test/stripe/application_fee_refunds_operations_test.rb +0 -56
  218. data/test/stripe/customer_sources_operations_test.rb +0 -64
  219. data/test/stripe/file_upload_test.rb +0 -76
  220. data/test/stripe/issuer_fraud_record_test.rb +0 -20
  221. data/test/stripe/subscription_schedule_revision_test.rb +0 -37
  222. data/test/stripe/subscription_schedule_revisions_operations_test.rb +0 -35
  223. data/test/stripe/transfer_reversals_operations_test.rb +0 -57
  224. data/test/stripe/usage_record_test.rb +0 -28
@@ -4,7 +4,7 @@ module Stripe
4
4
  class StripeObject
5
5
  include Enumerable
6
6
 
7
- @@permanent_attributes = Set.new([:id])
7
+ @@permanent_attributes = Set.new([:id]) # rubocop:disable Style/ClassVars
8
8
 
9
9
  # The default :id method is deprecated and isn't useful to us
10
10
  undef :id if method_defined?(:id)
@@ -93,10 +93,12 @@ module Stripe
93
93
  # considered to be equal if they have the same set of values and each one
94
94
  # of those values is the same.
95
95
  def ==(other)
96
- other.is_a?(StripeObject) && @values == other.instance_variable_get(:@values)
96
+ other.is_a?(StripeObject) &&
97
+ @values == other.instance_variable_get(:@values)
97
98
  end
98
99
 
99
- # Hash equality. As with `#==`, we consider two equivalent Stripe objects equal.
100
+ # Hash equality. As with `#==`, we consider two equivalent Stripe objects
101
+ # equal.
100
102
  def eql?(other)
101
103
  # Defer to the implementation on `#==`.
102
104
  self == other
@@ -121,21 +123,10 @@ module Stripe
121
123
 
122
124
  def inspect
123
125
  id_string = respond_to?(:id) && !id.nil? ? " id=#{id}" : ""
124
- "#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(@values)
126
+ "#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " +
127
+ JSON.pretty_generate(@values)
125
128
  end
126
129
 
127
- # Re-initializes the object based on a hash of values (usually one that's
128
- # come back from an API call). Adds or removes value accessors as necessary
129
- # and updates the state of internal data.
130
- #
131
- # Please don't use this method. If you're trying to do mass assignment, try
132
- # #initialize_from instead.
133
- def refresh_from(values, opts, partial = false)
134
- initialize_from(values, opts, partial)
135
- end
136
- extend Gem::Deprecate
137
- deprecate :refresh_from, "#update_attributes", 2016, 1
138
-
139
130
  # Mass assigns attributes on the model.
140
131
  #
141
132
  # This is a version of +update_attributes+ that takes some extra options
@@ -162,12 +153,12 @@ module Stripe
162
153
  end
163
154
  end
164
155
 
165
- def [](k)
166
- @values[k.to_sym]
156
+ def [](key)
157
+ @values[key.to_sym]
167
158
  end
168
159
 
169
- def []=(k, v)
170
- send(:"#{k}=", v)
160
+ def []=(key, value)
161
+ send(:"#{key}=", value)
171
162
  end
172
163
 
173
164
  def keys
@@ -178,17 +169,20 @@ module Stripe
178
169
  @values.values
179
170
  end
180
171
 
181
- def to_json(*_a)
172
+ def to_json(*_opts)
173
+ # TODO: pass opts to JSON.generate?
182
174
  JSON.generate(@values)
183
175
  end
184
176
 
185
- def as_json(*a)
186
- @values.as_json(*a)
177
+ def as_json(*opts)
178
+ @values.as_json(*opts)
187
179
  end
188
180
 
189
181
  def to_hash
190
182
  maybe_to_hash = lambda do |value|
191
- value && value.respond_to?(:to_hash) ? value.to_hash : value
183
+ return nil if value.nil?
184
+
185
+ value.respond_to?(:to_hash) ? value.to_hash : value
192
186
  end
193
187
 
194
188
  @values.each_with_object({}) do |(key, value), acc|
@@ -251,10 +245,11 @@ module Stripe
251
245
  # values within in that its parent StripeObject doesn't know about.
252
246
  #
253
247
  unsaved = @unsaved_values.include?(k)
254
- if options[:force] || unsaved || v.is_a?(StripeObject)
255
- update_hash[k.to_sym] =
256
- serialize_params_value(@values[k], @original_values[k], unsaved, options[:force], key: k)
257
- end
248
+ next unless options[:force] || unsaved || v.is_a?(StripeObject)
249
+
250
+ update_hash[k.to_sym] = serialize_params_value(
251
+ @values[k], @original_values[k], unsaved, options[:force], key: k
252
+ )
258
253
  end
259
254
 
260
255
  # a `nil` that makes it out of `#serialize_params_value` signals an empty
@@ -264,16 +259,6 @@ module Stripe
264
259
  update_hash
265
260
  end
266
261
 
267
- class << self
268
- # This class method has been deprecated in favor of the instance method
269
- # of the same name.
270
- def serialize_params(obj, options = {})
271
- obj.serialize_params(options)
272
- end
273
- extend Gem::Deprecate
274
- deprecate :serialize_params, "#serialize_params", 2016, 9
275
- end
276
-
277
262
  # A protected field is one that doesn't get an accessor assigned to it
278
263
  # (i.e. `obj.public = ...`) and one which is not allowed to be updated via
279
264
  # the class level `Model.update(id, { ... })`.
@@ -281,13 +266,11 @@ module Stripe
281
266
  []
282
267
  end
283
268
 
284
- protected
285
-
286
- def metaclass
269
+ protected def metaclass
287
270
  class << self; self; end
288
271
  end
289
272
 
290
- def remove_accessors(keys)
273
+ protected def remove_accessors(keys)
291
274
  # not available in the #instance_eval below
292
275
  protected_fields = self.class.protected_fields
293
276
 
@@ -298,13 +281,31 @@ module Stripe
298
281
 
299
282
  # Remove methods for the accessor's reader and writer.
300
283
  [k, :"#{k}=", :"#{k}?"].each do |method_name|
301
- remove_method(method_name) if method_defined?(method_name)
284
+ next unless method_defined?(method_name)
285
+
286
+ begin
287
+ remove_method(method_name)
288
+ rescue NameError
289
+ # In some cases there can be a method that's detected with
290
+ # `method_defined?`, but which cannot be removed with
291
+ # `remove_method`, even though it's on the same class. The only
292
+ # case so far that we've noticed this is when a class is
293
+ # reopened for monkey patching:
294
+ #
295
+ # https://github.com/stripe/stripe-ruby/issues/749
296
+ #
297
+ # Here we swallow that error and issue a warning so at least
298
+ # the program doesn't crash.
299
+ warn("WARNING: Unable to remove method `#{method_name}`; " \
300
+ "if custom, please consider renaming to a name that doesn't " \
301
+ "collide with an API property name.")
302
+ end
302
303
  end
303
304
  end
304
305
  end
305
306
  end
306
307
 
307
- def add_accessors(keys, values)
308
+ protected def add_accessors(keys, values)
308
309
  # not available in the #instance_eval below
309
310
  protected_fields = self.class.protected_fields
310
311
 
@@ -341,7 +342,11 @@ module Stripe
341
342
  end
342
343
  end
343
344
 
344
- def method_missing(name, *args)
345
+ # Disabling the cop because it's confused by the fact that the methods are
346
+ # protected, but we do define `#respond_to_missing?` just below. Hopefully
347
+ # this is fixed in more recent Rubocop versions.
348
+ # rubocop:disable Style/MissingRespondToMissing
349
+ protected def method_missing(name, *args)
345
350
  # TODO: only allow setting in updateable classes.
346
351
  if name.to_s.end_with?("=")
347
352
  attr = name.to_s[0...-1].to_sym
@@ -357,7 +362,9 @@ module Stripe
357
362
  begin
358
363
  mth = method(name)
359
364
  rescue NameError
360
- raise NoMethodError, "Cannot set #{attr} on this object. HINT: you can't set: #{@@permanent_attributes.to_a.join(', ')}"
365
+ raise NoMethodError,
366
+ "Cannot set #{attr} on this object. HINT: you can't set: " \
367
+ "#{@@permanent_attributes.to_a.join(', ')}"
361
368
  end
362
369
  return mth.call(args[0])
363
370
  elsif @values.key?(name)
@@ -372,11 +379,17 @@ module Stripe
372
379
  # raise right away.
373
380
  raise unless @transient_values.include?(name)
374
381
 
375
- raise NoMethodError, e.message + ". HINT: The '#{name}' attribute was set in the past, however. It was then wiped when refreshing the object with the result returned by Stripe's API, probably as a result of a save(). The attributes currently available on this object are: #{@values.keys.join(', ')}"
382
+ raise NoMethodError,
383
+ e.message + ". HINT: The '#{name}' attribute was set in the " \
384
+ "past, however. It was then wiped when refreshing the object " \
385
+ "with the result returned by Stripe's API, probably as a " \
386
+ "result of a save(). The attributes currently available on " \
387
+ "this object are: #{@values.keys.join(', ')}"
376
388
  end
377
389
  end
390
+ # rubocop:enable Style/MissingRespondToMissing
378
391
 
379
- def respond_to_missing?(symbol, include_private = false)
392
+ protected def respond_to_missing?(symbol, include_private = false)
380
393
  @values && @values.key?(symbol) || super
381
394
  end
382
395
 
@@ -392,7 +405,7 @@ module Stripe
392
405
  # * +:opts:+ Options for StripeObject like an API key.
393
406
  # * +:partial:+ Indicates that the re-initialization should not attempt to
394
407
  # remove accessors.
395
- def initialize_from(values, opts, partial = false)
408
+ protected def initialize_from(values, opts, partial = false)
396
409
  @opts = Util.normalize_opts(opts)
397
410
 
398
411
  # the `#send` is here so that we can keep this method private
@@ -402,8 +415,8 @@ module Stripe
402
415
  added = Set.new(values.keys - @values.keys)
403
416
 
404
417
  # Wipe old state before setting new. This is useful for e.g. updating a
405
- # customer, where there is no persistent card parameter. Mark those values
406
- # which don't persist as transient
418
+ # customer, where there is no persistent card parameter. Mark those
419
+ # values which don't persist as transient
407
420
 
408
421
  remove_accessors(removed)
409
422
  add_accessors(added, values)
@@ -423,7 +436,8 @@ module Stripe
423
436
  self
424
437
  end
425
438
 
426
- def serialize_params_value(value, original, unsaved, force, key: nil)
439
+ protected def serialize_params_value(value, original, unsaved, force,
440
+ key: nil)
427
441
  if value.nil?
428
442
  ""
429
443
 
@@ -498,11 +512,9 @@ module Stripe
498
512
  end
499
513
  end
500
514
 
501
- private
502
-
503
515
  # Produces a deep copy of the given object including support for arrays,
504
516
  # hashes, and StripeObjects.
505
- def self.deep_copy(obj)
517
+ private_class_method def self.deep_copy(obj)
506
518
  case obj
507
519
  when Array
508
520
  obj.map { |e| deep_copy(e) }
@@ -522,9 +534,8 @@ module Stripe
522
534
  obj
523
535
  end
524
536
  end
525
- private_class_method :deep_copy
526
537
 
527
- def dirty_value!(value)
538
+ private def dirty_value!(value)
528
539
  case value
529
540
  when Array
530
541
  value.map { |v| dirty_value!(v) }
@@ -535,12 +546,14 @@ module Stripe
535
546
 
536
547
  # Returns a hash of empty values for all the values that are in the given
537
548
  # StripeObject.
538
- def empty_values(obj)
549
+ private def empty_values(obj)
539
550
  values = case obj
540
551
  when Hash then obj
541
552
  when StripeObject then obj.instance_variable_get(:@values)
542
553
  else
543
- raise ArgumentError, "#empty_values got unexpected object type: #{obj.class.name}"
554
+ raise ArgumentError,
555
+ "#empty_values got unexpected object type: " \
556
+ "#{obj.class.name}"
544
557
  end
545
558
 
546
559
  values.each_with_object({}) do |(k, _), update|
@@ -4,6 +4,53 @@ module Stripe
4
4
  # StripeResponse encapsulates some vitals of a response that came back from
5
5
  # the Stripe API.
6
6
  class StripeResponse
7
+ # Headers provides an access wrapper to an API response's header data. It
8
+ # mainly exists so that we don't need to expose the entire
9
+ # `Net::HTTPResponse` object while still getting some of its benefits like
10
+ # case-insensitive access to header names and flattening of header values.
11
+ class Headers
12
+ # Initializes a Headers object from a Net::HTTP::HTTPResponse object.
13
+ def self.from_net_http(resp)
14
+ new(resp.to_hash)
15
+ end
16
+
17
+ # `hash` is expected to be a hash mapping header names to arrays of
18
+ # header values. This is the default format generated by calling
19
+ # `#to_hash` on a `Net::HTTPResponse` object because headers can be
20
+ # repeated multiple times. Using `#[]` will collapse values down to just
21
+ # the first.
22
+ def initialize(hash)
23
+ if !hash.is_a?(Hash) ||
24
+ !hash.keys.all? { |n| n.is_a?(String) } ||
25
+ !hash.values.all? { |a| a.is_a?(Array) } ||
26
+ !hash.values.all? { |a| a.all? { |v| v.is_a?(String) } }
27
+ raise ArgumentError,
28
+ "expect hash to be a map of string header names to arrays of " \
29
+ "header values"
30
+ end
31
+
32
+ @hash = {}
33
+
34
+ # This shouldn't be strictly necessary because `Net::HTTPResponse` will
35
+ # produce a hash with all headers downcased, but do it anyway just in
36
+ # case an object of this class was constructed manually.
37
+ #
38
+ # Also has the effect of duplicating the hash, which is desirable for a
39
+ # little extra object safety.
40
+ hash.each do |k, v|
41
+ @hash[k.downcase] = v
42
+ end
43
+ end
44
+
45
+ def [](name)
46
+ values = @hash[name.downcase]
47
+ if values && values.count > 1
48
+ warn("Duplicate header values for `#{name}`; returning only first")
49
+ end
50
+ values ? values.first : nil
51
+ end
52
+ end
53
+
7
54
  # The data contained by the HTTP body of the response deserialized from
8
55
  # JSON.
9
56
  attr_accessor :data
@@ -20,30 +67,15 @@ module Stripe
20
67
  # The Stripe request ID of the response.
21
68
  attr_accessor :request_id
22
69
 
23
- # Initializes a StripeResponse object from a Hash like the kind returned as
24
- # part of a Faraday exception.
25
- #
26
- # This may throw JSON::ParserError if the response body is not valid JSON.
27
- def self.from_faraday_hash(http_resp)
28
- resp = StripeResponse.new
29
- resp.data = JSON.parse(http_resp[:body], symbolize_names: true)
30
- resp.http_body = http_resp[:body]
31
- resp.http_headers = http_resp[:headers]
32
- resp.http_status = http_resp[:status]
33
- resp.request_id = http_resp[:headers]["Request-Id"]
34
- resp
35
- end
36
-
37
- # Initializes a StripeResponse object from a Faraday HTTP response object.
38
- #
39
- # This may throw JSON::ParserError if the response body is not valid JSON.
40
- def self.from_faraday_response(http_resp)
70
+ # Initializes a StripeResponse object from a Net::HTTP::HTTPResponse
71
+ # object.
72
+ def self.from_net_http(http_resp)
41
73
  resp = StripeResponse.new
42
74
  resp.data = JSON.parse(http_resp.body, symbolize_names: true)
43
75
  resp.http_body = http_resp.body
44
- resp.http_headers = http_resp.headers
45
- resp.http_status = http_resp.status
46
- resp.request_id = http_resp.headers["Request-Id"]
76
+ resp.http_headers = Headers.from_net_http(http_resp)
77
+ resp.http_status = http_resp.code.to_i
78
+ resp.request_id = http_resp["request-id"]
47
79
  resp
48
80
  end
49
81
  end
@@ -24,97 +24,27 @@ module Stripe
24
24
  OPTS_USER_SPECIFIED + Set[:client] - Set[:idempotency_key]
25
25
  ).freeze
26
26
 
27
- def self.objects_to_ids(h)
28
- case h
27
+ def self.objects_to_ids(obj)
28
+ case obj
29
29
  when APIResource
30
- h.id
30
+ obj.id
31
31
  when Hash
32
32
  res = {}
33
- h.each { |k, v| res[k] = objects_to_ids(v) unless v.nil? }
33
+ obj.each { |k, v| res[k] = objects_to_ids(v) unless v.nil? }
34
34
  res
35
35
  when Array
36
- h.map { |v| objects_to_ids(v) }
36
+ obj.map { |v| objects_to_ids(v) }
37
37
  else
38
- h
38
+ obj
39
39
  end
40
40
  end
41
41
 
42
- def self.object_classes # rubocop:disable Metrics/MethodLength
43
- @object_classes ||= {
44
- # data structures
45
- ListObject::OBJECT_NAME => ListObject,
46
-
47
- # business objects
48
- Account::OBJECT_NAME => Account,
49
- AccountLink::OBJECT_NAME => AccountLink,
50
- AlipayAccount::OBJECT_NAME => AlipayAccount,
51
- ApplePayDomain::OBJECT_NAME => ApplePayDomain,
52
- ApplicationFee::OBJECT_NAME => ApplicationFee,
53
- ApplicationFeeRefund::OBJECT_NAME => ApplicationFeeRefund,
54
- Balance::OBJECT_NAME => Balance,
55
- BalanceTransaction::OBJECT_NAME => BalanceTransaction,
56
- BankAccount::OBJECT_NAME => BankAccount,
57
- BitcoinReceiver::OBJECT_NAME => BitcoinReceiver,
58
- BitcoinTransaction::OBJECT_NAME => BitcoinTransaction,
59
- Card::OBJECT_NAME => Card,
60
- Charge::OBJECT_NAME => Charge,
61
- Checkout::Session::OBJECT_NAME => Checkout::Session,
62
- CountrySpec::OBJECT_NAME => CountrySpec,
63
- Coupon::OBJECT_NAME => Coupon,
64
- Customer::OBJECT_NAME => Customer,
65
- Dispute::OBJECT_NAME => Dispute,
66
- EphemeralKey::OBJECT_NAME => EphemeralKey,
67
- Event::OBJECT_NAME => Event,
68
- ExchangeRate::OBJECT_NAME => ExchangeRate,
69
- File::OBJECT_NAME => File,
70
- File::OBJECT_NAME_ALT => File,
71
- FileLink::OBJECT_NAME => FileLink,
72
- Invoice::OBJECT_NAME => Invoice,
73
- InvoiceItem::OBJECT_NAME => InvoiceItem,
74
- InvoiceLineItem::OBJECT_NAME => InvoiceLineItem,
75
- IssuerFraudRecord::OBJECT_NAME => IssuerFraudRecord,
76
- Issuing::Authorization::OBJECT_NAME => Issuing::Authorization,
77
- Issuing::Card::OBJECT_NAME => Issuing::Card,
78
- Issuing::CardDetails::OBJECT_NAME => Issuing::CardDetails,
79
- Issuing::Cardholder::OBJECT_NAME => Issuing::Cardholder,
80
- Issuing::Dispute::OBJECT_NAME => Issuing::Dispute,
81
- Issuing::Transaction::OBJECT_NAME => Issuing::Transaction,
82
- LoginLink::OBJECT_NAME => LoginLink,
83
- Order::OBJECT_NAME => Order,
84
- OrderReturn::OBJECT_NAME => OrderReturn,
85
- PaymentIntent::OBJECT_NAME => PaymentIntent,
86
- Payout::OBJECT_NAME => Payout,
87
- Person::OBJECT_NAME => Person,
88
- Plan::OBJECT_NAME => Plan,
89
- Product::OBJECT_NAME => Product,
90
- Radar::ValueList::OBJECT_NAME => Radar::ValueList,
91
- Radar::ValueListItem::OBJECT_NAME => Radar::ValueListItem,
92
- Recipient::OBJECT_NAME => Recipient,
93
- RecipientTransfer::OBJECT_NAME => RecipientTransfer,
94
- Refund::OBJECT_NAME => Refund,
95
- Reporting::ReportRun::OBJECT_NAME => Reporting::ReportRun,
96
- Reporting::ReportType::OBJECT_NAME => Reporting::ReportType,
97
- Reversal::OBJECT_NAME => Reversal,
98
- Review::OBJECT_NAME => Review,
99
- SKU::OBJECT_NAME => SKU,
100
- Sigma::ScheduledQueryRun::OBJECT_NAME => Sigma::ScheduledQueryRun,
101
- Source::OBJECT_NAME => Source,
102
- SourceTransaction::OBJECT_NAME => SourceTransaction,
103
- Subscription::OBJECT_NAME => Subscription,
104
- SubscriptionItem::OBJECT_NAME => SubscriptionItem,
105
- SubscriptionSchedule::OBJECT_NAME => SubscriptionSchedule,
106
- SubscriptionScheduleRevision::OBJECT_NAME => SubscriptionScheduleRevision,
107
- Terminal::ConnectionToken::OBJECT_NAME => Terminal::ConnectionToken,
108
- Terminal::Location::OBJECT_NAME => Terminal::Location,
109
- Terminal::Reader::OBJECT_NAME => Terminal::Reader,
110
- ThreeDSecure::OBJECT_NAME => ThreeDSecure,
111
- Token::OBJECT_NAME => Token,
112
- Topup::OBJECT_NAME => Topup,
113
- Transfer::OBJECT_NAME => Transfer,
114
- UsageRecord::OBJECT_NAME => UsageRecord,
115
- UsageRecordSummary::OBJECT_NAME => UsageRecordSummary,
116
- WebhookEndpoint::OBJECT_NAME => WebhookEndpoint,
117
- }
42
+ def self.object_classes
43
+ @object_classes ||= Stripe::ObjectTypes.object_names_to_classes
44
+ end
45
+
46
+ def self.object_name_matches_class?(object_name, klass)
47
+ Util.object_classes[object_name] == klass
118
48
  end
119
49
 
120
50
  # Converts a hash of fields or an array of hashes into a +StripeObject+ or
@@ -130,12 +60,16 @@ module Stripe
130
60
  # * +opts+ - Options for +StripeObject+ like an API key that will be reused
131
61
  # on subsequent API calls.
132
62
  def self.convert_to_stripe_object(data, opts = {})
63
+ opts = normalize_opts(opts)
64
+
133
65
  case data
134
66
  when Array
135
67
  data.map { |i| convert_to_stripe_object(i, opts) }
136
68
  when Hash
137
- # Try converting to a known object class. If none available, fall back to generic StripeObject
138
- object_classes.fetch(data[:object], StripeObject).construct_from(data, opts)
69
+ # Try converting to a known object class. If none available, fall back
70
+ # to generic StripeObject
71
+ object_classes.fetch(data[:object], StripeObject)
72
+ .construct_from(data, opts)
139
73
  else
140
74
  data
141
75
  end
@@ -144,24 +78,24 @@ module Stripe
144
78
  def self.log_error(message, data = {})
145
79
  if !Stripe.logger.nil? ||
146
80
  !Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_ERROR
147
- log_internal(message, data, color: :cyan,
148
- level: Stripe::LEVEL_ERROR, logger: Stripe.logger, out: $stderr)
81
+ log_internal(message, data, color: :cyan, level: Stripe::LEVEL_ERROR,
82
+ logger: Stripe.logger, out: $stderr)
149
83
  end
150
84
  end
151
85
 
152
86
  def self.log_info(message, data = {})
153
87
  if !Stripe.logger.nil? ||
154
88
  !Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_INFO
155
- log_internal(message, data, color: :cyan,
156
- level: Stripe::LEVEL_INFO, logger: Stripe.logger, out: $stdout)
89
+ log_internal(message, data, color: :cyan, level: Stripe::LEVEL_INFO,
90
+ logger: Stripe.logger, out: $stdout)
157
91
  end
158
92
  end
159
93
 
160
94
  def self.log_debug(message, data = {})
161
95
  if !Stripe.logger.nil? ||
162
96
  !Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_DEBUG
163
- log_internal(message, data, color: :blue,
164
- level: Stripe::LEVEL_DEBUG, logger: Stripe.logger, out: $stdout)
97
+ log_internal(message, data, color: :blue, level: Stripe::LEVEL_DEBUG,
98
+ logger: Stripe.logger, out: $stdout)
165
99
  end
166
100
  end
167
101
 
@@ -264,11 +198,13 @@ module Stripe
264
198
 
265
199
  def self.check_string_argument!(key)
266
200
  raise TypeError, "argument must be a string" unless key.is_a?(String)
201
+
267
202
  key
268
203
  end
269
204
 
270
205
  def self.check_api_key!(key)
271
206
  raise TypeError, "api_key must be a string" unless key.is_a?(String)
207
+
272
208
  key
273
209
  end
274
210
 
@@ -296,13 +232,13 @@ module Stripe
296
232
 
297
233
  # Constant time string comparison to prevent timing attacks
298
234
  # Code borrowed from ActiveSupport
299
- def self.secure_compare(a, b)
300
- return false unless a.bytesize == b.bytesize
235
+ def self.secure_compare(str_a, str_b)
236
+ return false unless str_a.bytesize == str_b.bytesize
301
237
 
302
- l = a.unpack "C#{a.bytesize}"
238
+ l = str_a.unpack "C#{str_a.bytesize}"
303
239
 
304
240
  res = 0
305
- b.each_byte { |byte| res |= byte ^ l.shift }
241
+ str_b.each_byte { |byte| res |= byte ^ l.shift }
306
242
  res.zero?
307
243
  end
308
244
 
@@ -311,14 +247,14 @@ module Stripe
311
247
  #
312
248
 
313
249
  COLOR_CODES = {
314
- black: 0, light_black: 60,
315
- red: 1, light_red: 61,
316
- green: 2, light_green: 62,
317
- yellow: 3, light_yellow: 63,
318
- blue: 4, light_blue: 64,
250
+ black: 0, light_black: 60,
251
+ red: 1, light_red: 61,
252
+ green: 2, light_green: 62,
253
+ yellow: 3, light_yellow: 63,
254
+ blue: 4, light_blue: 64,
319
255
  magenta: 5, light_magenta: 65,
320
- cyan: 6, light_cyan: 66,
321
- white: 7, light_white: 67,
256
+ cyan: 6, light_cyan: 66,
257
+ white: 7, light_white: 67,
322
258
  default: 9,
323
259
  }.freeze
324
260
  private_constant :COLOR_CODES
@@ -347,23 +283,32 @@ module Stripe
347
283
  end
348
284
  private_class_method :level_name
349
285
 
350
- # TODO: Make these named required arguments when we drop support for Ruby
351
- # 2.0.
352
- def self.log_internal(message, data = {}, color: nil, level: nil, logger: nil, out: nil)
286
+ def self.log_internal(message, data = {}, color:, level:, logger:, out:)
353
287
  data_str = data.reject { |_k, v| v.nil? }
354
288
  .map do |(k, v)|
355
- format("%s=%s", colorize(k, color, logger.nil? && !out.nil? && out.isatty), wrap_logfmt_value(v))
289
+ format("%<key>s=%<value>s",
290
+ key: colorize(k, color, logger.nil? && !out.nil? && out.isatty),
291
+ value: wrap_logfmt_value(v))
356
292
  end.join(" ")
357
293
 
358
294
  if !logger.nil?
359
295
  # the library's log levels are mapped to the same values as the
360
296
  # standard library's logger
361
297
  logger.log(level,
362
- format("message=%s %s", wrap_logfmt_value(message), data_str))
298
+ format("message=%<message>s %<data_str>s",
299
+ message: wrap_logfmt_value(message),
300
+ data_str: data_str))
363
301
  elsif out.isatty
364
- out.puts format("%s %s %s", colorize(level_name(level)[0, 4].upcase, color, out.isatty), message, data_str)
302
+ out.puts format("%<level>s %<message>s %<data_str>s",
303
+ level: colorize(level_name(level)[0, 4].upcase,
304
+ color, out.isatty),
305
+ message: message,
306
+ data_str: data_str)
365
307
  else
366
- out.puts format("message=%s level=%s %s", wrap_logfmt_value(message), level_name(level), data_str)
308
+ out.puts format("message=%<message>s level=%<level>s %<data_str>s",
309
+ message: wrap_logfmt_value(message),
310
+ level: level_name(level),
311
+ data_str: data_str)
367
312
  end
368
313
  end
369
314
  private_class_method :log_internal
@@ -381,7 +326,7 @@ module Stripe
381
326
  if %r{[^\w\-/]} =~ val
382
327
  # If the string contains any special characters, escape any double
383
328
  # quotes it has, remove newlines, and wrap the whole thing in quotes.
384
- format(%("%s"), val.gsub('"', '\"').delete("\n"))
329
+ format(%("%<value>s"), value: val.gsub('"', '\"').delete("\n"))
385
330
  else
386
331
  # Otherwise use the basic value if it looks like a standard set of
387
332
  # characters (and allow a few special characters like hyphens, and