stripe 4.10.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (225) 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 +86 -1
  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/api_operations/delete.rb +23 -1
  14. data/lib/stripe/api_operations/list.rb +0 -6
  15. data/lib/stripe/api_operations/nested_resource.rb +14 -7
  16. data/lib/stripe/api_operations/request.rb +3 -7
  17. data/lib/stripe/api_operations/save.rb +1 -3
  18. data/lib/stripe/api_resource.rb +50 -2
  19. data/lib/stripe/connection_manager.rb +131 -0
  20. data/lib/stripe/error_object.rb +94 -0
  21. data/lib/stripe/errors.rb +22 -9
  22. data/lib/stripe/list_object.rb +11 -5
  23. data/lib/stripe/multipart_encoder.rb +131 -0
  24. data/lib/stripe/object_types.rb +94 -0
  25. data/lib/stripe/{account.rb → resources/account.rb} +49 -27
  26. data/lib/stripe/{account_link.rb → resources/account_link.rb} +1 -1
  27. data/lib/stripe/resources/alipay_account.rb +34 -0
  28. data/lib/stripe/{apple_pay_domain.rb → resources/apple_pay_domain.rb} +1 -1
  29. data/lib/stripe/resources/application_fee.rb +13 -0
  30. data/lib/stripe/resources/application_fee_refund.rb +30 -0
  31. data/lib/stripe/{balance.rb → resources/balance.rb} +1 -1
  32. data/lib/stripe/{balance_transaction.rb → resources/balance_transaction.rb} +1 -5
  33. data/lib/stripe/{bank_account.rb → resources/bank_account.rb} +14 -4
  34. data/lib/stripe/{bitcoin_receiver.rb → resources/bitcoin_receiver.rb} +3 -3
  35. data/lib/stripe/{bitcoin_transaction.rb → resources/bitcoin_transaction.rb} +1 -1
  36. data/lib/stripe/resources/capability.rb +33 -0
  37. data/lib/stripe/{card.rb → resources/card.rb} +12 -4
  38. data/lib/stripe/resources/charge.rb +22 -0
  39. data/lib/stripe/{checkout → resources/checkout}/session.rb +2 -2
  40. data/lib/stripe/{country_spec.rb → resources/country_spec.rb} +1 -1
  41. data/lib/stripe/{coupon.rb → resources/coupon.rb} +2 -2
  42. data/lib/stripe/resources/credit_note.rb +22 -0
  43. data/lib/stripe/resources/customer.rb +35 -0
  44. data/lib/stripe/resources/customer_balance_transaction.rb +30 -0
  45. data/lib/stripe/resources/discount.rb +7 -0
  46. data/lib/stripe/{dispute.rb → resources/dispute.rb} +9 -7
  47. data/lib/stripe/{ephemeral_key.rb → resources/ephemeral_key.rb} +5 -2
  48. data/lib/stripe/{event.rb → resources/event.rb} +1 -1
  49. data/lib/stripe/{exchange_rate.rb → resources/exchange_rate.rb} +1 -1
  50. data/lib/stripe/{file.rb → resources/file.rb} +8 -11
  51. data/lib/stripe/{file_link.rb → resources/file_link.rb} +2 -2
  52. data/lib/stripe/resources/invoice.rb +73 -0
  53. data/lib/stripe/{invoice_item.rb → resources/invoice_item.rb} +2 -2
  54. data/lib/stripe/{invoice_line_item.rb → resources/invoice_line_item.rb} +1 -1
  55. data/lib/stripe/resources/issuing/authorization.rb +33 -0
  56. data/lib/stripe/resources/issuing/card.rb +24 -0
  57. data/lib/stripe/{issuing → resources/issuing}/card_details.rb +1 -1
  58. data/lib/stripe/{issuing → resources/issuing}/cardholder.rb +2 -2
  59. data/lib/stripe/{issuing → resources/issuing}/dispute.rb +2 -2
  60. data/lib/stripe/{issuing → resources/issuing}/transaction.rb +2 -2
  61. data/lib/stripe/resources/login_link.rb +14 -0
  62. data/lib/stripe/resources/order.rb +32 -0
  63. data/lib/stripe/{order_return.rb → resources/order_return.rb} +1 -1
  64. data/lib/stripe/resources/payment_intent.rb +42 -0
  65. data/lib/stripe/resources/payment_method.rb +32 -0
  66. data/lib/stripe/resources/payout.rb +22 -0
  67. data/lib/stripe/{person.rb → resources/person.rb} +8 -3
  68. data/lib/stripe/{plan.rb → resources/plan.rb} +1 -1
  69. data/lib/stripe/{product.rb → resources/product.rb} +3 -3
  70. data/lib/stripe/resources/radar/early_fraud_warning.rb +11 -0
  71. data/lib/stripe/{radar → resources/radar}/value_list.rb +2 -2
  72. data/lib/stripe/{radar → resources/radar}/value_list_item.rb +2 -2
  73. data/lib/stripe/{recipient.rb → resources/recipient.rb} +2 -6
  74. data/lib/stripe/{recipient_transfer.rb → resources/recipient_transfer.rb} +1 -1
  75. data/lib/stripe/{refund.rb → resources/refund.rb} +1 -1
  76. data/lib/stripe/{reporting → resources/reporting}/report_run.rb +2 -2
  77. data/lib/stripe/{reporting → resources/reporting}/report_type.rb +2 -2
  78. data/lib/stripe/resources/reversal.rb +29 -0
  79. data/lib/stripe/resources/review.rb +20 -0
  80. data/lib/stripe/resources/setup_intent.rb +32 -0
  81. data/lib/stripe/{sigma → resources/sigma}/scheduled_query_run.rb +2 -2
  82. data/lib/stripe/{sku.rb → resources/sku.rb} +3 -3
  83. data/lib/stripe/{source.rb → resources/source.rb} +17 -15
  84. data/lib/stripe/{source_transaction.rb → resources/source_transaction.rb} +1 -1
  85. data/lib/stripe/resources/subscription.rb +25 -0
  86. data/lib/stripe/{subscription_item.rb → resources/subscription_item.rb} +5 -2
  87. data/lib/stripe/resources/subscription_schedule.rb +32 -0
  88. data/lib/stripe/resources/tax_id.rb +26 -0
  89. data/lib/stripe/resources/tax_rate.rb +11 -0
  90. data/lib/stripe/{terminal → resources/terminal}/connection_token.rb +2 -2
  91. data/lib/stripe/{terminal → resources/terminal}/location.rb +2 -2
  92. data/lib/stripe/{terminal → resources/terminal}/reader.rb +2 -2
  93. data/lib/stripe/{three_d_secure.rb → resources/three_d_secure.rb} +1 -1
  94. data/lib/stripe/{token.rb → resources/token.rb} +1 -1
  95. data/lib/stripe/resources/topup.rb +22 -0
  96. data/lib/stripe/resources/transfer.rb +26 -0
  97. data/lib/stripe/resources/usage_record.rb +7 -0
  98. data/lib/stripe/{usage_record_summary.rb → resources/usage_record_summary.rb} +1 -1
  99. data/lib/stripe/{webhook_endpoint.rb → resources/webhook_endpoint.rb} +2 -2
  100. data/lib/stripe/resources.rb +77 -0
  101. data/lib/stripe/singleton_api_resource.rb +3 -1
  102. data/lib/stripe/stripe_client.rb +337 -218
  103. data/lib/stripe/stripe_object.rb +54 -59
  104. data/lib/stripe/stripe_response.rb +53 -21
  105. data/lib/stripe/util.rb +54 -110
  106. data/lib/stripe/version.rb +1 -1
  107. data/lib/stripe/webhook.rb +5 -3
  108. data/lib/stripe.rb +64 -86
  109. data/stripe.gemspec +14 -5
  110. data/test/stripe/account_link_test.rb +1 -1
  111. data/test/stripe/account_test.rb +186 -30
  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 +105 -18
  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 +1 -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 +19 -1
  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 +230 -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 +16 -6
  181. data/test/stripe/terminal/reader_test.rb +16 -6
  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 +114 -124
  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/payment_method.rb +0 -23
  206. data/lib/stripe/payout.rb +0 -20
  207. data/lib/stripe/reversal.rb +0 -22
  208. data/lib/stripe/review.rb +0 -14
  209. data/lib/stripe/subscription.rb +0 -25
  210. data/lib/stripe/subscription_schedule.rb +0 -32
  211. data/lib/stripe/subscription_schedule_revision.rb +0 -25
  212. data/lib/stripe/topup.rb +0 -16
  213. data/lib/stripe/transfer.rb +0 -23
  214. data/lib/stripe/usage_record.rb +0 -14
  215. data/test/stripe/account_external_accounts_operations_test.rb +0 -69
  216. data/test/stripe/account_login_links_operations_test.rb +0 -21
  217. data/test/stripe/account_persons_operations_test.rb +0 -70
  218. data/test/stripe/application_fee_refunds_operations_test.rb +0 -56
  219. data/test/stripe/customer_sources_operations_test.rb +0 -64
  220. data/test/stripe/file_upload_test.rb +0 -76
  221. data/test/stripe/issuer_fraud_record_test.rb +0 -20
  222. data/test/stripe/subscription_schedule_revision_test.rb +0 -37
  223. data/test/stripe/subscription_schedule_revisions_operations_test.rb +0 -35
  224. data/test/stripe/transfer_reversals_operations_test.rb +0 -57
  225. 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
 
