stripe 1.31.0 → 1.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +5 -0
  3. data/.travis.yml +2 -12
  4. data/Gemfile +29 -4
  5. data/History.txt +168 -0
  6. data/README.md +134 -0
  7. data/Rakefile +10 -0
  8. data/VERSION +1 -1
  9. data/bin/stripe-console +12 -5
  10. data/lib/data/ca-certificates.crt +3868 -5114
  11. data/lib/stripe/account.rb +41 -21
  12. data/lib/stripe/alipay_account.rb +20 -0
  13. data/lib/stripe/api_operations/create.rb +1 -1
  14. data/lib/stripe/api_operations/delete.rb +1 -1
  15. data/lib/stripe/api_operations/list.rb +1 -2
  16. data/lib/stripe/api_operations/save.rb +87 -0
  17. data/lib/stripe/api_resource.rb +37 -4
  18. data/lib/stripe/apple_pay_domain.rb +12 -0
  19. data/lib/stripe/application_fee.rb +8 -8
  20. data/lib/stripe/application_fee_refund.rb +7 -3
  21. data/lib/stripe/balance_transaction.rb +1 -1
  22. data/lib/stripe/bank_account.rb +9 -5
  23. data/lib/stripe/bitcoin_receiver.rb +6 -6
  24. data/lib/stripe/bitcoin_transaction.rb +1 -1
  25. data/lib/stripe/card.rb +9 -5
  26. data/lib/stripe/charge.rb +30 -12
  27. data/lib/stripe/country_spec.rb +9 -0
  28. data/lib/stripe/coupon.rb +1 -1
  29. data/lib/stripe/customer.rb +6 -4
  30. data/lib/stripe/dispute.rb +2 -2
  31. data/lib/stripe/errors.rb +82 -0
  32. data/lib/stripe/file_upload.rb +1 -1
  33. data/lib/stripe/invoice.rb +3 -3
  34. data/lib/stripe/invoice_item.rb +1 -1
  35. data/lib/stripe/list_object.rb +7 -6
  36. data/lib/stripe/order.rb +10 -2
  37. data/lib/stripe/order_return.rb +9 -0
  38. data/lib/stripe/plan.rb +1 -1
  39. data/lib/stripe/product.rb +2 -10
  40. data/lib/stripe/recipient.rb +1 -1
  41. data/lib/stripe/refund.rb +1 -1
  42. data/lib/stripe/reversal.rb +7 -3
  43. data/lib/stripe/singleton_api_resource.rb +3 -3
  44. data/lib/stripe/sku.rb +2 -2
  45. data/lib/stripe/source.rb +11 -0
  46. data/lib/stripe/stripe_object.rb +167 -91
  47. data/lib/stripe/subscription.rb +15 -9
  48. data/lib/stripe/subscription_item.rb +12 -0
  49. data/lib/stripe/three_d_secure.rb +9 -0
  50. data/lib/stripe/transfer.rb +3 -4
  51. data/lib/stripe/util.rb +100 -28
  52. data/lib/stripe/version.rb +1 -1
  53. data/lib/stripe.rb +283 -140
  54. data/stripe.gemspec +5 -18
  55. data/test/stripe/account_test.rb +55 -9
  56. data/test/stripe/alipay_account_test.rb +11 -0
  57. data/test/stripe/api_operations_test.rb +31 -0
  58. data/test/stripe/api_resource_test.rb +204 -10
  59. data/test/stripe/apple_pay_domain_test.rb +34 -0
  60. data/test/stripe/application_fee_test.rb +8 -5
  61. data/test/stripe/bitcoin_receiver_test.rb +2 -2
  62. data/test/stripe/charge_refund_test.rb +12 -0
  63. data/test/stripe/charge_test.rb +32 -4
  64. data/test/stripe/country_spec_test.rb +43 -0
  65. data/test/stripe/coupon_test.rb +9 -1
  66. data/test/stripe/customer_card_test.rb +2 -2
  67. data/test/stripe/customer_test.rb +24 -1
  68. data/test/stripe/dispute_test.rb +8 -0
  69. data/test/stripe/errors_test.rb +18 -0
  70. data/test/stripe/invoice_item_test.rb +19 -0
  71. data/test/stripe/invoice_test.rb +27 -1
  72. data/test/stripe/list_object_test.rb +36 -15
  73. data/test/stripe/order_return_test.rb +25 -0
  74. data/test/stripe/order_test.rb +21 -1
  75. data/test/stripe/plan_test.rb +31 -0
  76. data/test/stripe/product_test.rb +17 -7
  77. data/test/stripe/recipient_card_test.rb +2 -2
  78. data/test/stripe/recipient_test.rb +21 -0
  79. data/test/stripe/refund_test.rb +10 -1
  80. data/test/stripe/sku_test.rb +15 -6
  81. data/test/stripe/source_test.rb +83 -0
  82. data/test/stripe/stripe_object_test.rb +180 -11
  83. data/test/stripe/subscription_item_test.rb +76 -0
  84. data/test/stripe/subscription_test.rb +161 -37
  85. data/test/stripe/three_d_secure_test.rb +22 -0
  86. data/test/stripe/transfer_test.rb +8 -0
  87. data/test/stripe/util_test.rb +48 -16
  88. data/test/stripe_test.rb +58 -0
  89. data/test/test_data.rb +337 -27
  90. data/test/test_helper.rb +7 -3
  91. metadata +47 -133
  92. data/README.rdoc +0 -68
  93. data/gemfiles/default-with-activesupport.gemfile +0 -10
  94. data/gemfiles/json.gemfile +0 -12
  95. data/gemfiles/yajl.gemfile +0 -12
  96. data/lib/stripe/api_operations/update.rb +0 -58
  97. data/lib/stripe/errors/api_connection_error.rb +0 -4
  98. data/lib/stripe/errors/api_error.rb +0 -4
  99. data/lib/stripe/errors/authentication_error.rb +0 -4
  100. data/lib/stripe/errors/card_error.rb +0 -12
  101. data/lib/stripe/errors/invalid_request_error.rb +0 -11
  102. data/lib/stripe/errors/rate_limit_error.rb +0 -4
  103. data/lib/stripe/errors/stripe_error.rb +0 -26
