stripe 3.3.2 → 3.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +20 -0
- data/.rubocop_todo.yml +62 -0
- data/.travis.yml +1 -1
- data/Gemfile +19 -12
- data/History.txt +10 -0
- data/README.md +5 -1
- data/Rakefile +8 -5
- data/VERSION +1 -1
- data/bin/stripe-console +2 -2
- data/lib/stripe.rb +72 -74
- data/lib/stripe/account.rb +15 -17
- data/lib/stripe/alipay_account.rb +10 -7
- data/lib/stripe/api_operations/create.rb +1 -1
- data/lib/stripe/api_operations/delete.rb +1 -1
- data/lib/stripe/api_operations/list.rb +2 -2
- data/lib/stripe/api_operations/request.rb +5 -12
- data/lib/stripe/api_operations/save.rb +6 -6
- data/lib/stripe/api_resource.rb +7 -9
- data/lib/stripe/apple_pay_domain.rb +2 -2
- data/lib/stripe/application_fee.rb +5 -5
- data/lib/stripe/application_fee_refund.rb +5 -5
- data/lib/stripe/balance.rb +1 -1
- data/lib/stripe/balance_transaction.rb +2 -2
- data/lib/stripe/bank_account.rb +7 -7
- data/lib/stripe/bitcoin_receiver.rb +4 -2
- data/lib/stripe/bitcoin_transaction.rb +3 -1
- data/lib/stripe/card.rb +5 -5
- data/lib/stripe/charge.rb +18 -18
- data/lib/stripe/country_spec.rb +2 -2
- data/lib/stripe/coupon.rb +1 -1
- data/lib/stripe/customer.rb +23 -23
- data/lib/stripe/dispute.rb +3 -3
- data/lib/stripe/ephemeral_key.rb +4 -4
- data/lib/stripe/errors.rb +4 -4
- data/lib/stripe/event.rb +1 -1
- data/lib/stripe/file_upload.rb +5 -5
- data/lib/stripe/invoice.rb +7 -7
- data/lib/stripe/invoice_item.rb +1 -1
- data/lib/stripe/invoice_line_item.rb +1 -1
- data/lib/stripe/list_object.rb +14 -18
- data/lib/stripe/login_link.rb +3 -3
- data/lib/stripe/oauth.rb +15 -13
- data/lib/stripe/order.rb +5 -5
- data/lib/stripe/order_return.rb +1 -1
- data/lib/stripe/payout.rb +3 -3
- data/lib/stripe/plan.rb +1 -1
- data/lib/stripe/product.rb +1 -1
- data/lib/stripe/recipient.rb +3 -2
- data/lib/stripe/recipient_transfer.rb +1 -2
- data/lib/stripe/refund.rb +1 -1
- data/lib/stripe/reversal.rb +5 -5
- data/lib/stripe/singleton_api_resource.rb +3 -3
- data/lib/stripe/sku.rb +1 -1
- data/lib/stripe/source.rb +13 -10
- data/lib/stripe/stripe_client.rb +149 -169
- data/lib/stripe/stripe_object.rb +77 -76
- data/lib/stripe/subscription.rb +5 -5
- data/lib/stripe/subscription_item.rb +2 -2
- data/lib/stripe/three_d_secure.rb +1 -1
- data/lib/stripe/token.rb +1 -1
- data/lib/stripe/transfer.rb +3 -3
- data/lib/stripe/util.rb +77 -62
- data/lib/stripe/version.rb +1 -1
- data/lib/stripe/webhook.rb +14 -10
- data/stripe.gemspec +14 -14
- data/test/stripe/account_test.rb +69 -81
- data/test/stripe/alipay_account_test.rb +19 -1
- data/test/stripe/api_operations_test.rb +7 -7
- data/test/stripe/api_resource_test.rb +224 -260
- data/test/stripe/apple_pay_domain_test.rb +8 -8
- data/test/stripe/application_fee_refund_test.rb +8 -8
- data/test/stripe/application_fee_test.rb +3 -3
- data/test/stripe/balance_test.rb +2 -2
- data/test/stripe/bank_account_test.rb +9 -11
- data/test/stripe/charge_test.rb +11 -11
- data/test/stripe/country_spec_test.rb +4 -4
- data/test/stripe/coupon_test.rb +10 -10
- data/test/stripe/customer_card_test.rb +11 -15
- data/test/stripe/customer_test.rb +26 -27
- data/test/stripe/dispute_test.rb +8 -8
- data/test/stripe/ephemeral_key_test.rb +14 -14
- data/test/stripe/errors_test.rb +2 -2
- data/test/stripe/file_upload_test.rb +26 -28
- data/test/stripe/invoice_item_test.rb +14 -14
- data/test/stripe/invoice_line_item_test.rb +1 -1
- data/test/stripe/invoice_test.rb +37 -37
- data/test/stripe/list_object_test.rb +60 -76
- data/test/stripe/login_link_test.rb +14 -14
- data/test/stripe/oauth_test.rb +42 -50
- data/test/stripe/order_return_test.rb +5 -5
- data/test/stripe/order_test.rb +12 -12
- data/test/stripe/payout_test.rb +9 -9
- data/test/stripe/plan_test.rb +9 -9
- data/test/stripe/product_test.rb +8 -8
- data/test/stripe/recipient_test.rb +9 -10
- data/test/stripe/refund_test.rb +9 -9
- data/test/stripe/reversal_test.rb +10 -10
- data/test/stripe/sku_test.rb +8 -8
- data/test/stripe/source_test.rb +14 -16
- data/test/stripe/stripe_client_test.rb +235 -266
- data/test/stripe/stripe_object_test.rb +163 -147
- data/test/stripe/stripe_response_test.rb +4 -3
- data/test/stripe/subscription_item_test.rb +11 -11
- data/test/stripe/subscription_test.rb +14 -14
- data/test/stripe/three_d_secure_test.rb +2 -2
- data/test/stripe/transfer_test.rb +8 -8
- data/test/stripe/util_test.rb +59 -57
- data/test/stripe/webhook_test.rb +18 -16
- data/test/stripe_test.rb +4 -4
- data/test/test_data.rb +26 -26
- data/test/test_helper.rb +29 -25
- metadata +6 -10
- data/test/stripe/bitcoin_receiver_test.rb +0 -67
- data/test/stripe/bitcoin_transaction_test.rb +0 -19
- data/test/stripe/recipient_card_test.rb +0 -44
data/lib/stripe/stripe_object.rb
CHANGED
@@ -5,11 +5,9 @@ module Stripe
|
|
5
5
|
@@permanent_attributes = Set.new([:id])
|
6
6
|
|
7
7
|
# The default :id method is deprecated and isn't useful to us
|
8
|
-
if method_defined?(:id)
|
9
|
-
undef :id
|
10
|
-
end
|
8
|
+
undef :id if method_defined?(:id)
|
11
9
|
|
12
|
-
def initialize(id=nil, opts={})
|
10
|
+
def initialize(id = nil, opts = {})
|
13
11
|
id, @retrieve_params = Util.normalize_id(id)
|
14
12
|
@opts = Util.normalize_opts(opts)
|
15
13
|
@original_values = {}
|
@@ -21,11 +19,11 @@ module Stripe
|
|
21
19
|
@values[:id] = id if id
|
22
20
|
end
|
23
21
|
|
24
|
-
def self.construct_from(values, opts={})
|
22
|
+
def self.construct_from(values, opts = {})
|
25
23
|
values = Stripe::Util.symbolize_names(values)
|
26
24
|
|
27
25
|
# work around protected #initialize_from for now
|
28
|
-
|
26
|
+
new(values[:id]).send(:initialize_from, values, opts)
|
29
27
|
end
|
30
28
|
|
31
29
|
# Determines the equality of two Stripe objects. Stripe objects are
|
@@ -42,13 +40,13 @@ module Stripe
|
|
42
40
|
@values.fetch(:deleted, false)
|
43
41
|
end
|
44
42
|
|
45
|
-
def to_s(*
|
43
|
+
def to_s(*_args)
|
46
44
|
JSON.pretty_generate(to_hash)
|
47
45
|
end
|
48
46
|
|
49
47
|
def inspect
|
50
|
-
id_string =
|
51
|
-
"#<#{self.class}:0x#{
|
48
|
+
id_string = respond_to?(:id) && !id.nil? ? " id=#{id}" : ""
|
49
|
+
"#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(@values)
|
52
50
|
end
|
53
51
|
|
54
52
|
# Re-initializes the object based on a hash of values (usually one that's
|
@@ -57,11 +55,11 @@ module Stripe
|
|
57
55
|
#
|
58
56
|
# Please don't use this method. If you're trying to do mass assignment, try
|
59
57
|
# #initialize_from instead.
|
60
|
-
def refresh_from(values, opts, partial=false)
|
58
|
+
def refresh_from(values, opts, partial = false)
|
61
59
|
initialize_from(values, opts, partial)
|
62
60
|
end
|
63
61
|
extend Gem::Deprecate
|
64
|
-
deprecate :refresh_from, "#update_attributes", 2016,
|
62
|
+
deprecate :refresh_from, "#update_attributes", 2016, 1
|
65
63
|
|
66
64
|
# Mass assigns attributes on the model.
|
67
65
|
#
|
@@ -108,7 +106,7 @@ module Stripe
|
|
108
106
|
@values.values
|
109
107
|
end
|
110
108
|
|
111
|
-
def to_json(*
|
109
|
+
def to_json(*_a)
|
112
110
|
JSON.generate(@values)
|
113
111
|
end
|
114
112
|
|
@@ -121,14 +119,13 @@ module Stripe
|
|
121
119
|
value.respond_to?(:to_hash) ? value.to_hash : value
|
122
120
|
end
|
123
121
|
|
124
|
-
@values.
|
122
|
+
@values.each_with_object({}) do |(key, value), acc|
|
125
123
|
acc[key] = case value
|
126
124
|
when Array
|
127
125
|
value.map(&maybe_to_hash)
|
128
126
|
else
|
129
127
|
maybe_to_hash.call(value)
|
130
128
|
end
|
131
|
-
acc
|
132
129
|
end
|
133
130
|
end
|
134
131
|
|
@@ -136,27 +133,13 @@ module Stripe
|
|
136
133
|
@values.each(&blk)
|
137
134
|
end
|
138
135
|
|
139
|
-
def _dump(level)
|
140
|
-
# The StripeClient instance in @opts is not serializable and is not
|
141
|
-
# really a property of the StripeObject, so we exclude it when
|
142
|
-
# dumping
|
143
|
-
opts = @opts.clone
|
144
|
-
opts.delete(:client)
|
145
|
-
Marshal.dump([@values, opts])
|
146
|
-
end
|
147
|
-
|
148
|
-
def self._load(args)
|
149
|
-
values, opts = Marshal.load(args)
|
150
|
-
construct_from(values, opts)
|
151
|
-
end
|
152
|
-
|
153
136
|
# Sets all keys within the StripeObject as unsaved so that they will be
|
154
137
|
# included with an update when #serialize_params is called. This method is
|
155
138
|
# also recursive, so any StripeObjects contained as values or which are
|
156
139
|
# values in a tenant array are also marked as dirty.
|
157
140
|
def dirty!
|
158
141
|
@unsaved_values = Set.new(@values.keys)
|
159
|
-
@values.
|
142
|
+
@values.each_value do |v|
|
160
143
|
dirty_value!(v)
|
161
144
|
end
|
162
145
|
end
|
@@ -182,7 +165,7 @@ module Stripe
|
|
182
165
|
|
183
166
|
# a `nil` that makes it out of `#serialize_params_value` signals an empty
|
184
167
|
# value that we shouldn't appear in the serialized form of the object
|
185
|
-
update_hash.reject! { |_, v| v
|
168
|
+
update_hash.reject! { |_, v| v.nil? }
|
186
169
|
|
187
170
|
update_hash
|
188
171
|
end
|
@@ -197,8 +180,6 @@ module Stripe
|
|
197
180
|
deprecate :serialize_params, "#serialize_params", 2016, 9
|
198
181
|
end
|
199
182
|
|
200
|
-
protected
|
201
|
-
|
202
183
|
# A protected field is one that doesn't get an accessor assigned to it
|
203
184
|
# (i.e. `obj.public = ...`) and one which is not allowed to be updated via
|
204
185
|
# the class level `Model.update(id, { ... })`.
|
@@ -206,6 +187,8 @@ module Stripe
|
|
206
187
|
[]
|
207
188
|
end
|
208
189
|
|
190
|
+
protected
|
191
|
+
|
209
192
|
def metaclass
|
210
193
|
class << self; self; end
|
211
194
|
end
|
@@ -221,9 +204,7 @@ module Stripe
|
|
221
204
|
|
222
205
|
# Remove methods for the accessor's reader and writer.
|
223
206
|
[k, :"#{k}=", :"#{k}?"].each do |method_name|
|
224
|
-
if method_defined?(method_name)
|
225
|
-
remove_method(method_name)
|
226
|
-
end
|
207
|
+
remove_method(method_name) if method_defined?(method_name)
|
227
208
|
end
|
228
209
|
end
|
229
210
|
end
|
@@ -241,10 +222,9 @@ module Stripe
|
|
241
222
|
define_method(k) { @values[k] }
|
242
223
|
define_method(:"#{k}=") do |v|
|
243
224
|
if v == ""
|
244
|
-
raise ArgumentError.
|
245
|
-
"You cannot set #{k} to an empty string. " \
|
225
|
+
raise ArgumentError, "You cannot set #{k} to an empty string. " \
|
246
226
|
"We interpret empty strings as nil in requests. " \
|
247
|
-
"You may set (object).#{k} = nil to delete the property."
|
227
|
+
"You may set (object).#{k} = nil to delete the property."
|
248
228
|
end
|
249
229
|
@values[k] = Util.convert_to_stripe_object(v, @opts)
|
250
230
|
dirty_value!(@values[k])
|
@@ -260,7 +240,7 @@ module Stripe
|
|
260
240
|
|
261
241
|
def method_missing(name, *args)
|
262
242
|
# TODO: only allow setting in updateable classes.
|
263
|
-
if name.to_s.end_with?(
|
243
|
+
if name.to_s.end_with?("=")
|
264
244
|
attr = name.to_s[0...-1].to_sym
|
265
245
|
|
266
246
|
# Pull out the assigned value. This is only used in the case of a
|
@@ -269,31 +249,32 @@ module Stripe
|
|
269
249
|
val = args.first
|
270
250
|
|
271
251
|
# the second argument is only required when adding boolean accessors
|
272
|
-
add_accessors([attr],
|
252
|
+
add_accessors([attr], attr => val)
|
273
253
|
|
274
254
|
begin
|
275
255
|
mth = method(name)
|
276
256
|
rescue NameError
|
277
|
-
raise NoMethodError
|
257
|
+
raise NoMethodError, "Cannot set #{attr} on this object. HINT: you can't set: #{@@permanent_attributes.to_a.join(', ')}"
|
278
258
|
end
|
279
259
|
return mth.call(args[0])
|
280
|
-
|
281
|
-
return @values[name]
|
260
|
+
elsif @values.key?(name)
|
261
|
+
return @values[name]
|
282
262
|
end
|
283
263
|
|
284
264
|
begin
|
285
265
|
super
|
286
266
|
rescue NoMethodError => e
|
287
|
-
if
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
267
|
+
# If we notice the accessed name if our set of transient values we can
|
268
|
+
# give the user a slightly more helpful error message. If not, just
|
269
|
+
# raise right away.
|
270
|
+
raise unless @transient_values.include?(name)
|
271
|
+
|
272
|
+
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(', ')}"
|
292
273
|
end
|
293
274
|
end
|
294
275
|
|
295
276
|
def respond_to_missing?(symbol, include_private = false)
|
296
|
-
@values && @values.
|
277
|
+
@values && @values.key?(symbol) || super
|
297
278
|
end
|
298
279
|
|
299
280
|
# Re-initializes the object based on a hash of values (usually one that's
|
@@ -308,9 +289,11 @@ module Stripe
|
|
308
289
|
# * +:opts:+ Options for StripeObject like an API key.
|
309
290
|
# * +:partial:+ Indicates that the re-initialization should not attempt to
|
310
291
|
# remove accessors.
|
311
|
-
def initialize_from(values, opts, partial=false)
|
292
|
+
def initialize_from(values, opts, partial = false)
|
312
293
|
@opts = Util.normalize_opts(opts)
|
313
|
-
|
294
|
+
|
295
|
+
# the `#send` is here so that we can keep this method private
|
296
|
+
@original_values = self.class.send(:deep_copy, values)
|
314
297
|
|
315
298
|
removed = partial ? Set.new : Set.new(@values.keys - values.keys)
|
316
299
|
added = Set.new(values.keys - @values.keys)
|
@@ -328,8 +311,8 @@ module Stripe
|
|
328
311
|
@unsaved_values.delete(k)
|
329
312
|
end
|
330
313
|
|
331
|
-
update_attributes(values, opts, :
|
332
|
-
values.
|
314
|
+
update_attributes(values, opts, dirty: false)
|
315
|
+
values.each_key do |k|
|
333
316
|
@transient_values.delete(k)
|
334
317
|
@unsaved_values.delete(k)
|
335
318
|
end
|
@@ -338,9 +321,8 @@ module Stripe
|
|
338
321
|
end
|
339
322
|
|
340
323
|
def serialize_params_value(value, original, unsaved, force, key: nil)
|
341
|
-
|
342
|
-
|
343
|
-
''
|
324
|
+
if value.nil?
|
325
|
+
""
|
344
326
|
|
345
327
|
# The logic here is that essentially any object embedded in another
|
346
328
|
# object that had a `type` is actually an API resource of a different
|
@@ -364,10 +346,10 @@ module Stripe
|
|
364
346
|
# We throw an error if a property was set explicitly but we can't do
|
365
347
|
# anything with it because the integration is probably not working as the
|
366
348
|
# user intended it to.
|
367
|
-
|
349
|
+
elsif value.is_a?(APIResource) && !value.save_with_parent
|
368
350
|
if !unsaved
|
369
351
|
nil
|
370
|
-
elsif value.respond_to?(:id) && value.id
|
352
|
+
elsif value.respond_to?(:id) && !value.id.nil?
|
371
353
|
value
|
372
354
|
else
|
373
355
|
raise ArgumentError, "Cannot save property `#{key}` containing " \
|
@@ -375,15 +357,11 @@ module Stripe
|
|
375
357
|
"not marked as `save_with_parent`."
|
376
358
|
end
|
377
359
|
|
378
|
-
|
360
|
+
elsif value.is_a?(Array)
|
379
361
|
update = value.map { |v| serialize_params_value(v, nil, true, force) }
|
380
362
|
|
381
363
|
# This prevents an array that's unchanged from being resent.
|
382
|
-
if update != serialize_params_value(original, nil, true, force)
|
383
|
-
update
|
384
|
-
else
|
385
|
-
nil
|
386
|
-
end
|
364
|
+
update if update != serialize_params_value(original, nil, true, force)
|
387
365
|
|
388
366
|
# Handle a Hash for now, but in the long run we should be able to
|
389
367
|
# eliminate all places where hashes are stored as values internally by
|
@@ -395,11 +373,11 @@ module Stripe
|
|
395
373
|
# existing array being held by a StripeObject. This could happen for
|
396
374
|
# example by appending a new hash onto `additional_owners` for an
|
397
375
|
# account.
|
398
|
-
|
376
|
+
elsif value.is_a?(Hash)
|
399
377
|
Util.convert_to_stripe_object(value, @opts).serialize_params
|
400
378
|
|
401
|
-
|
402
|
-
update = value.serialize_params(:
|
379
|
+
elsif value.is_a?(StripeObject)
|
380
|
+
update = value.serialize_params(force: force)
|
403
381
|
|
404
382
|
# If the entire object was replaced, then we need blank each field of
|
405
383
|
# the old object that held a value. The new serialized values will
|
@@ -415,6 +393,30 @@ module Stripe
|
|
415
393
|
|
416
394
|
private
|
417
395
|
|
396
|
+
# Produces a deep copy of the given object including support for arrays,
|
397
|
+
# hashes, and StripeObjects.
|
398
|
+
def self.deep_copy(obj)
|
399
|
+
case obj
|
400
|
+
when Array
|
401
|
+
obj.map { |e| deep_copy(e) }
|
402
|
+
when Hash
|
403
|
+
obj.each_with_object({}) do |(k, v), copy|
|
404
|
+
copy[k] = deep_copy(v)
|
405
|
+
copy
|
406
|
+
end
|
407
|
+
when StripeObject
|
408
|
+
StripeObject.construct_from(
|
409
|
+
deep_copy(obj.instance_variable_get(:@values)),
|
410
|
+
obj.instance_variable_get(:@opts).select do |k, _v|
|
411
|
+
Util::OPTS_COPYABLE.include?(k)
|
412
|
+
end
|
413
|
+
)
|
414
|
+
else
|
415
|
+
obj
|
416
|
+
end
|
417
|
+
end
|
418
|
+
private_class_method :deep_copy
|
419
|
+
|
418
420
|
def dirty_value!(value)
|
419
421
|
case value
|
420
422
|
when Array
|
@@ -428,15 +430,14 @@ module Stripe
|
|
428
430
|
# StripeObject.
|
429
431
|
def empty_values(obj)
|
430
432
|
values = case obj
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
values.
|
438
|
-
update[k] =
|
439
|
-
update
|
433
|
+
when Hash then obj
|
434
|
+
when StripeObject then obj.instance_variable_get(:@values)
|
435
|
+
else
|
436
|
+
raise ArgumentError, "#empty_values got unexpected object type: #{obj.class.name}"
|
437
|
+
end
|
438
|
+
|
439
|
+
values.each_with_object({}) do |(k, _), update|
|
440
|
+
update[k] = ""
|
440
441
|
end
|
441
442
|
end
|
442
443
|
end
|
data/lib/stripe/subscription.rb
CHANGED
@@ -5,21 +5,21 @@ module Stripe
|
|
5
5
|
include Stripe::APIOperations::Save
|
6
6
|
include Stripe::APIOperations::Delete
|
7
7
|
|
8
|
-
OBJECT_NAME =
|
8
|
+
OBJECT_NAME = "subscription".freeze
|
9
9
|
|
10
10
|
save_nested_resource :source
|
11
11
|
|
12
12
|
def delete_discount
|
13
13
|
_, opts = request(:delete, discount_url)
|
14
|
-
initialize_from({ :
|
14
|
+
initialize_from({ discount: nil }, opts, true)
|
15
15
|
end
|
16
16
|
|
17
|
-
def self.update(id, params={}, opts={})
|
17
|
+
def self.update(id, params = {}, opts = {})
|
18
18
|
params[:items] = Util.array_to_hash(params[:items]) if params[:items]
|
19
19
|
super(id, params, opts)
|
20
20
|
end
|
21
21
|
|
22
|
-
def self.create(params={}, opts={})
|
22
|
+
def self.create(params = {}, opts = {})
|
23
23
|
params[:items] = Util.array_to_hash(params[:items]) if params[:items]
|
24
24
|
super(params, opts)
|
25
25
|
end
|
@@ -27,7 +27,7 @@ module Stripe
|
|
27
27
|
private
|
28
28
|
|
29
29
|
def discount_url
|
30
|
-
resource_url +
|
30
|
+
resource_url + "/discount"
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
@@ -5,10 +5,10 @@ module Stripe
|
|
5
5
|
extend Stripe::APIOperations::List
|
6
6
|
include Stripe::APIOperations::Save
|
7
7
|
|
8
|
-
OBJECT_NAME =
|
8
|
+
OBJECT_NAME = "subscription_item".freeze
|
9
9
|
|
10
10
|
def self.resource_url
|
11
|
-
|
11
|
+
"/v1/subscription_items"
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
data/lib/stripe/token.rb
CHANGED
data/lib/stripe/transfer.rb
CHANGED
@@ -4,15 +4,15 @@ module Stripe
|
|
4
4
|
extend Stripe::APIOperations::Create
|
5
5
|
include Stripe::APIOperations::Save
|
6
6
|
|
7
|
-
OBJECT_NAME =
|
7
|
+
OBJECT_NAME = "transfer".freeze
|
8
8
|
|
9
9
|
def cancel
|
10
|
-
resp, api_key =
|
10
|
+
resp, api_key = request(:post, cancel_url)
|
11
11
|
initialize_from(resp.data, api_key)
|
12
12
|
end
|
13
13
|
|
14
14
|
def cancel_url
|
15
|
-
resource_url +
|
15
|
+
resource_url + "/cancel"
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
data/lib/stripe/util.rb
CHANGED
@@ -2,6 +2,22 @@ require "cgi"
|
|
2
2
|
|
3
3
|
module Stripe
|
4
4
|
module Util
|
5
|
+
# Options that a user is allowed to specify.
|
6
|
+
OPTS_USER_SPECIFIED = Set[
|
7
|
+
:api_key,
|
8
|
+
:idempotency_key,
|
9
|
+
:stripe_account,
|
10
|
+
:stripe_version
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
# Options that should be copyable from one StripeObject to another
|
14
|
+
# including options that may be internal.
|
15
|
+
OPTS_COPYABLE = (OPTS_USER_SPECIFIED + Set[:api_base]).freeze
|
16
|
+
|
17
|
+
# Options that should be persisted between API requests. This includes
|
18
|
+
# client, which is an object containing an HTTP client to reuse.
|
19
|
+
OPTS_KEYS_TO_PERSIST = (OPTS_USER_SPECIFIED + Set[:client]).freeze
|
20
|
+
|
5
21
|
def self.objects_to_ids(h)
|
6
22
|
case h
|
7
23
|
when APIResource
|
@@ -91,25 +107,25 @@ module Stripe
|
|
91
107
|
|
92
108
|
def self.log_error(message, data = {})
|
93
109
|
if !Stripe.logger.nil? ||
|
94
|
-
|
110
|
+
!Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_ERROR
|
95
111
|
log_internal(message, data, color: :cyan,
|
96
|
-
|
112
|
+
level: Stripe::LEVEL_ERROR, logger: Stripe.logger, out: $stderr)
|
97
113
|
end
|
98
114
|
end
|
99
115
|
|
100
116
|
def self.log_info(message, data = {})
|
101
117
|
if !Stripe.logger.nil? ||
|
102
|
-
|
118
|
+
!Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_INFO
|
103
119
|
log_internal(message, data, color: :cyan,
|
104
|
-
|
120
|
+
level: Stripe::LEVEL_INFO, logger: Stripe.logger, out: $stdout)
|
105
121
|
end
|
106
122
|
end
|
107
123
|
|
108
124
|
def self.log_debug(message, data = {})
|
109
125
|
if !Stripe.logger.nil? ||
|
110
|
-
|
126
|
+
!Stripe.log_level.nil? && Stripe.log_level <= Stripe::LEVEL_DEBUG
|
111
127
|
log_internal(message, data, color: :blue,
|
112
|
-
|
128
|
+
level: Stripe::LEVEL_DEBUG, logger: Stripe.logger, out: $stdout)
|
113
129
|
end
|
114
130
|
end
|
115
131
|
|
@@ -117,13 +133,12 @@ module Stripe
|
|
117
133
|
# This is nominally equivalent to File.readable?, but that can
|
118
134
|
# report incorrect results on some more oddball filesystems
|
119
135
|
# (such as AFS)
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end
|
136
|
+
|
137
|
+
File.open(file) { |f| }
|
138
|
+
rescue StandardError
|
139
|
+
false
|
140
|
+
else
|
141
|
+
true
|
127
142
|
end
|
128
143
|
|
129
144
|
def self.symbolize_names(object)
|
@@ -131,7 +146,11 @@ module Stripe
|
|
131
146
|
when Hash
|
132
147
|
new_hash = {}
|
133
148
|
object.each do |key, value|
|
134
|
-
key = (
|
149
|
+
key = (begin
|
150
|
+
key.to_sym
|
151
|
+
rescue StandardError
|
152
|
+
key
|
153
|
+
end) || key
|
135
154
|
new_hash[key] = symbolize_names(value)
|
136
155
|
end
|
137
156
|
new_hash
|
@@ -147,8 +166,8 @@ module Stripe
|
|
147
166
|
# involves escaping special characters from parameter keys and values (e.g.
|
148
167
|
# `&`).
|
149
168
|
def self.encode_parameters(params)
|
150
|
-
Util.flatten_params(params)
|
151
|
-
|
169
|
+
Util.flatten_params(params)
|
170
|
+
.map { |k, v| "#{url_encode(k)}=#{url_encode(v)}" }.join("&")
|
152
171
|
end
|
153
172
|
|
154
173
|
# Transforms an array into a hash with integer keys. Used for a small
|
@@ -158,7 +177,7 @@ module Stripe
|
|
158
177
|
case array
|
159
178
|
when Array
|
160
179
|
hash = {}
|
161
|
-
array.each_with_index { |v,i| hash[i.to_s] = v }
|
180
|
+
array.each_with_index { |v, i| hash[i.to_s] = v }
|
162
181
|
hash
|
163
182
|
else
|
164
183
|
array
|
@@ -173,16 +192,16 @@ module Stripe
|
|
173
192
|
# Don't use strict form encoding by changing the square bracket control
|
174
193
|
# characters back to their literals. This is fine by the server, and
|
175
194
|
# makes these parameter strings easier to read.
|
176
|
-
gsub(
|
195
|
+
gsub("%5B", "[").gsub("%5D", "]")
|
177
196
|
end
|
178
197
|
|
179
|
-
def self.flatten_params(params, parent_key=nil)
|
198
|
+
def self.flatten_params(params, parent_key = nil)
|
180
199
|
result = []
|
181
200
|
|
182
201
|
# do not sort the final output because arrays (and arrays of hashes
|
183
202
|
# especially) can be order sensitive, but do sort incoming parameters
|
184
203
|
params.each do |key, value|
|
185
|
-
calculated_key = parent_key ? "#{parent_key}[#{key}]" :
|
204
|
+
calculated_key = parent_key ? "#{parent_key}[#{key}]" : key.to_s
|
186
205
|
if value.is_a?(Hash)
|
187
206
|
result += flatten_params(value, calculated_key)
|
188
207
|
elsif value.is_a?(Array)
|
@@ -211,7 +230,7 @@ module Stripe
|
|
211
230
|
end
|
212
231
|
|
213
232
|
def self.normalize_id(id)
|
214
|
-
if id.
|
233
|
+
if id.is_a?(Hash) # overloaded id
|
215
234
|
params_hash = id.dup
|
216
235
|
id = params_hash.delete(:id)
|
217
236
|
else
|
@@ -225,22 +244,22 @@ module Stripe
|
|
225
244
|
def self.normalize_opts(opts)
|
226
245
|
case opts
|
227
246
|
when String
|
228
|
-
{:
|
247
|
+
{ api_key: opts }
|
229
248
|
when Hash
|
230
|
-
check_api_key!(opts.fetch(:api_key)) if opts.
|
249
|
+
check_api_key!(opts.fetch(:api_key)) if opts.key?(:api_key)
|
231
250
|
opts.clone
|
232
251
|
else
|
233
|
-
raise TypeError
|
252
|
+
raise TypeError, "normalize_opts expects a string or a hash"
|
234
253
|
end
|
235
254
|
end
|
236
255
|
|
237
256
|
def self.check_string_argument!(key)
|
238
|
-
raise TypeError
|
257
|
+
raise TypeError, "argument must be a string" unless key.is_a?(String)
|
239
258
|
key
|
240
259
|
end
|
241
260
|
|
242
261
|
def self.check_api_key!(key)
|
243
|
-
raise TypeError
|
262
|
+
raise TypeError, "api_key must be a string" unless key.is_a?(String)
|
244
263
|
key
|
245
264
|
end
|
246
265
|
|
@@ -250,15 +269,14 @@ module Stripe
|
|
250
269
|
# certain key values when the user could have set them with a variety of
|
251
270
|
# diffent naming schemes.
|
252
271
|
def self.normalize_headers(headers)
|
253
|
-
headers.
|
272
|
+
headers.each_with_object({}) do |(k, v), new_headers|
|
254
273
|
if k.is_a?(Symbol)
|
255
|
-
k = titlecase_parts(k.to_s.
|
274
|
+
k = titlecase_parts(k.to_s.tr("_", "-"))
|
256
275
|
elsif k.is_a?(String)
|
257
276
|
k = titlecase_parts(k)
|
258
277
|
end
|
259
278
|
|
260
279
|
new_headers[k] = v
|
261
|
-
new_headers
|
262
280
|
end
|
263
281
|
end
|
264
282
|
|
@@ -279,22 +297,24 @@ module Stripe
|
|
279
297
|
|
280
298
|
res = 0
|
281
299
|
b.each_byte { |byte| res |= byte ^ l.shift }
|
282
|
-
res
|
300
|
+
res.zero?
|
283
301
|
end
|
284
302
|
|
285
|
-
|
303
|
+
#
|
304
|
+
# private
|
305
|
+
#
|
286
306
|
|
287
307
|
COLOR_CODES = {
|
288
|
-
:
|
289
|
-
:
|
290
|
-
:
|
291
|
-
:
|
292
|
-
:
|
293
|
-
:
|
294
|
-
:
|
295
|
-
:
|
296
|
-
:
|
297
|
-
}
|
308
|
+
black: 0, light_black: 60,
|
309
|
+
red: 1, light_red: 61,
|
310
|
+
green: 2, light_green: 62,
|
311
|
+
yellow: 3, light_yellow: 63,
|
312
|
+
blue: 4, light_blue: 64,
|
313
|
+
magenta: 5, light_magenta: 65,
|
314
|
+
cyan: 6, light_cyan: 66,
|
315
|
+
white: 7, light_white: 67,
|
316
|
+
default: 9,
|
317
|
+
}.freeze
|
298
318
|
private_constant :COLOR_CODES
|
299
319
|
|
300
320
|
# We use a pretty janky version of form encoding (Rack's) that supports
|
@@ -317,16 +337,16 @@ module Stripe
|
|
317
337
|
def self.check_array_of_maps_start_keys!(arr)
|
318
338
|
expected_key = nil
|
319
339
|
arr.each do |item|
|
320
|
-
|
321
|
-
|
340
|
+
break unless item.is_a?(Hash)
|
341
|
+
break if item.count.zero?
|
322
342
|
|
323
343
|
first_key = item.first[0]
|
324
344
|
|
325
345
|
if expected_key
|
326
346
|
if expected_key != first_key
|
327
347
|
raise ArgumentError,
|
328
|
-
|
329
|
-
|
348
|
+
"All maps nested in an array should start with the same key " \
|
349
|
+
"(expected starting key '#{expected_key}', got '#{first_key}')"
|
330
350
|
end
|
331
351
|
else
|
332
352
|
expected_key = first_key
|
@@ -362,34 +382,29 @@ module Stripe
|
|
362
382
|
# TODO: Make these named required arguments when we drop support for Ruby
|
363
383
|
# 2.0.
|
364
384
|
def self.log_internal(message, data = {}, color: nil, level: nil, logger: nil, out: nil)
|
365
|
-
data_str = data.
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
wrap_logfmt_value(v)
|
370
|
-
]
|
371
|
-
}.join(" ")
|
385
|
+
data_str = data.reject { |_k, v| v.nil? }
|
386
|
+
.map do |(k, v)|
|
387
|
+
format("%s=%s", colorize(k, color, !out.nil? && out.isatty), wrap_logfmt_value(v))
|
388
|
+
end.join(" ")
|
372
389
|
|
373
390
|
if !logger.nil?
|
374
391
|
# the library's log levels are mapped to the same values as the
|
375
392
|
# standard library's logger
|
376
393
|
logger.log(level,
|
377
|
-
|
394
|
+
format("message=%s %s", wrap_logfmt_value(message), data_str))
|
378
395
|
elsif out.isatty
|
379
|
-
out.puts "%s %s %s"
|
380
|
-
[colorize(level_name(level)[0, 4].upcase, color, out.isatty), message, data_str]
|
396
|
+
out.puts format("%s %s %s", colorize(level_name(level)[0, 4].upcase, color, out.isatty), message, data_str)
|
381
397
|
else
|
382
|
-
out.puts "message=%s level=%s %s"
|
383
|
-
[wrap_logfmt_value(message), level_name(level), data_str]
|
398
|
+
out.puts format("message=%s level=%s %s", wrap_logfmt_value(message), level_name(level), data_str)
|
384
399
|
end
|
385
400
|
end
|
386
401
|
private_class_method :log_internal
|
387
402
|
|
388
403
|
def self.titlecase_parts(s)
|
389
|
-
s.split("-")
|
390
|
-
|
391
|
-
|
392
|
-
|
404
|
+
s.split("-")
|
405
|
+
.reject { |p| p == "" }
|
406
|
+
.map { |p| p[0].upcase + p[1..-1].downcase }
|
407
|
+
.join("-")
|
393
408
|
end
|
394
409
|
private_class_method :titlecase_parts
|
395
410
|
|
@@ -406,7 +421,7 @@ module Stripe
|
|
406
421
|
if %r{[^\w\-/]} =~ val
|
407
422
|
# If the string contains any special characters, escape any double
|
408
423
|
# quotes it has, remove newlines, and wrap the whole thing in quotes.
|
409
|
-
%
|
424
|
+
format(%("%s"), val.gsub('"', '\"').delete("\n"))
|
410
425
|
else
|
411
426
|
# Otherwise use the basic value if it looks like a standard set of
|
412
427
|
# characters (and allow a few special characters like hyphens, and
|