Module: Stripe::Util

Defined in:
lib/stripe/util.rb

Class Method Summary collapse

Class Method Details

.check_api_key!(key) ⇒ Object

Raises:

  • (TypeError)


318
319
320
321
322
# File 'lib/stripe/util.rb', line 318

def self.check_api_key!(key)
  raise TypeError, "api_key must be a string" unless key.is_a?(String)

  key
end

.check_string_argument!(key) ⇒ Object

Raises:

  • (TypeError)


312
313
314
315
316
# File 'lib/stripe/util.rb', line 312

def self.check_string_argument!(key)
  raise TypeError, "argument must be a string" unless key.is_a?(String)

  key
end

.convert_to_stripe_object(data, opts = {}, api_mode: :v1, requestor: nil) ⇒ Object

Converts a hash of fields or an array of hashes into a StripeObject or array of StripeObjects. These new objects will be created as a concrete type as dictated by their ‘object` field (e.g. an `object` value of `charge` would create an instance of Charge), but if `object` is not present or of an unknown type, the newly created instance will fall back to being a StripeObject.

Attributes

  • data - Hash of fields and values to be converted into a StripeObject.

  • params - Params for StripeObject like filters used in search that will be reused on subsequent API calls.

  • opts - Options for StripeObject like an API key that will be reused on subsequent API calls.



99
100
101
# File 'lib/stripe/util.rb', line 99

def self.convert_to_stripe_object(data, opts = {}, api_mode: :v1, requestor: nil)
  convert_to_stripe_object_with_params(data, {}, opts, api_mode: api_mode, requestor: requestor)
end

.convert_to_stripe_object_with_params(data, params, opts = {}, last_response = nil, api_mode: :v1, requestor: nil) ⇒ Object

Converts a hash of fields or an array of hashes into a StripeObject or array of StripeObjects. These new objects will be created as a concrete type as dictated by their ‘object` field (e.g. an `object` value of `charge` would create an instance of Charge), but if `object` is not present or of an unknown type, the newly created instance will fall back to being a StripeObject.

Attributes

  • data - Hash of fields and values to be converted into a StripeObject.

  • opts - Options for StripeObject like an API key that will be reused on subsequent API calls.

  • last_response - The raw response associated with the object.

  • api_mode - The API mode to use when converting the object, either :v1 or :v2.

  • requestor - The requestor to use when constructing the object.



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/stripe/util.rb', line 118

def self.convert_to_stripe_object_with_params(
  data,
  params,
  opts = {},
  last_response = nil,
  api_mode: :v1,
  requestor: nil
)
  opts = normalize_opts(opts)

  case data
  when Array
    data.map { |i| convert_to_stripe_object(i, opts, api_mode: api_mode) }
  when Hash
    # TODO: This is a terrible hack.
    # Waiting on https://jira.corp.stripe.com/browse/API_SERVICES-3167 to add
    # an object in v2 lists
    if api_mode == :v2 && data.include?(:data) && data.include?(:next_page_url)
      return V2::ListObject.construct_from(data, opts, last_response, api_mode, requestor)
    end

    # Try converting to a known object class.  If none available, fall back
    # to generic StripeObject
    object_type = data[:type] || data["type"]
    object_name = data[:object] || data["object"]
    object_class = if api_mode == :v2
                     if object_name == "v2.core.event" && thin_event_classes.key?(object_type)
                       thin_event_classes.fetch(object_type)
                     else
                       v2_object_classes.fetch(
                         object_name, StripeObject
                       )
                     end
                   else
                     object_classes.fetch(object_name, StripeObject)
                   end

    obj = object_class.construct_from(data, opts, last_response, api_mode, requestor)

    # set filters so that we can fetch the same limit, expansions, and
    # predicates when accessing the next and previous pages
    obj.filters = params.dup if obj && (obj.is_a?(SearchResultObject) || obj.is_a?(ListObject))

    obj
  else
    data
  end
end

.custom_method(resource, target, name, http_verb, http_path) ⇒ Object

Adds a custom method to a resource class. This is used to add support for non-CRUDL API requests, e.g. capturing charges. custom_method takes the following parameters:

  • name: the name of the custom method to create (as a symbol)

  • http_verb: the HTTP verb for the API request (:get, :post, or :delete)

  • http_path: the path to append to the resource’s URL. If not provided,

    the name is used as the path
    
  • resource: the resource implementation class

  • target: the class that custom static method will be added to

For example, this call:

custom_method :capture, http_verb: post

