stripe 4.24.0 → 5.36.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +169 -0
  3. data/CODE_OF_CONDUCT.md +77 -0
  4. data/Gemfile +10 -16
  5. data/README.md +111 -44
  6. data/Rakefile +8 -7
  7. data/VERSION +1 -1
  8. data/lib/stripe.rb +39 -130
  9. data/lib/stripe/api_operations/create.rb +1 -1
  10. data/lib/stripe/api_operations/delete.rb +7 -3
  11. data/lib/stripe/api_operations/list.rb +1 -12
  12. data/lib/stripe/api_operations/nested_resource.rb +29 -26
  13. data/lib/stripe/api_operations/request.rb +82 -6
  14. data/lib/stripe/api_operations/save.rb +7 -4
  15. data/lib/stripe/api_resource.rb +12 -3
  16. data/lib/stripe/connection_manager.rb +179 -0
  17. data/lib/stripe/error_object.rb +94 -0
  18. data/lib/stripe/errors.rb +24 -3
  19. data/lib/stripe/instrumentation.rb +82 -0
  20. data/lib/stripe/list_object.rb +34 -5
  21. data/lib/stripe/multipart_encoder.rb +131 -0
  22. data/lib/stripe/oauth.rb +8 -6
  23. data/lib/stripe/object_types.rb +12 -1
  24. data/lib/stripe/resources.rb +12 -1
  25. data/lib/stripe/resources/account.rb +6 -14
  26. data/lib/stripe/resources/account_link.rb +2 -1
  27. data/lib/stripe/resources/alipay_account.rb +1 -1
  28. data/lib/stripe/resources/apple_pay_domain.rb +2 -1
  29. data/lib/stripe/resources/application_fee.rb +2 -12
  30. data/lib/stripe/resources/application_fee_refund.rb +1 -1
  31. data/lib/stripe/resources/balance.rb +2 -1
  32. data/lib/stripe/resources/balance_transaction.rb +2 -1
  33. data/lib/stripe/resources/bank_account.rb +3 -2
  34. data/lib/stripe/resources/billing_portal/configuration.rb +14 -0
  35. data/lib/stripe/resources/billing_portal/session.rb +12 -0
  36. data/lib/stripe/resources/bitcoin_receiver.rb +2 -1
  37. data/lib/stripe/resources/bitcoin_transaction.rb +1 -1
  38. data/lib/stripe/resources/capability.rb +1 -1
  39. data/lib/stripe/resources/card.rb +2 -1
  40. data/lib/stripe/resources/charge.rb +8 -69
  41. data/lib/stripe/resources/checkout/session.rb +6 -1
  42. data/lib/stripe/resources/country_spec.rb +2 -1
  43. data/lib/stripe/resources/coupon.rb +2 -1
  44. data/lib/stripe/resources/credit_note.rb +12 -1
  45. data/lib/stripe/resources/credit_note_line_item.rb +7 -0
  46. data/lib/stripe/resources/customer.rb +9 -63
  47. data/lib/stripe/resources/customer_balance_transaction.rb +1 -1
  48. data/lib/stripe/resources/discount.rb +1 -1
  49. data/lib/stripe/resources/dispute.rb +2 -7
  50. data/lib/stripe/resources/ephemeral_key.rb +2 -1
  51. data/lib/stripe/resources/event.rb +2 -1
  52. data/lib/stripe/resources/exchange_rate.rb +2 -1
  53. data/lib/stripe/resources/file.rb +6 -14
  54. data/lib/stripe/resources/file_link.rb +2 -1
  55. data/lib/stripe/resources/identity/verification_report.rb +12 -0
  56. data/lib/stripe/resources/identity/verification_session.rb +35 -0
  57. data/lib/stripe/resources/invoice.rb +8 -2
  58. data/lib/stripe/resources/invoice_item.rb +2 -1
  59. data/lib/stripe/resources/invoice_line_item.rb +1 -1
  60. data/lib/stripe/resources/issuing/authorization.rb +2 -1
  61. data/lib/stripe/resources/issuing/card.rb +2 -1
  62. data/lib/stripe/resources/issuing/card_details.rb +1 -1
  63. data/lib/stripe/resources/issuing/cardholder.rb +2 -1
  64. data/lib/stripe/resources/issuing/dispute.rb +13 -1
  65. data/lib/stripe/resources/issuing/transaction.rb +2 -1
  66. data/lib/stripe/resources/line_item.rb +7 -0
  67. data/lib/stripe/resources/login_link.rb +1 -1
  68. data/lib/stripe/resources/mandate.rb +8 -0
  69. data/lib/stripe/resources/order.rb +2 -9
  70. data/lib/stripe/resources/order_return.rb +2 -1
  71. data/lib/stripe/resources/payment_intent.rb +2 -1
  72. data/lib/stripe/resources/payment_method.rb +2 -1
  73. data/lib/stripe/resources/payout.rb +10 -5
  74. data/lib/stripe/resources/person.rb +1 -1
  75. data/lib/stripe/resources/plan.rb +2 -1
  76. data/lib/stripe/resources/price.rb +12 -0
  77. data/lib/stripe/resources/product.rb +2 -1
  78. data/lib/stripe/resources/promotion_code.rb +12 -0
  79. data/lib/stripe/resources/quote.rb +95 -0
  80. data/lib/stripe/resources/radar/early_fraud_warning.rb +2 -1
  81. data/lib/stripe/resources/radar/value_list.rb +2 -1
  82. data/lib/stripe/resources/radar/value_list_item.rb +2 -1
  83. data/lib/stripe/resources/recipient.rb +2 -5
  84. data/lib/stripe/resources/recipient_transfer.rb +1 -1
  85. data/lib/stripe/resources/refund.rb +2 -1
  86. data/lib/stripe/resources/reporting/report_run.rb +2 -1
  87. data/lib/stripe/resources/reporting/report_type.rb +2 -1
  88. data/lib/stripe/resources/reversal.rb +1 -1
  89. data/lib/stripe/resources/review.rb +2 -1
  90. data/lib/stripe/resources/setup_attempt.rb +10 -0
  91. data/lib/stripe/resources/setup_intent.rb +2 -1
  92. data/lib/stripe/resources/sigma/scheduled_query_run.rb +2 -1
  93. data/lib/stripe/resources/sku.rb +2 -1
  94. data/lib/stripe/resources/source.rb +11 -10
  95. data/lib/stripe/resources/source_transaction.rb +1 -1
  96. data/lib/stripe/resources/subscription.rb +10 -9
  97. data/lib/stripe/resources/subscription_item.rb +8 -2
  98. data/lib/stripe/resources/subscription_schedule.rb +2 -1
  99. data/lib/stripe/resources/tax_code.rb +10 -0
  100. data/lib/stripe/resources/tax_id.rb +1 -1
  101. data/lib/stripe/resources/tax_rate.rb +2 -1
  102. data/lib/stripe/resources/terminal/connection_token.rb +2 -1
  103. data/lib/stripe/resources/terminal/location.rb +2 -1
  104. data/lib/stripe/resources/terminal/reader.rb +2 -1
  105. data/lib/stripe/resources/three_d_secure.rb +2 -1
  106. data/lib/stripe/resources/token.rb +2 -1
  107. data/lib/stripe/resources/topup.rb +2 -1
  108. data/lib/stripe/resources/transfer.rb +2 -6
  109. data/lib/stripe/resources/usage_record.rb +1 -17
  110. data/lib/stripe/resources/usage_record_summary.rb +1 -1
  111. data/lib/stripe/resources/webhook_endpoint.rb +2 -1
  112. data/lib/stripe/stripe_client.rb +608 -242
  113. data/lib/stripe/stripe_configuration.rb +194 -0
  114. data/lib/stripe/stripe_object.rb +30 -25
  115. data/lib/stripe/stripe_response.rb +87 -27
  116. data/lib/stripe/util.rb +37 -18
  117. data/lib/stripe/version.rb +1 -1
  118. data/lib/stripe/webhook.rb +39 -8
  119. data/stripe.gemspec +15 -11
  120. metadata +24 -216
  121. data/.editorconfig +0 -10
  122. data/.gitattributes +0 -4
  123. data/.github/ISSUE_TEMPLATE.md +0 -5
  124. data/.gitignore +0 -8
  125. data/.rubocop.yml +0 -43
  126. data/.rubocop_todo.yml +0 -38
  127. data/.travis.yml +0 -43
  128. data/.vscode/extensions.json +0 -7
  129. data/.vscode/settings.json +0 -8
  130. data/lib/stripe/resources/issuer_fraud_record.rb +0 -9
  131. data/test/api_stub_helpers.rb +0 -1
  132. data/test/openapi/README.md +0 -9
  133. data/test/stripe/account_link_test.rb +0 -18
  134. data/test/stripe/account_test.rb +0 -428
  135. data/test/stripe/alipay_account_test.rb +0 -37
  136. data/test/stripe/api_operations_test.rb +0 -80
  137. data/test/stripe/api_resource_test.rb +0 -621
  138. data/test/stripe/apple_pay_domain_test.rb +0 -46
  139. data/test/stripe/application_fee_refund_test.rb +0 -37
  140. data/test/stripe/application_fee_test.rb +0 -58
  141. data/test/stripe/balance_test.rb +0 -13
  142. data/test/stripe/balance_transaction_test.rb +0 -20
  143. data/test/stripe/bank_account_test.rb +0 -36
  144. data/test/stripe/capability_test.rb +0 -45
  145. data/test/stripe/charge_test.rb +0 -80
  146. data/test/stripe/checkout/session_test.rb +0 -41
  147. data/test/stripe/country_spec_test.rb +0 -20
  148. data/test/stripe/coupon_test.rb +0 -61
  149. data/test/stripe/credit_note_test.rb +0 -61
  150. data/test/stripe/customer_balance_transaction_test.rb +0 -37
  151. data/test/stripe/customer_card_test.rb +0 -42
  152. data/test/stripe/customer_test.rb +0 -269
  153. data/test/stripe/dispute_test.rb +0 -51
  154. data/test/stripe/ephemeral_key_test.rb +0 -93
  155. data/test/stripe/errors_test.rb +0 -20
  156. data/test/stripe/exchange_rate_test.rb +0 -20
  157. data/test/stripe/file_link_test.rb +0 -41
  158. data/test/stripe/file_test.rb +0 -97
  159. data/test/stripe/file_upload_test.rb +0 -79
  160. data/test/stripe/invoice_item_test.rb +0 -66
  161. data/test/stripe/invoice_line_item_test.rb +0 -8
  162. data/test/stripe/invoice_test.rb +0 -213
  163. data/test/stripe/issuer_fraud_record_test.rb +0 -20
  164. data/test/stripe/issuing/authorization_test.rb +0 -72
  165. data/test/stripe/issuing/card_test.rb +0 -62
  166. data/test/stripe/issuing/cardholder_test.rb +0 -53
  167. data/test/stripe/issuing/dispute_test.rb +0 -45
  168. data/test/stripe/issuing/transaction_test.rb +0 -48
  169. data/test/stripe/list_object_test.rb +0 -156
  170. data/test/stripe/login_link_test.rb +0 -37
  171. data/test/stripe/oauth_test.rb +0 -88
  172. data/test/stripe/order_return_test.rb +0 -21
  173. data/test/stripe/order_test.rb +0 -82
  174. data/test/stripe/payment_intent_test.rb +0 -107
  175. data/test/stripe/payment_method_test.rb +0 -84
  176. data/test/stripe/payout_test.rb +0 -57
  177. data/test/stripe/person_test.rb +0 -46
  178. data/test/stripe/plan_test.rb +0 -98
  179. data/test/stripe/product_test.rb +0 -59
  180. data/test/stripe/radar/early_fraud_warning_test.rb +0 -22
  181. data/test/stripe/radar/value_list_item_test.rb +0 -48
  182. data/test/stripe/radar/value_list_test.rb +0 -61
  183. data/test/stripe/recipient_test.rb +0 -62
  184. data/test/stripe/refund_test.rb +0 -39
  185. data/test/stripe/reporting/report_run_test.rb +0 -33
  186. data/test/stripe/reporting/report_type_test.rb +0 -22
  187. data/test/stripe/reversal_test.rb +0 -43
  188. data/test/stripe/review_test.rb +0 -27
  189. data/test/stripe/setup_intent_test.rb +0 -84
  190. data/test/stripe/sigma/scheduled_query_run_test.rb +0 -22
  191. data/test/stripe/sku_test.rb +0 -60
  192. data/test/stripe/source_test.rb +0 -99
  193. data/test/stripe/source_transaction_test.rb +0 -19
  194. data/test/stripe/stripe_client_test.rb +0 -842
  195. data/test/stripe/stripe_object_test.rb +0 -525
  196. data/test/stripe/stripe_response_test.rb +0 -49
  197. data/test/stripe/subscription_item_test.rb +0 -75
  198. data/test/stripe/subscription_schedule_test.rb +0 -82
  199. data/test/stripe/subscription_test.rb +0 -80
  200. data/test/stripe/tax_id_test.rb +0 -31
  201. data/test/stripe/tax_rate_test.rb +0 -43
  202. data/test/stripe/terminal/connection_token_test.rb +0 -16
  203. data/test/stripe/terminal/location_test.rb +0 -68
  204. data/test/stripe/terminal/reader_test.rb +0 -62
  205. data/test/stripe/three_d_secure_test.rb +0 -23
  206. data/test/stripe/topup_test.rb +0 -62
  207. data/test/stripe/transfer_test.rb +0 -88
  208. data/test/stripe/usage_record_summary_test.rb +0 -19
  209. data/test/stripe/usage_record_test.rb +0 -28
  210. data/test/stripe/util_test.rb +0 -402
  211. data/test/stripe/webhook_endpoint_test.rb +0 -59
  212. data/test/stripe/webhook_test.rb +0 -96
  213. data/test/stripe_mock.rb +0 -77
  214. data/test/stripe_test.rb +0 -63
  215. data/test/test_data.rb +0 -61
  216. data/test/test_helper.rb +0 -71
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stripe
4
+ # Configurable options:
5
+ #
6
+ # =ca_bundle_path=
7
+ # The location of a file containing a bundle of CA certificates. By default
8
+ # the library will use an included bundle that can successfully validate
9
+ # Stripe certificates.
10
+ #
11
+ # =log_level=
12
+ # When set prompts the library to log some extra information to $stdout and
13
+ # $stderr about what it's doing. For example, it'll produce information about
14
+ # requests, responses, and errors that are received. Valid log levels are
15
+ # `debug` and `info`, with `debug` being a little more verbose in places.
16
+ #
17
+ # Use of this configuration is only useful when `.logger` is _not_ set. When
18
+ # it is, the decision what levels to print is entirely deferred to the logger.
19
+ #
20
+ # =logger=
21
+ # The logger should support the same interface as the `Logger` class that's
22
+ # part of Ruby's standard library (hint, anything in `Rails.logger` will
23
+ # likely be suitable).
24
+ #
25
+ # If `.logger` is set, the value of `.log_level` is ignored. The decision on
26
+ # what levels to print is entirely deferred to the logger.
27
+ class StripeConfiguration
28
+ attr_accessor :api_key
29
+ attr_accessor :api_version
30
+ attr_accessor :client_id
31
+ attr_accessor :enable_telemetry
32
+ attr_accessor :logger
33
+ attr_accessor :stripe_account
34
+
35
+ attr_reader :api_base
36
+ attr_reader :uploads_base
37
+ attr_reader :connect_base
38
+ attr_reader :ca_bundle_path
39
+ attr_reader :log_level
40
+ attr_reader :initial_network_retry_delay
41
+ attr_reader :max_network_retries
42
+ attr_reader :max_network_retry_delay
43
+ attr_reader :open_timeout
44
+ attr_reader :read_timeout
45
+ attr_reader :write_timeout
46
+ attr_reader :proxy
47
+ attr_reader :verify_ssl_certs
48
+
49
+ def self.setup
50
+ new.tap do |instance|
51
+ yield(instance) if block_given?
52
+ end
53
+ end
54
+
55
+ # Create a new config based off an existing one. This is useful when the
56
+ # caller wants to override the global configuration
57
+ def reverse_duplicate_merge(hash)
58
+ dup.tap do |instance|
59
+ hash.each do |option, value|
60
+ instance.public_send("#{option}=", value)
61
+ end
62
+ end
63
+ end
64
+
65
+ def initialize
66
+ @ca_bundle_path = Stripe::DEFAULT_CA_BUNDLE_PATH
67
+ @enable_telemetry = true
68
+ @verify_ssl_certs = true
69
+
70
+ @max_network_retries = 0
71
+ @initial_network_retry_delay = 0.5
72
+ @max_network_retry_delay = 2
73
+
74
+ @open_timeout = 30
75
+ @read_timeout = 80
76
+ @write_timeout = 30
77
+
78
+ @api_base = "https://api.stripe.com"
79
+ @connect_base = "https://connect.stripe.com"
80
+ @uploads_base = "https://files.stripe.com"
81
+ end
82
+
83
+ def log_level=(val)
84
+ # Backwards compatibility for values that we briefly allowed
85
+ if val == "debug"
86
+ val = Stripe::LEVEL_DEBUG
87
+ elsif val == "info"
88
+ val = Stripe::LEVEL_INFO
89
+ end
90
+
91
+ levels = [Stripe::LEVEL_INFO, Stripe::LEVEL_DEBUG, Stripe::LEVEL_ERROR]
92
+
93
+ if !val.nil? && !levels.include?(val)
94
+ raise ArgumentError,
95
+ "log_level should only be set to `nil`, `debug` or `info`"
96
+ end
97
+ @log_level = val
98
+ end
99
+
100
+ def max_network_retries=(val)
101
+ @max_network_retries = val.to_i
102
+ end
103
+
104
+ def max_network_retry_delay=(val)
105
+ @max_network_retry_delay = val.to_i
106
+ end
107
+
108
+ def initial_network_retry_delay=(val)
109
+ @initial_network_retry_delay = val.to_i
110
+ end
111
+
112
+ def open_timeout=(open_timeout)
113
+ @open_timeout = open_timeout
114
+ StripeClient.clear_all_connection_managers(config: self)
115
+ end
116
+
117
+ def read_timeout=(read_timeout)
118
+ @read_timeout = read_timeout
119
+ StripeClient.clear_all_connection_managers(config: self)
120
+ end
121
+
122
+ def write_timeout=(write_timeout)
123
+ unless Net::HTTP.instance_methods.include?(:write_timeout=)
124
+ raise NotImplementedError
125
+ end
126
+
127
+ @write_timeout = write_timeout
128
+ StripeClient.clear_all_connection_managers(config: self)
129
+ end
130
+
131
+ def proxy=(proxy)
132
+ @proxy = proxy
133
+ StripeClient.clear_all_connection_managers(config: self)
134
+ end
135
+
136
+ def verify_ssl_certs=(verify_ssl_certs)
137
+ @verify_ssl_certs = verify_ssl_certs
138
+ StripeClient.clear_all_connection_managers(config: self)
139
+ end
140
+
141
+ def uploads_base=(uploads_base)
142
+ @uploads_base = uploads_base
143
+ StripeClient.clear_all_connection_managers(config: self)
144
+ end
145
+
146
+ def connect_base=(connect_base)
147
+ @connect_base = connect_base
148
+ StripeClient.clear_all_connection_managers(config: self)
149
+ end
150
+
151
+ def api_base=(api_base)
152
+ @api_base = api_base
153
+ StripeClient.clear_all_connection_managers(config: self)
154
+ end
155
+
156
+ def ca_bundle_path=(path)
157
+ @ca_bundle_path = path
158
+
159
+ # empty this field so a new store is initialized
160
+ @ca_store = nil
161
+
162
+ StripeClient.clear_all_connection_managers(config: self)
163
+ end
164
+
165
+ # A certificate store initialized from the the bundle in #ca_bundle_path and
166
+ # which is used to validate TLS on every request.
167
+ #
168
+ # This was added to the give the gem "pseudo thread safety" in that it seems
169
+ # when initiating many parallel requests marshaling the certificate store is
170
+ # the most likely point of failure (see issue #382). Any program attempting
171
+ # to leverage this pseudo safety should make a call to this method (i.e.
172
+ # `Stripe.ca_store`) in their initialization code because it marshals lazily
173
+ # and is itself not thread safe.
174
+ def ca_store
175
+ @ca_store ||= begin
176
+ store = OpenSSL::X509::Store.new
177
+ store.add_file(ca_bundle_path)
178
+ store
179
+ end
180
+ end
181
+
182
+ def enable_telemetry?
183
+ enable_telemetry
184
+ end
185
+
186
+ # Generates a deterministic key to identify configuration objects with
187
+ # identical configuration values.
188
+ def key
189
+ instance_variables
190
+ .collect { |variable| instance_variable_get(variable) }
191
+ .join
192
+ end
193
+ end
194
+ end
@@ -127,18 +127,6 @@ module Stripe
127
127
  JSON.pretty_generate(@values)