data/lib/stripe.rb CHANGED
@@ -14,76 +14,144 @@ require 'stripe/version'
14
14
 
15
15
  # API operations
16
16
  require 'stripe/api_operations/create'
17
- require 'stripe/api_operations/update'
17
+ require 'stripe/api_operations/save'
18
18
  require 'stripe/api_operations/delete'
19
19
  require 'stripe/api_operations/list'
20
20
  require 'stripe/api_operations/request'
21
21
 
22
- # Resources
22
+ # API resource support classes
23
+ require 'stripe/errors'
23
24
  require 'stripe/util'
24
25
  require 'stripe/stripe_object'
26
+ require 'stripe/list_object'
25
27
  require 'stripe/api_resource'
26
28
  require 'stripe/singleton_api_resource'
27
- require 'stripe/list_object'
29
+
30
+ # Named API resources
28
31
  require 'stripe/account'
32
+ require 'stripe/alipay_account'
33
+ require 'stripe/apple_pay_domain'
34
+ require 'stripe/application_fee'
35
+ require 'stripe/application_fee_refund'
29
36
  require 'stripe/balance'
30
37
  require 'stripe/balance_transaction'
38
+ require 'stripe/bank_account'
39
+ require 'stripe/bitcoin_receiver'
40
+ require 'stripe/bitcoin_transaction'
41
+ require 'stripe/card'
42
+ require 'stripe/charge'
43
+ require 'stripe/country_spec'
44
+ require 'stripe/coupon'
31
45
  require 'stripe/customer'
46
+ require 'stripe/dispute'
47
+ require 'stripe/event'
48
+ require 'stripe/file_upload'
32
49
  require 'stripe/invoice'
33
50
  require 'stripe/invoice_item'
34
- require 'stripe/charge'
51
+ require 'stripe/order'
52
+ require 'stripe/order_return'
35
53
  require 'stripe/plan'
36
- require 'stripe/file_upload'
37
- require 'stripe/coupon'
38
- require 'stripe/token'
39
- require 'stripe/event'
40
- require 'stripe/transfer'
54
+ require 'stripe/product'
41
55
  require 'stripe/recipient'
42
- require 'stripe/bank_account'
43
- require 'stripe/card'
44
- require 'stripe/subscription'
45
- require 'stripe/application_fee'
46
56
  require 'stripe/refund'