adds a ‘capture` class method to the resource class that, when called, will send a POST request to `/v1/<object_name>/capture`. TODO: are we comfortable with deleting this?



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/stripe/util.rb', line 53

def self.custom_method(resource, target, name, http_verb, http_path)
  unless %i[get post delete].include?(http_verb)
    raise ArgumentError,
          "Invalid http_verb value: #{http_verb.inspect}. Should be one " \
          "of :get, :post or :delete."
  end
  unless target.respond_to?(:resource_url)
    raise ArgumentError,
          "Invalid target value: #{target}. Target class should have a " \
          "`resource_url` method."
  end
  http_path ||= name.to_s
  target.define_singleton_method(name) do |id, params = {}, opts = {}|
    unless id.is_a?(String)
      raise ArgumentError,
            "id should be a string representing the ID of an API resource"
    end

    url = "#{target.resource_url}/" \
          "#{CGI.escape(id)}/" \
          "#{CGI.escape(http_path)}"

    resource.execute_resource_request(
      http_verb,
      url,
      :api,
      params,
      opts
    )
  end
end

.encode_parameters(params, api_mode) ⇒ Object

Encodes a hash of parameters in a way that’s suitable for use as query parameters in a URI or as form parameters in a request body. This mainly involves escaping special characters from parameter keys and values (e.g. ‘&`).



221
222
223
224
# File 'lib/stripe/util.rb', line 221

def self.encode_parameters(params, api_mode)
  Util.flatten_params(params, api_mode)
      .map { |k, v| "#{url_encode(k)}=#{url_encode(v)}" }.join("&")
end

.flatten_params(params, api_mode, parent_key = nil) ⇒ Object



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/stripe/util.rb', line 237

def self.flatten_params(params, api_mode, parent_key = nil)
  result = []

  # do not sort the final output because arrays (and arrays of hashes
  # especially) can be order sensitive, but do sort incoming parameters
  params.each do |key, value|
    calculated_key = parent_key ? "#{parent_key}[#{key}]" : key.to_s
    if value.is_a?(Hash)
      result += flatten_params(value, api_mode, calculated_key)
    elsif value.is_a?(Array)
      result += flatten_params_array(value, api_mode, calculated_key)
    else
      result << [calculated_key, value]
    end
  end

  result
end

.flatten_params_array(value, api_mode, calculated_key) ⇒ Object



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/stripe/util.rb', line 256

def self.flatten_params_array(value, api_mode, calculated_key)
  result = []
  value.each_with_index do |elem, i|
    if elem.is_a?(Hash)
      result += flatten_params(elem, api_mode, "#{calculated_key}[#{i}]")
    elsif elem.is_a?(Array)
      result += flatten_params_array(elem, api_mode, calculated_key)
    else
      result << if api_mode == :v2
                  [calculated_key, elem]
                else
                  ["#{calculated_key}[#{i}]", elem]
                end
    end
  end
  result
end

.get_api_mode(path) ⇒ Object

Returns either v1 or v2 as api_mode based on the given path



359
360
361
362
363
364
365
# File 'lib/stripe/util.rb', line 359

def self.get_api_mode(path)
  if path.start_with?("/v2/")
    :v2
  else
    :v1
  end
end

.log_debug(message, data = {}) ⇒ Object



187
188
189
190
191
192
193
194
195
# File 'lib/stripe/util.rb', line 187

def self.log_debug(message, data = {})
  config = data.delete(:config) || Stripe.config
  logger = config.logger || Stripe.logger
  if !logger.nil? ||
     (!config.log_level.nil? && config.log_level <= Stripe::LEVEL_DEBUG)
    log_internal(message, data, color: :blue, level: Stripe::LEVEL_DEBUG,
                                logger: Stripe.logger, out: $stdout)
  end
end

.log_error(message, data = {}) ⇒ Object



167
168
169
170
171
172
173
174
175
# File 'lib/stripe/util.rb', line 167

def self.log_error(message, data = {})
  config = data.delete(:config) || Stripe.config
  logger = config.logger || Stripe.logger
  if !logger.nil? ||
     (!config.log_level.nil? && config.log_level <= Stripe::LEVEL_ERROR)
    log_internal(message, data, color: :cyan, level: Stripe::LEVEL_ERROR,
                                logger: Stripe.logger, out: $stderr)
  end
end

.log_info(message, data = {}) ⇒ Object



177
178
179
180
181
182
183
184
185
# File 'lib/stripe/util.rb', line 177

def self.log_info(message, data = {})
  config = data.delete(:config) || Stripe.config
  logger = config.logger || Stripe.logger
  if !logger.nil? ||
     (!config.log_level.nil? && config.log_level <= Stripe::LEVEL_INFO)
    log_internal(message, data, color: :cyan, level: Stripe::LEVEL_INFO,
                                logger: Stripe.logger, out: $stdout)
  end
end

.monotonic_timeObject

‘Time.now` can be unstable in cases like an administrator manually updating its value or a reconcilation via NTP. For this reason, prefer the use of the system’s monotonic clock especially where comparing times to calculate an elapsed duration.

Shortcut for getting monotonic time, mostly for purposes of line length and test stubbing. Returns time in seconds since the event used for monotonic reference purposes by the platform (e.g. system boot time).



282
283
284
# File 'lib/stripe/util.rb', line 282

def self.monotonic_time
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
end

.normalize_headers(headers) ⇒ Object

Normalizes header keys so that they’re all lower case and each hyphen-delimited section starts with a single capitalized letter. For example, ‘request-id` becomes `Request-Id`. This is useful for extracting certain key values when the user could have set them with a variety of diffent naming schemes.