128
128
  end
129
129
 
130
- # Re-initializes the object based on a hash of values (usually one that's
131
- # come back from an API call). Adds or removes value accessors as necessary
132
- # and updates the state of internal data.
133
- #
134
- # Please don't use this method. If you're trying to do mass assignment, try
135
- # #initialize_from instead.
136
- def refresh_from(values, opts, partial = false)
137
- initialize_from(values, opts, partial)
138
- end
139
- extend Gem::Deprecate
140
- deprecate :refresh_from, "#update_attributes", 2016, 1
141
-
142
130
  # Mass assigns attributes on the model.
143
131
  #
144
132
  # This is a version of +update_attributes+ that takes some extra options
@@ -147,7 +135,8 @@ module Stripe
147
135
  # ==== Attributes
148
136
  #
149
137
  # * +values+ - Hash of values to use to update the current attributes of
150
- # the object.
138
+ # the object. If you are on ruby 2.7 or higher make sure to wrap in curly
139
+ # braces to be ruby 3 compatible.
151
140
  # * +opts+ - Options for +StripeObject+ like an API key that will be reused
152
141
  # on subsequent API calls.
153
142
  #
@@ -192,7 +181,9 @@ module Stripe
192
181
 
193
182
  def to_hash
194
183
  maybe_to_hash = lambda do |value|