@@ -313,7 +296,7 @@ module Stripe
313
296
  #
314
297
  # Here we swallow that error and issue a warning so at least
315
298
  # the program doesn't crash.
316
- $stderr.puts("WARNING: Unable to remove method `#{method_name}`; " \
299
+ warn("WARNING: Unable to remove method `#{method_name}`; " \
317
300
  "if custom, please consider renaming to a name that doesn't " \
318
301
  "collide with an API property name.")
319
302
  end
@@ -322,7 +305,7 @@ module Stripe
322
305
  end
323
306
  end
324
307
 
325
- def add_accessors(keys, values)
308
+ protected def add_accessors(keys, values)
326
309
  # not available in the #instance_eval below
327
310
  protected_fields = self.class.protected_fields
328
311
 
@@ -359,7 +342,11 @@ module Stripe
359
342
  end
360
343
  end
361
344
 
362
- 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)
363
350
  # TODO: only allow setting in updateable classes.
364
351
  if name.to_s.end_with?("=")
365
352
  attr = name.to_s[0...-1].to_sym
@@ -375,7 +362,9 @@ module Stripe
375
362
  begin
376
363
  mth = method(name)
377
364
  rescue NameError
378
- 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(', ')}"
379
368
  end
380
369
  return mth.call(args[0])