329
330
331
332
333
334
335
336
# File 'lib/stripe/util.rb', line 329

def self.normalize_headers(headers)
  headers.each_with_object({}) do |(k, v), new_headers|
    k = k.to_s.tr("_", "-") if k.is_a?(Symbol)
    k = k.split("-").reject(&:empty?).map(&:capitalize).join("-")

    new_headers[k] = v
  end
end

.normalize_id(id) ⇒ Object



286
287
288
289
290
291
292
293
294
# File 'lib/stripe/util.rb', line 286

def self.normalize_id(id)
  if id.is_a?(Hash) # overloaded id
    params_hash = id.dup
    id = params_hash.delete(:id)
  else
    params_hash = {}
  end
  [id, params_hash]
end

.normalize_opts(opts) ⇒ Object

The secondary opts argument can either be a string or hash Turn this value into an api_key and a set of headers



298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/stripe/util.rb', line 298

def self.normalize_opts(opts)
  case opts
  when String
    { api_key: opts }
  when Hash
    check_api_key!(opts.fetch(:api_key)) if opts.key?(:api_key)
    # Explicitly use dup here instead of clone to avoid preserving freeze
    # state on input params.
    opts.dup
  else
    raise TypeError, "normalize_opts expects a string or a hash"
  end
end

.object_classesObject



22
23
24
# File 'lib/stripe/util.rb', line 22

def self.object_classes
  @object_classes ||= Stripe::ObjectTypes.object_names_to_classes
end

.object_name_matches_class?(object_name, klass) ⇒ Boolean

Returns:

  • (Boolean)


34
35
36
# File 'lib/stripe/util.rb', line 34

def self.object_name_matches_class?(object_name, klass)
  Util.object_classes[object_name] == klass
end

.objects_to_ids(obj) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/stripe/util.rb', line 7

def self.objects_to_ids(obj)
  case obj
  when APIResource
    obj.id
  when Hash
    res = {}
    obj.each { |k, v| res[k] = objects_to_ids(v) unless v.nil? }
    res
  when Array
    obj.map { |v| objects_to_ids(v) }
  else
    obj
  end
end

.request_id_dashboard_url(request_id, api_key) ⇒ Object

Generates a Dashboard link to inspect a request ID based off of a request ID value and an API key, which is used to attempt to extract whether the environment is livemode or testmode.



341
342
343
344
# File 'lib/stripe/util.rb', line 341

def self.request_id_dashboard_url(request_id, api_key)
  env = !api_key.nil? && api_key.start_with?("sk_live") ? "live" : "test"
  "https://dashboard.stripe.com/#{env}/logs/#{request_id}"
end

.secure_compare(str_a, str_b) ⇒ Object

Constant time string comparison to prevent timing attacks Code borrowed from ActiveSupport



348
349
350
351
352
353
354
355
356
# File 'lib/stripe/util.rb', line 348

def self.secure_compare(str_a, str_b)
  return false unless str_a.bytesize == str_b.bytesize

  l = str_a.unpack "C#{str_a.bytesize}"

  res = 0
  str_b.each_byte { |byte| res |= byte ^ l.shift }
  res.zero?
end

.symbolize_names(object) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/stripe/util.rb', line 197

def self.symbolize_names(object)
  case object
  when Hash
    new_hash = {}
    object.each do |key, value|
      key = (begin
        key.to_sym
      rescue StandardError
        key
      end) || key
      new_hash[key] = symbolize_names(value)
    end
    new_hash
  when Array
    object.map { |value| symbolize_names(value) }
  else
    object
  end
end

.thin_event_classesObject



30
31
32
# File 'lib/stripe/util.rb', line 30

def self.thin_event_classes
  @thin_event_classes ||= Stripe::EventTypes.thin_event_names_to_classes
end

.url_encode(key) ⇒ Object

Encodes a string in a way that makes it suitable for use in a set of query parameters in a URI or in a set of form parameters in a request body.



229
230
231
232
233
234
235
# File 'lib/stripe/util.rb', line 229

def self.url_encode(key)
  CGI.escape(key.to_s).
    # Don't use strict form encoding by changing the square bracket control
    # characters back to their literals. This is fine by the server, and
    # makes these parameter strings easier to read.
    gsub("%5B", "[").gsub("%5D", "]")
end

.v2_object_classesObject



26
27
28
# File 'lib/stripe/util.rb', line 26

def self.v2_object_classes
  @v2_object_classes ||= Stripe::ObjectTypes.v2_object_names_to_classes
end