195
- value && value.respond_to?(:to_hash) ? value.to_hash : value
184
+ return nil if value.nil?
185
+
186
+ value.respond_to?(:to_hash) ? value.to_hash : value
196
187
  end
197
188
 
198
189
  @values.each_with_object({}) do |(key, value), acc|
@@ -256,6 +247,7 @@ module Stripe
256
247
  #
257
248
  unsaved = @unsaved_values.include?(k)
258
249
  next unless options[:force] || unsaved || v.is_a?(StripeObject)
250
+
259
251
  update_hash[k.to_sym] = serialize_params_value(
260
252
  @values[k], @original_values[k], unsaved, options[:force], key: k
261
253
  )
@@ -268,16 +260,6 @@ module Stripe
268
260
  update_hash
269
261
  end
270
262
 
271
- class << self
272
- # This class method has been deprecated in favor of the instance method
273
- # of the same name.
274
- def serialize_params(obj, options = {})
275
- obj.serialize_params(options)
276
- end
277
- extend Gem::Deprecate
278
- deprecate :serialize_params, "#serialize_params", 2016, 9
279
- end
280
-
281
263
  # A protected field is one that doesn't get an accessor assigned to it
282
264
  # (i.e. `obj.public = ...`) and one which is not allowed to be updated via
283
265
  # the class level `Model.update(id, { ... })`.