47
57
  require 'stripe/reversal'
48
- require 'stripe/application_fee_refund'
49
- require 'stripe/bitcoin_receiver'
50
- require 'stripe/bitcoin_transaction'
51
- require 'stripe/dispute'
52
- require 'stripe/product'
53
58
  require 'stripe/sku'
54
- require 'stripe/order'
55
-
56
- # Errors
57
- require 'stripe/errors/stripe_error'
58
- require 'stripe/errors/api_error'
59
- require 'stripe/errors/api_connection_error'
60
- require 'stripe/errors/card_error'
61
- require 'stripe/errors/invalid_request_error'
62
- require 'stripe/errors/authentication_error'
63
- require 'stripe/errors/rate_limit_error'
59
+ require 'stripe/source'
60
+ require 'stripe/subscription'
61
+ require 'stripe/subscription_item'
62
+ require 'stripe/three_d_secure'
63
+ require 'stripe/token'
64
+ require 'stripe/transfer'
64
65
 
65
66
  module Stripe
66
67
  DEFAULT_CA_BUNDLE_PATH = File.dirname(__FILE__) + '/data/ca-certificates.crt'
67
68
 
69
+ # Exceptions which we'd like to retry. This includes both socket errors that
70
+ # may represent an intermittent problem and some special HTTP statuses.
71
+ RETRY_EXCEPTIONS = [
72
+ # Destination refused the connection. This could occur from a single
73
+ # saturated server, so retry in case it's intermittent.
74
+ Errno::ECONNREFUSED,
75
+
76
+ # Connection reset. This occasionally occurs on a server problem, and
77
+ # deserves a retry because the server should terminate all requests
78
+ # properly even if they were invalid.
79
+ Errno::ECONNRESET,
80
+
81
+ # Timed out making the connection. It's worth retrying under this
82
+ # circumstance.
83
+ Errno::ETIMEDOUT,
84
+
85
+ # A server may respond with a 409 to indicate that there is a concurrent
86
+ # request executing with the same idempotency key. In the case that a
87
+ # request failed due to a connection problem and the client has retried too
88
+ # early, but the server is still executing the old request, we would like
89
+ # the client to continue retrying until getting a "real" response status
90
+ # back.
91
+ RestClient::Conflict,
92
+
93
+ # Retry on timeout-related problems. This shouldn't be lumped in with HTTP
94
+ # exceptions, but with RestClient it is.
95
+ RestClient::RequestTimeout,
96
+ ].freeze
97
+
68
98
  @api_base = 'https://api.stripe.com'
69
99
  @connect_base = 'https://connect.stripe.com'
70
100
  @uploads_base = 'https://uploads.stripe.com'
71
101
 
72
- @ssl_bundle_path = DEFAULT_CA_BUNDLE_PATH
102
+ @max_network_retries = 0
103
+ @max_network_retry_delay = 2
104
+ @initial_network_retry_delay = 0.5
105
+
106
+ @ca_bundle_path = DEFAULT_CA_BUNDLE_PATH
107
+ @ca_store = nil
73
108
  @verify_ssl_certs = true
74
109
 
75
110
  @open_timeout = 30
76
111
  @read_timeout = 80
77
112
 
78
113
  class << self
79
- attr_accessor :api_key, :api_base, :verify_ssl_certs, :api_version, :connect_base, :uploads_base,
114
+ attr_accessor :stripe_account, :api_key, :api_base, :verify_ssl_certs, :api_version, :connect_base, :uploads_base,
80
115
  :open_timeout, :read_timeout
116
+
117
+ attr_reader :max_network_retry_delay, :initial_network_retry_delay
81
118
  end
82
119
 
83
120
  def self.api_url(url='', api_base_url=nil)
84
121
  (api_base_url || @api_base) + url
85
122
  end
86
123
 