381
370
  elsif @values.key?(name)
@@ -390,11 +379,17 @@ module Stripe
390
379
  # raise right away.
391
380
  raise unless @transient_values.include?(name)
392
381
 
393
- 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(', ')}"
394
388
  end
395
389
  end
390
+ # rubocop:enable Style/MissingRespondToMissing
396
391
 
397
- def respond_to_missing?(symbol, include_private = false)
392
+ protected def respond_to_missing?(symbol, include_private = false)
398
393
  @values && @values.key?(symbol) || super
399
394
  end
400
395
 
@@ -410,7 +405,7 @@ module Stripe
410
405
  # * +:opts:+ Options for StripeObject like an API key.
411
406
  # * +:partial:+ Indicates that the re-initialization should not attempt to
412
407
  # remove accessors.
413
- def initialize_from(values, opts, partial = false)
408
+ protected def initialize_from(values, opts, partial = false)
414
409
  @opts = Util.normalize_opts(opts)
415
410
 
416
411
  # the `#send` is here so that we can keep this method private
@@ -420,8 +415,8 @@ module Stripe
420
415
  added = Set.new(values.keys - @values.keys)
421
416
 
422
417
  # Wipe old state before setting new. This is useful for e.g. updating a
423
- # customer, where there is no persistent card parameter. Mark those values
424
- # 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
425
420
 
426
421
  remove_accessors(removed)
427
422
  add_accessors(added, values)
@@ -441,7 +436,8 @@ module Stripe
441
436
  self
442
437
  end
443
438
 
444
- def serialize_params_value(value, original, unsaved, force, key: nil)
439
+ protected def serialize_params_value(value, original, unsaved, force,
440
+ key: nil)
445
441
  if value.nil?
446
442
  ""
447
443
 
@@ -516,11 +512,9 @@ module Stripe
516
512
  end
517
513
  end
518
514
 
519
- private
520
-
521
515
  # Produces a deep copy of the given object including support for arrays,
522
516
  # hashes, and StripeObjects.
523
- def self.deep_copy(obj)
517
+ private_class_method def self.deep_copy(obj)
524
518
  case obj
525
519
  when Array
526
520
  obj.map { |e| deep_copy(e) }
@@ -540,9 +534,8 @@ module Stripe
540
534
  obj
541
535
  end
542
536
  end
543
- private_class_method :deep_copy
544
537
 
545
- def dirty_value!(value)
538
+ private def dirty_value!(value)
546
539
  case value
547
540
  when Array
548
541
  value.map { |v| dirty_value!(v) }
@@ -553,12 +546,14 @@ module Stripe
553
546
 