@@ -285,6 +267,27 @@ module Stripe
285
267
  []
286
268
  end
287
269
 
270
+ # When designing APIs, we now make a conscious effort server-side to avoid
271
+ # naming fields after important built-ins in various languages (e.g. class,
272
+ # method, etc.).
273
+ #
274
+ # However, a long time ago we made the mistake (either consciously or by
275
+ # accident) of initializing our `metadata` fields as instances of
276
+ # `StripeObject`, and metadata can have a wide range of different keys
277
+ # defined in it. This is somewhat a convenient in that it allows users to
278
+ # access data like `obj.metadata.my_field`, but is almost certainly not
279
+ # worth the cost.
280
+ #
281
+ # Naming metadata fields bad things like `class` causes `initialize_from`
282
+ # to produce strange results, so we ban known offenders here.
283
+ #
284
+ # In a future major version we should consider leaving `metadata` as a hash
285
+ # and forcing people to access it with `obj.metadata[:my_field]` because
286
+ # the potential for trouble is just too high. For now, reserve names.
287
+ RESERVED_FIELD_NAMES = [
288
+ :class,
289
+ ].freeze
290
+
288
291
  protected def metaclass
289
292
  class << self; self; end
290
293
  end
@@ -295,6 +298,7 @@ module Stripe
295
298
 