124
+ # The location of a file containing a bundle of CA certificates. By default
125
+ # the library will use an included bundle that can successfully validate
126
+ # Stripe certificates.
127
+ def self.ca_bundle_path
128
+ @ca_bundle_path
129
+ end
130
+
131
+ def self.ca_bundle_path=(path)
132
+ @ca_bundle_path = path
133
+
134
+ # empty this field so a new store is initialized
135
+ @ca_store = nil
136
+ end
137
+
138
+ # A certificate store initialized from the the bundle in #ca_bundle_path and
139
+ # which is used to validate TLS on every request.
140
+ #
141
+ # This was added to the give the gem "pseudo thread safety" in that it seems
142
+ # when initiating many parallel requests marshaling the certificate store is
143
+ # the most likely point of failure (see issue #382). Any program attempting
144
+ # to leverage this pseudo safety should make a call to this method (i.e.
145
+ # `Stripe.ca_store`) in their initialization code because it marshals lazily
146
+ # and is itself not thread safe.
147
+ def self.ca_store
148
+ @ca_store ||= begin
149
+ store = OpenSSL::X509::Store.new
150
+ store.add_file(ca_bundle_path)
151
+ store
152
+ end
153
+ end
154
+
87
155
  def self.request(method, url, api_key, params={}, headers={}, api_base_url=nil)
88
156
  api_base_url = api_base_url || @api_base
89
157
 
@@ -104,7 +172,7 @@ module Stripe
104
172
 
105
173
  if verify_ssl_certs
106
174
  request_opts = {:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
107
- :ssl_ca_file => @ssl_bundle_path}
175
+ :ssl_cert_store => ca_store}
108
176
  else
109
177
  request_opts = {:verify_ssl => false}
110
178
  unless @verify_ssl_warned
@@ -131,52 +199,84 @@ module Stripe
131
199
  end
132
200
  end
133
201
 