554
547
  # Returns a hash of empty values for all the values that are in the given
555
548
  # StripeObject.
556
- def empty_values(obj)
549
+ private def empty_values(obj)
557
550
  values = case obj
558
551
  when Hash then obj
559
552
  when StripeObject then obj.instance_variable_get(:@values)
560
553
  else
561
- 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}"
562
557
  end
563
558
 
564
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
data/lib/stripe/util.rb CHANGED
@@ -24,98 +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
- PaymentMethod::OBJECT_NAME => PaymentMethod,
87
- Payout::OBJECT_NAME => Payout,
88
- Person::OBJECT_NAME => Person,
89
- Plan::OBJECT_NAME => Plan,
90
- Product::OBJECT_NAME => Product,
91
- Radar::ValueList::OBJECT_NAME => Radar::ValueList,
92
- Radar::ValueListItem::OBJECT_NAME => Radar::ValueListItem,
93
- Recipient::OBJECT_NAME => Recipient,
94
- RecipientTransfer::OBJECT_NAME => RecipientTransfer,
95
- Refund::OBJECT_NAME => Refund,
96
- Reporting::ReportRun::OBJECT_NAME => Reporting::ReportRun,
97
- Reporting::ReportType::OBJECT_NAME => Reporting::ReportType,
98
- Reversal::OBJECT_NAME => Reversal,
99
- Review::OBJECT_NAME => Review,
100
- SKU::OBJECT_NAME => SKU,
101
- Sigma::ScheduledQueryRun::OBJECT_NAME => Sigma::ScheduledQueryRun,
102
- Source::OBJECT_NAME => Source,
103
- SourceTransaction::OBJECT_NAME => SourceTransaction,
104
- Subscription::OBJECT_NAME => Subscription,
105
- SubscriptionItem::OBJECT_NAME => SubscriptionItem,
106
- SubscriptionSchedule::OBJECT_NAME => SubscriptionSchedule,
107
- SubscriptionScheduleRevision::OBJECT_NAME => SubscriptionScheduleRevision,
108
- Terminal::ConnectionToken::OBJECT_NAME => Terminal::ConnectionToken,
109
- Terminal::Location::OBJECT_NAME => Terminal::Location,
110
- Terminal::Reader::OBJECT_NAME => Terminal::Reader,
111
- ThreeDSecure::OBJECT_NAME => ThreeDSecure,
112
- Token::OBJECT_NAME => Token,
113
- Topup::OBJECT_NAME => Topup,
114
- Transfer::OBJECT_NAME => Transfer,
115
- UsageRecord::OBJECT_NAME => UsageRecord,
116
- UsageRecordSummary::OBJECT_NAME => UsageRecordSummary,
117
- WebhookEndpoint::OBJECT_NAME => WebhookEndpoint,
118
- }
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
119
48
  end
120
49
 
121
50
  # Converts a hash of fields or an array of hashes into a +StripeObject+ or
@@ -131,12 +60,16 @@ module Stripe
131
60
  # * +opts+ - Options for +StripeObject+ like an API key that will be reused
132
61
  # on subsequent API calls.
133
62
  def self.convert_to_stripe_object(data, opts = {})
63
+ opts = normalize_opts(opts)
64
+
134
65
  case data
135
66
  when Array
136
67
  data.map { |i| convert_to_stripe_object(i, opts) }
137
68
  when Hash
138
- # Try converting to a known object class. If none available, fall back to generic StripeObject
139
- 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)
140
73
  else
141
74
  data
142
75
  end
@@ -145,24 +78,24 @@ module Stripe
145
78
  def self.log_error(message, data = {})
146
79
  if !Stripe.logger.nil? ||
147
80
  !Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_ERROR
148
- log_internal(message, data, color: :cyan,
149
- 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)
150
83
  end
151
84
  end
152
85
 
153
86
  def self.log_info(message, data = {})
154
87
  if !Stripe.logger.nil? ||
155
88
  !Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_INFO
156
- log_internal(message, data, color: :cyan,
157
- 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)
158
91
  end
159
92
  end
160
93
 
161
94
  def self.log_debug(message, data = {})
162
95
  if !Stripe.logger.nil? ||
163
96
  !Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_DEBUG
164
- log_internal(message, data, color: :blue,
165
- 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)
166
99
  end
167
100
  end
168
101
 
@@ -265,11 +198,13 @@ module Stripe
265
198
 
266
199
  def self.check_string_argument!(key)
267
200
  raise TypeError, "argument must be a string" unless key.is_a?(String)