296
299
  metaclass.instance_eval do
297
300
  keys.each do |k|
301
+ next if RESERVED_FIELD_NAMES.include?(k)
298
302
  next if protected_fields.include?(k)
299
303
  next if @@permanent_attributes.include?(k)
300
304
 
@@ -330,6 +334,7 @@ module Stripe
330
334
 
331
335
  metaclass.instance_eval do
332
336
  keys.each do |k|
337
+ next if RESERVED_FIELD_NAMES.include?(k)
333
338
  next if protected_fields.include?(k)
334
339
  next if @@permanent_attributes.include?(k)
335
340
 
@@ -393,7 +398,7 @@ module Stripe
393
398
  begin
394
399
  super
395
400
  rescue NoMethodError => e
396
- # If we notice the accessed name if our set of transient values we can
401
+ # If we notice the accessed name of our set of transient values we can
397
402
  # give the user a slightly more helpful error message. If not, just
398
403
  # raise right away.
399
404
  raise unless @transient_values.include?(name)
@@ -1,16 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stripe
4
- # StripeResponse encapsulates some vitals of a response that came back from
5
- # the Stripe API.
6
- class StripeResponse
7
- # The data contained by the HTTP body of the response deserialized from
8
- # JSON.
9
- attr_accessor :data
4
+ # Headers provides an access wrapper to an API response's header data. It
5
+ # mainly exists so that we don't need to expose the entire
6
+ # `Net::HTTPResponse` object while still getting some of its benefits like
7
+ # case-insensitive access to header names and flattening of header values.
8
+ class StripeResponseHeaders
9
+ # Initializes a Headers object from a Net::HTTP::HTTPResponse object.
10
+ def self.from_net_http(resp)
11
+ new(resp.to_hash)
12
+ end
10
13
 