134
- request_opts.update(:headers => request_headers(api_key).update(headers),
202
+ request_opts.update(:headers => request_headers(api_key, method).update(headers),
135
203
  :method => method, :open_timeout => open_timeout,
136
204
  :payload => payload, :url => url, :timeout => read_timeout)
137
205
 
206
+ response = execute_request_with_rescues(request_opts, api_base_url)
207
+
208
+ [parse(response), api_key]
209
+ end
210
+
211
+ def self.max_network_retries
212
+ @max_network_retries
213
+ end
214
+
215
+ def self.max_network_retries=(val)
216
+ @max_network_retries = val.to_i
217
+ end
218
+
219
+ private
220
+
221
+ def self.api_error(error, resp, error_obj)
222
+ APIError.new(error[:message], resp.code, resp.body, error_obj, resp.headers)
223
+ end
224
+
225
+ def self.authentication_error(error, resp, error_obj)
226
+ AuthenticationError.new(error[:message], resp.code, resp.body, error_obj,
227
+ resp.headers)
228
+ end
229
+
230
+ def self.card_error(error, resp, error_obj)
231
+ CardError.new(error[:message], error[:param], error[:code],
232
+ resp.code, resp.body, error_obj, resp.headers)
233
+ end
234
+
235
+ def self.execute_request(opts)
236
+ RestClient::Request.execute(opts)
237
+ end
238
+
239
+ def self.execute_request_with_rescues(request_opts, api_base_url, retry_count = 0)
138
240
  begin
139
241
  response = execute_request(request_opts)
140
- rescue SocketError => e
141
- handle_restclient_error(e, api_base_url)
142
- rescue NoMethodError => e
143
- # Work around RestClient bug
144
- if e.message =~ /\WRequestFailed\W/
145
- e = APIConnectionError.new('Unexpected HTTP response code')
146
- handle_restclient_error(e, api_base_url)
147
- else
148
- raise
242
+
243
+ # We rescue all exceptions from a request so that we have an easy spot to
244
+ # implement our retry logic across the board. We'll re-raise if it's a type
245
+ # of exception that we didn't expect to handle.
246
+ rescue => e
247
+ if should_retry?(e, retry_count)
248
+ retry_count = retry_count + 1
249
+ sleep sleep_time(retry_count)
250
+ retry
149
251
  end
150
- rescue RestClient::ExceptionWithResponse => e
151
- if e.response
152
- handle_api_error(e.response)
252
+
253
+ case e
254
+ when SocketError
255
+ response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
256
+
257
+ when RestClient::ExceptionWithResponse
258
+ if e.response
259
+ handle_api_error(e.response)
260
+ else
261
+ response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
262
+ end
263
+
264
+ when RestClient::Exception, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError
265
+ response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
266
+
267
+ # Only handle errors when we know we can do so, and re-raise otherwise.
268
+ # This should be pretty infrequent.
153
269
  else
154
- handle_restclient_error(e, api_base_url)
270
+ raise
155
271
  end
156
- rescue RestClient::Exception, Errno::ECONNREFUSED => e
157
- handle_restclient_error(e, api_base_url)
158
272
  end
159
273
 
160
- [parse(response), api_key]
274
+ response
161
275
  end
162
276
 
163
- private
164
-
165
- def self.user_agent
166
- @uname ||= get_uname
167
- lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
168
-
169
- {
170
- :bindings_version => Stripe::VERSION,
171
- :lang => 'ruby',
172
- :lang_version => lang_version,
173
- :platform => RUBY_PLATFORM,
174
- :engine => defined?(RUBY_ENGINE) ? RUBY_ENGINE : '',
175
- :publisher => 'stripe',
176
- :uname => @uname,
177
- :hostname => Socket.gethostname,
178
- }
179
-
277
+ def self.general_api_error(rcode, rbody)
278
+ APIError.new("Invalid response object from API: #{rbody.inspect} " +
279
+ "(HTTP response code was #{rcode})", rcode, rbody)
180
280
  end
181
281
 
182
282
  def self.get_uname
@@ -185,74 +285,31 @@ module Stripe
185
285
  else
186
286
  case RbConfig::CONFIG['host_os']
187
287
  when /linux|darwin|bsd|sunos|solaris|cygwin/i
188
- _uname_uname
288
+ get_uname_from_system
189
289
  when /mswin|mingw/i
190
- _uname_ver
290
+ get_uname_from_system_ver
191
291
  else
192
292
  "unknown platform"
193
293
  end
194
294
  end
195
295
  end
196
296
 
197
- def self._uname_uname
297
+ def self.get_uname_from_system
198
298
  (`uname -a 2>/dev/null` || '').strip
299
+ rescue Errno::ENOENT
300
+ "uname executable not found"
199
301
  rescue Errno::ENOMEM # couldn't create subprocess
200
302
  "uname lookup failed"
201
303
  end
202
304
 
203
- def self._uname_ver
305
+ def self.get_uname_from_system_ver
204
306
  (`ver` || '').strip
307
+ rescue Errno::ENOENT
308
+ "ver executable not found"
205
309
  rescue Errno::ENOMEM # couldn't create subprocess
206
310
  "uname lookup failed"
207
311
  end
208
312
 
209
- # DEPRECATED. Use `Util#encode_parameters` instead.
210
- def self.uri_encode(params)
211
- Util.encode_parameters(params)
212
- end
213
- class << self
214
- extend Gem::Deprecate
215
- deprecate :uri_encode, "Stripe::Util#encode_parameters", 2016, 01
216
- end
217
-
218
- def self.request_headers(api_key)
219
- headers = {
220
- :user_agent => "Stripe/v1 RubyBindings/#{Stripe::VERSION}",
221
- :authorization => "Bearer #{api_key}",
222
- :content_type => 'application/x-www-form-urlencoded'
223
- }
224
-
225
- headers[:stripe_version] = api_version if api_version
226
-
227
- begin
228
- headers.update(:x_stripe_client_user_agent => JSON.generate(user_agent))
229
- rescue => e
230
- headers.update(:x_stripe_client_raw_user_agent => user_agent.inspect,
231
- :error => "#{e} (#{e.class})")
232
- end
233
- end
234
-
235
- def self.execute_request(opts)
236
- RestClient::Request.execute(opts)
237
- end
238
-
239
- def self.parse(response)
240
- begin
241
- # Would use :symbolize_names => true, but apparently there is
242
- # some library out there that makes symbolize_names not work.
243
- response = JSON.parse(response.body)
244
- rescue JSON::ParserError
245
- raise general_api_error(response.code, response.body)
246
- end
247
-
248
- Util.symbolize_names(response)
249
- end
250
-
251
- def self.general_api_error(rcode, rbody)
252
- APIError.new("Invalid response object from API: #{rbody.inspect} " +
253
- "(HTTP response code was #{rcode})", rcode, rbody)
254
- end
255
-
256
313
  def self.handle_api_error(resp)
257
314
  begin
258
315
  error_obj = JSON.parse(resp.body)
@@ -271,6 +328,8 @@ module Stripe
271
328
  raise authentication_error(error, resp, error_obj)
272
329
  when 402
273
330
  raise card_error(error, resp, error_obj)
331
+ when 403
332
+ raise permission_error(error, resp, error_obj)
274
333
  when 429
275
334
  raise rate_limit_error(error, resp, error_obj)
276
335
  else
@@ -279,31 +338,8 @@ module Stripe
279
338
 
280
339
  end
281
340
 
282
- def self.invalid_request_error(error, resp, error_obj)
283
- InvalidRequestError.new(error[:message], error[:param], resp.code,
284
- resp.body, error_obj, resp.headers)
285
- end
286
-
287
- def self.authentication_error(error, resp, error_obj)
288
- AuthenticationError.new(error[:message], resp.code, resp.body, error_obj,
289
- resp.headers)
290
- end
291
-
292
- def self.rate_limit_error(error, resp, error_obj)
293
- RateLimitError.new(error[:message], resp.code, resp.body, error_obj,
294
- resp.headers)
295
- end
296
-
297
- def self.card_error(error, resp, error_obj)
298
- CardError.new(error[:message], error[:param], error[:code],
299
- resp.code, resp.body, error_obj, resp.headers)
300
- end
301
-
302
- def self.api_error(error, resp, error_obj)
303
- APIError.new(error[:message], resp.code, resp.body, error_obj, resp.headers)
304
- end
341
+ def self.handle_restclient_error(e, request_opts, retry_count, api_base_url=nil)
305
342
 
306
- def self.handle_restclient_error(e, api_base_url=nil)
307
343
  api_base_url = @api_base unless api_base_url
308
344
  connection_message = "Please check your internet connection and try again. " \
309
345
  "If this problem persists, you should check Stripe's service status at " \
@@ -317,6 +353,12 @@ module Stripe
317
353
  message = "The connection to the server (#{api_base_url}) broke before the " \
318
354
  "request completed. #{connection_message}"
319
355
 
356
+ when OpenSSL::SSL::SSLError
357
+ message = "Could not establish a secure connection to Stripe, you may " \
358
+ "need to upgrade your OpenSSL version. To check, try running " \
359
+ "'openssl s_client -connect api.stripe.com:443' from the " \
360
+ "command line."
361
+
320
362
  when RestClient::SSLCertificateNotVerified
321
363
  message = "Could not verify Stripe's SSL certificate. " \
322
364
  "Please make sure that your network is not intercepting certificates. " \
@@ -334,6 +376,107 @@ module Stripe
334
376
 
335
377
  end
336
378
 
379
+ if retry_count > 0
380
+ message += " Request was retried #{retry_count} times."
381
+ end
382
+
337
383
  raise APIConnectionError.new(message + "\n\n(Network error: #{e.message})")
338
384
  end
385
+
386
+ def self.invalid_request_error(error, resp, error_obj)
387
+ InvalidRequestError.new(error[:message], error[:param], resp.code,
388
+ resp.body, error_obj, resp.headers)
389
+ end
390
+
391
+ def self.parse(response)
392
+ begin
393
+ # Would use :symbolize_names => true, but apparently there is
394
+ # some library out there that makes symbolize_names not work.
395
+ response = JSON.parse(response.body)
396
+ rescue JSON::ParserError
397
+ raise general_api_error(response.code, response.body)
398
+ end
399
+
400
+ Util.symbolize_names(response)
401
+ end
402
+
403
+ def self.permission_error(error, resp, error_obj)
404
+ PermissionError.new(error[:message], resp.code, resp.body, error_obj, resp.headers)
405
+ end
406
+
407
+ def self.rate_limit_error(error, resp, error_obj)
408
+ RateLimitError.new(error[:message], resp.code, resp.body, error_obj,
409
+ resp.headers)
410
+ end
411
+
412
+ def self.request_headers(api_key, method)
413
+ headers = {
414
+ 'User-Agent' => "Stripe/v1 RubyBindings/#{Stripe::VERSION}",
415
+ 'Authorization' => "Bearer #{api_key}",
416
+ 'Content-Type' => 'application/x-www-form-urlencoded'
417
+ }
418
+
419
+ # It is only safe to retry network failures on post and delete
420
+ # requests if we add an Idempotency-Key header
421
+ if [:post, :delete].include?(method) && self.max_network_retries > 0
422
+ headers['Idempotency-Key'] ||= SecureRandom.uuid
423
+ end
424
+
425
+ headers['Stripe-Version'] = api_version if api_version
426
+ headers['Stripe-Account'] = stripe_account if stripe_account
427
+
428
+ begin
429
+ headers.update('X-Stripe-Client-User-Agent' => JSON.generate(user_agent))
430
+ rescue => e
431
+ headers.update('X-Stripe-Client-Raw-User-Agent' => user_agent.inspect,
432
+ :error => "#{e} (#{e.class})")
433
+ end
434
+ end
435
+
436
+ def self.should_retry?(e, retry_count)
437
+ retry_count < self.max_network_retries &&
438
+ RETRY_EXCEPTIONS.any? { |klass| e.is_a?(klass) }
439
+ end
440
+
441
+ def self.sleep_time(retry_count)
442
+ # Apply exponential backoff with initial_network_retry_delay on the number
443
+ # of attempts so far as inputs. Do not allow the number to exceed
444
+ # max_network_retry_delay.
445
+ sleep_seconds = [initial_network_retry_delay * (2 ** (retry_count - 1)), max_network_retry_delay].min
446
+
447
+ # Apply some jitter by randomizing the value in the range of (sleep_seconds
448
+ # / 2) to (sleep_seconds).
449
+ sleep_seconds = sleep_seconds * (0.5 * (1 + rand()))
450
+
451
+ # But never sleep less than the base sleep seconds.
452
+ sleep_seconds = [initial_network_retry_delay, sleep_seconds].max
453
+
454
+ sleep_seconds
455
+ end
456
+
457
+ # DEPRECATED. Use `Util#encode_parameters` instead.
458
+ def self.uri_encode(params)
459
+ Util.encode_parameters(params)
460
+ end
461
+ class << self
462
+ extend Gem::Deprecate
463
+ deprecate :uri_encode, "Stripe::Util#encode_parameters", 2016, 01
464
+ end
465
+
466
+ def self.user_agent
467
+ @uname ||= get_uname
468
+ lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
469
+
470
+ {
471
+ :bindings_version => Stripe::VERSION,
472
+ :lang => 'ruby',
473
+ :lang_version => lang_version,
474
+ :platform => RUBY_PLATFORM,
475
+ :engine => defined?(RUBY_ENGINE) ? RUBY_ENGINE : '',
476
+ :publisher => 'stripe',
477
+ :uname => @uname,
478
+ :hostname => Socket.gethostname,
479
+ }
480
+
481
+ end
339
482
  end
data/stripe.gemspec CHANGED
@@ -5,28 +5,15 @@ require 'stripe/version'
5
5
  spec = Gem::Specification.new do |s|
6
6
  s.name = 'stripe'
7
7
  s.version = Stripe::VERSION
8
+ s.required_ruby_version = '>= 1.9.3'
8
9
  s.summary = 'Ruby bindings for the Stripe API'
9
10
  s.description = 'Stripe is the easiest way to accept payments online. See https://stripe.com for details.'
10
- s.authors = ['Ross Boucher', 'Greg Brockman']
11
- s.email = ['boucher@stripe.com', 'gdb@stripe.com']
12
- s.homepage = 'https://stripe.com/api'
11
+ s.author = 'Stripe'
12
+ s.email = 'support@stripe.com'
13
+ s.homepage = 'https://stripe.com/docs/api/ruby'
13
14
  s.license = 'MIT'
14
15
 
15
- s.add_dependency('rest-client', '~> 1.4')
16
- s.add_dependency('json', '~> 1.8.1')
17
-
18
- s.add_development_dependency('mocha', '~> 0.13.2')
19
- s.add_development_dependency('shoulda', '~> 3.4.0')
20
- s.add_development_dependency('test-unit')
21
- s.add_development_dependency('rake')
22
-
23
- # to avoid problems, bring Byebug in on just versions of Ruby under which
24
- # it's known to work well
25
- if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.0.0')
26
- s.add_development_dependency("byebug")
27
- s.add_development_dependency("pry")
28
- s.add_development_dependency("pry-byebug")
29
- end
16
+ s.add_dependency('rest-client', '>= 1.4', '< 4.0')
30
17
 
31
18
  s.files = `git ls-files`.split("\n")
32
19
  s.test_files = `git ls-files -- test/*`.split("\n")