201
+
268
202
  key
269
203
  end
270
204
 
271
205
  def self.check_api_key!(key)
272
206
  raise TypeError, "api_key must be a string" unless key.is_a?(String)
207
+
273
208
  key
274
209
  end
275
210
 
@@ -297,13 +232,13 @@ module Stripe
297
232
 
298
233
  # Constant time string comparison to prevent timing attacks
299
234
  # Code borrowed from ActiveSupport
300
- def self.secure_compare(a, b)
301
- 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
302
237
 
303
- l = a.unpack "C#{a.bytesize}"
238
+ l = str_a.unpack "C#{str_a.bytesize}"
304
239
 
305
240
  res = 0
306
- b.each_byte { |byte| res |= byte ^ l.shift }
241
+ str_b.each_byte { |byte| res |= byte ^ l.shift }
307
242
  res.zero?
308
243
  end
309
244
 
@@ -312,14 +247,14 @@ module Stripe
312
247
  #
313
248
 
314
249
  COLOR_CODES = {
315
- black: 0, light_black: 60,
316
- red: 1, light_red: 61,
317
- green: 2, light_green: 62,
318
- yellow: 3, light_yellow: 63,
319
- 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,
320
255
  magenta: 5, light_magenta: 65,
321
- cyan: 6, light_cyan: 66,
322
- white: 7, light_white: 67,
256
+ cyan: 6, light_cyan: 66,
257
+ white: 7, light_white: 67,
323
258
  default: 9,
324
259
  }.freeze
325
260
  private_constant :COLOR_CODES
@@ -348,23 +283,32 @@ module Stripe
348
283
  end
349
284
  private_class_method :level_name
350
285
 
351
- # TODO: Make these named required arguments when we drop support for Ruby
352
- # 2.0.
353
- 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:)
354
287
  data_str = data.reject { |_k, v| v.nil? }
355
288
  .map do |(k, v)|
356
- 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))
357
292
  end.join(" ")
358
293
 
359
294
  if !logger.nil?
360
295
  # the library's log levels are mapped to the same values as the
361
296
  # standard library's logger
362
297
  logger.log(level,
363
- 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))
364
301
  elsif out.isatty
365
- 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)
366
307
  else
367
- 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)
368
312
  end
369
313
  end
370
314
  private_class_method :log_internal
@@ -382,7 +326,7 @@ module Stripe
382
326
  if %r{[^\w\-/]} =~ val
383
327
  # If the string contains any special characters, escape any double
384
328
  # quotes it has, remove newlines, and wrap the whole thing in quotes.
385
- format(%("%s"), val.gsub('"', '\"').delete("\n"))
329
+ format(%("%<value>s"), value: val.gsub('"', '\"').delete("\n"))
386
330
  else
387
331
  # Otherwise use the basic value if it looks like a standard set of
388
332
  # characters (and allow a few special characters like hyphens, and
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stripe
4
- VERSION = "4.10.0".freeze
4
+ VERSION = "5.0.0"
5
5
  end
@@ -8,7 +8,8 @@ module Stripe
8
8
  #
9
9
  # This may raise JSON::ParserError if the payload is not valid JSON, or
10
10
  # SignatureVerificationError if the signature verification fails.
11
- def self.construct_event(payload, sig_header, secret, tolerance: DEFAULT_TOLERANCE)
11
+ def self.construct_event(payload, sig_header, secret,
12
+ tolerance: DEFAULT_TOLERANCE)
12
13
  Signature.verify_header(payload, sig_header, secret, tolerance: tolerance)
13
14
 
14
15
  # It's a good idea to parse the payload only after verifying it. We use
@@ -21,7 +22,7 @@ module Stripe
21
22
  end
22
23
 
23
24
  module Signature
24
- EXPECTED_SCHEME = "v1".freeze
25
+ EXPECTED_SCHEME = "v1"
25
26
 
26
27
  def self.compute_signature(payload, secret)
27
28
  OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), secret, payload)
@@ -50,7 +51,8 @@ module Stripe
50
51
  # Returns true otherwise
51
52
  def self.verify_header(payload, header, secret, tolerance: nil)
52
53
  begin
53
- timestamp, signatures = get_timestamp_and_signatures(header, EXPECTED_SCHEME)
54
+ timestamp, signatures =
55
+ get_timestamp_and_signatures(header, EXPECTED_SCHEME)
54
56
  rescue StandardError
55
57
  raise SignatureVerificationError.new(
56
58
  "Unable to extract timestamp and signatures from header",