11
- # The raw HTTP body of the response.
12
- attr_accessor :http_body
14
+ # `hash` is expected to be a hash mapping header names to arrays of
15
+ # header values. This is the default format generated by calling
16
+ # `#to_hash` on a `Net::HTTPResponse` object because headers can be
17
+ # repeated multiple times. Using `#[]` will collapse values down to just
18
+ # the first.
19
+ def initialize(hash)
20
+ if !hash.is_a?(Hash) ||
21
+ !hash.keys.all? { |n| n.is_a?(String) } ||
22
+ !hash.values.all? { |a| a.is_a?(Array) } ||
23
+ !hash.values.all? { |a| a.all? { |v| v.is_a?(String) } }
24
+ raise ArgumentError,
25
+ "expect hash to be a map of string header names to arrays of " \
26
+ "header values"
27
+ end
13
28
 
29
+ @hash = {}
30
+
31
+ # This shouldn't be strictly necessary because `Net::HTTPResponse` will
32
+ # produce a hash with all headers downcased, but do it anyway just in
33
+ # case an object of this class was constructed manually.
34
+ #
35
+ # Also has the effect of duplicating the hash, which is desirable for a
36
+ # little extra object safety.
37
+ hash.each do |k, v|
38
+ @hash[k.downcase] = v
39
+ end
40
+ end
41
+
42
+ def [](name)
43
+ values = @hash[name.downcase]
44
+ if values && values.count > 1
45
+ warn("Duplicate header values for `#{name}`; returning only first")
46
+ end
47
+ values ? values.first : nil
48
+ end
49
+ end
50
+
51
+ module StripeResponseBase
14
52
  # A Hash of the HTTP headers of the response.
15
53
  attr_accessor :http_headers
16
54
 
@@ -20,30 +58,52 @@ module Stripe
20
58
  # The Stripe request ID of the response.
21
59
  attr_accessor :request_id
22
60
 
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
61
+ def self.populate_for_net_http(resp, http_resp)
62
+ resp.http_headers = StripeResponseHeaders.from_net_http(http_resp)
63
+ resp.http_status = http_resp.code.to_i
64
+ resp.request_id = http_resp["request-id"]
35
65
  end
66
+ end
36
67
 
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)
68
+ # StripeResponse encapsulates some vitals of a response that came back from
69
+ # the Stripe API.
70
+ class StripeResponse
71
+ include StripeResponseBase
72
+ # The data contained by the HTTP body of the response deserialized from
73
+ # JSON.
74
+ attr_accessor :data
75
+
76
+ # The raw HTTP body of the response.
77
+ attr_accessor :http_body
78
+
79
+ # Initializes a StripeResponse object from a Net::HTTP::HTTPResponse
80
+ # object.
81
+ def self.from_net_http(http_resp)
41
82
  resp = StripeResponse.new
42
83
  resp.data = JSON.parse(http_resp.body, symbolize_names: true)
43
84
  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"]
85
+ StripeResponseBase.populate_for_net_http(resp, http_resp)
86
+ resp
87
+ end
88
+ end
89
+
90
+ # We have to alias StripeResponseHeaders to StripeResponse::Headers, as this
91
+ # class used to be embedded within StripeResponse and we want to be backwards
92
+ # compatible.
93
+ StripeResponse::Headers = StripeResponseHeaders
94
+
95
+ # StripeHeadersOnlyResponse includes only header-related vitals of the
96
+ # response. This is used for streaming requests where the response was read
97
+ # directly in a block and we explicitly don't want to store the body of the
98
+ # response in memory.
99
+ class StripeHeadersOnlyResponse
100
+ include StripeResponseBase
101
+
102
+ # Initializes a StripeHeadersOnlyResponse object from a
103
+ # Net::HTTP::HTTPResponse object.
104
+ def self.from_net_http(http_resp)
105
+ resp = StripeHeadersOnlyResponse.new
106
+ StripeResponseBase.populate_for_net_http(resp, http_resp)
47
107
  resp
48
108
  end
49
109
  end
data/lib/stripe/util.rb CHANGED
@@ -76,24 +76,30 @@ module Stripe
76
76
  end
77
77
 
78
78
  def self.log_error(message, data = {})
79
- if !Stripe.logger.nil? ||
80
- !Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_ERROR
79
+ config = data.delete(:config) || Stripe.config
80
+ logger = config.logger || Stripe.logger
81
+ if !logger.nil? ||
82
+ !config.log_level.nil? && config.log_level <= Stripe::LEVEL_ERROR
81
83
  log_internal(message, data, color: :cyan, level: Stripe::LEVEL_ERROR,
82
84
  logger: Stripe.logger, out: $stderr)
83
85
  end
84
86
  end
85
87
 
86
88
  def self.log_info(message, data = {})
87
- if !Stripe.logger.nil? ||
88
- !Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_INFO
89
+ config = data.delete(:config) || Stripe.config
90
+ logger = config.logger || Stripe.logger
91
+ if !logger.nil? ||
92
+ !config.log_level.nil? && config.log_level <= Stripe::LEVEL_INFO
89
93
  log_internal(message, data, color: :cyan, level: Stripe::LEVEL_INFO,
90
94
  logger: Stripe.logger, out: $stdout)
91
95
  end
92
96
  end
93
97
 
94
98
  def self.log_debug(message, data = {})
95
- if !Stripe.logger.nil? ||
96
- !Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_DEBUG
99
+ config = data.delete(:config) || Stripe.config
100
+ logger = config.logger || Stripe.logger
101
+ if !logger.nil? ||
102
+ !config.log_level.nil? && config.log_level <= Stripe::LEVEL_DEBUG
97
103
  log_internal(message, data, color: :blue, level: Stripe::LEVEL_DEBUG,
98
104
  logger: Stripe.logger, out: $stdout)
99
105
  end
@@ -172,6 +178,18 @@ module Stripe
172
178
  result
173
179
  end
174
180
 
181
+ # `Time.now` can be unstable in cases like an administrator manually
182
+ # updating its value or a reconcilation via NTP. For this reason, prefer
183
+ # the use of the system's monotonic clock especially where comparing times
184
+ # to calculate an elapsed duration.
185
+ #
186
+ # Shortcut for getting monotonic time, mostly for purposes of line length
187
+ # and test stubbing. Returns time in seconds since the event used for
188
+ # monotonic reference purposes by the platform (e.g. system boot time).
189
+ def self.monotonic_time
190
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
191
+ end
192
+
175
193
  def self.normalize_id(id)
176
194
  if id.is_a?(Hash) # overloaded id
177
195
  params_hash = id.dup
@@ -190,7 +208,9 @@ module Stripe
190
208
  { api_key: opts }
191
209
  when Hash
192
210
  check_api_key!(opts.fetch(:api_key)) if opts.key?(:api_key)
193
- opts.clone
211
+ # Explicitly use dup here instead of clone to avoid preserving freeze
212
+ # state on input params.
213
+ opts.dup
194
214
  else
195
215
  raise TypeError, "normalize_opts expects a string or a hash"
196
216
  end
@@ -198,11 +218,13 @@ module Stripe
198
218
 
199
219
  def self.check_string_argument!(key)
200
220
  raise TypeError, "argument must be a string" unless key.is_a?(String)
221
+
201
222
  key
202
223
  end
203
224
 
204
225
  def self.check_api_key!(key)
205
226
  raise TypeError, "api_key must be a string" unless key.is_a?(String)
227
+
206
228
  key
207
229
  end
208
230
 
@@ -245,14 +267,14 @@ module Stripe
245
267
  #
246
268
 
247
269
  COLOR_CODES = {
248
- black: 0, light_black: 60,
249
- red: 1, light_red: 61,
250
- green: 2, light_green: 62,
251
- yellow: 3, light_yellow: 63,
252
- blue: 4, light_blue: 64,
270
+ black: 0, light_black: 60,
271
+ red: 1, light_red: 61,
272
+ green: 2, light_green: 62,
273
+ yellow: 3, light_yellow: 63,
274
+ blue: 4, light_blue: 64,
253
275
  magenta: 5, light_magenta: 65,
254
- cyan: 6, light_cyan: 66,
255
- white: 7, light_white: 67,
276
+ cyan: 6, light_cyan: 66,
277
+ white: 7, light_white: 67,
256
278
  default: 9,
257
279
  }.freeze
258
280
  private_constant :COLOR_CODES
@@ -281,10 +303,7 @@ module Stripe
281
303
  end
282
304
  private_class_method :level_name
283
305
 
284
- # TODO: Make these named required arguments when we drop support for Ruby
285
- # 2.0.
286
- def self.log_internal(message, data = {}, color: nil, level: nil,
287
- logger: nil, out: nil)
306
+ def self.log_internal(message, data = {}, color:, level:, logger:, out:)
288
307
  data_str = data.reject { |_k, v| v.nil? }
289
308
  .map do |(k, v)|
290
309
  format("%<key>s=%<value>s",