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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE.md +5 -0
- data/.travis.yml +2 -12
- data/Gemfile +29 -4
- data/History.txt +168 -0
- data/README.md +134 -0
- data/Rakefile +10 -0
- data/VERSION +1 -1
- data/bin/stripe-console +12 -5
- data/lib/data/ca-certificates.crt +3868 -5114
- data/lib/stripe/account.rb +41 -21
- data/lib/stripe/alipay_account.rb +20 -0
- 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 +1 -2
- data/lib/stripe/api_operations/save.rb +87 -0
- data/lib/stripe/api_resource.rb +37 -4
- data/lib/stripe/apple_pay_domain.rb +12 -0
- data/lib/stripe/application_fee.rb +8 -8
- data/lib/stripe/application_fee_refund.rb +7 -3
- data/lib/stripe/balance_transaction.rb +1 -1
- data/lib/stripe/bank_account.rb +9 -5
- data/lib/stripe/bitcoin_receiver.rb +6 -6
- data/lib/stripe/bitcoin_transaction.rb +1 -1
- data/lib/stripe/card.rb +9 -5
- data/lib/stripe/charge.rb +30 -12
- data/lib/stripe/country_spec.rb +9 -0
- data/lib/stripe/coupon.rb +1 -1
- data/lib/stripe/customer.rb +6 -4
- data/lib/stripe/dispute.rb +2 -2
- data/lib/stripe/errors.rb +82 -0
- data/lib/stripe/file_upload.rb +1 -1
- data/lib/stripe/invoice.rb +3 -3
- data/lib/stripe/invoice_item.rb +1 -1
- data/lib/stripe/list_object.rb +7 -6
- data/lib/stripe/order.rb +10 -2
- data/lib/stripe/order_return.rb +9 -0
- data/lib/stripe/plan.rb +1 -1
- data/lib/stripe/product.rb +2 -10
- data/lib/stripe/recipient.rb +1 -1
- data/lib/stripe/refund.rb +1 -1
- data/lib/stripe/reversal.rb +7 -3
- data/lib/stripe/singleton_api_resource.rb +3 -3
- data/lib/stripe/sku.rb +2 -2
- data/lib/stripe/source.rb +11 -0
- data/lib/stripe/stripe_object.rb +167 -91
- data/lib/stripe/subscription.rb +15 -9
- data/lib/stripe/subscription_item.rb +12 -0
- data/lib/stripe/three_d_secure.rb +9 -0
- data/lib/stripe/transfer.rb +3 -4
- data/lib/stripe/util.rb +100 -28
- data/lib/stripe/version.rb +1 -1
- data/lib/stripe.rb +283 -140
- data/stripe.gemspec +5 -18
- data/test/stripe/account_test.rb +55 -9
- data/test/stripe/alipay_account_test.rb +11 -0
- data/test/stripe/api_operations_test.rb +31 -0
- data/test/stripe/api_resource_test.rb +204 -10
- data/test/stripe/apple_pay_domain_test.rb +34 -0
- data/test/stripe/application_fee_test.rb +8 -5
- data/test/stripe/bitcoin_receiver_test.rb +2 -2
- data/test/stripe/charge_refund_test.rb +12 -0
- data/test/stripe/charge_test.rb +32 -4
- data/test/stripe/country_spec_test.rb +43 -0
- data/test/stripe/coupon_test.rb +9 -1
- data/test/stripe/customer_card_test.rb +2 -2
- data/test/stripe/customer_test.rb +24 -1
- data/test/stripe/dispute_test.rb +8 -0
- data/test/stripe/errors_test.rb +18 -0
- data/test/stripe/invoice_item_test.rb +19 -0
- data/test/stripe/invoice_test.rb +27 -1
- data/test/stripe/list_object_test.rb +36 -15
- data/test/stripe/order_return_test.rb +25 -0
- data/test/stripe/order_test.rb +21 -1
- data/test/stripe/plan_test.rb +31 -0
- data/test/stripe/product_test.rb +17 -7
- data/test/stripe/recipient_card_test.rb +2 -2
- data/test/stripe/recipient_test.rb +21 -0
- data/test/stripe/refund_test.rb +10 -1
- data/test/stripe/sku_test.rb +15 -6
- data/test/stripe/source_test.rb +83 -0
- data/test/stripe/stripe_object_test.rb +180 -11
- data/test/stripe/subscription_item_test.rb +76 -0
- data/test/stripe/subscription_test.rb +161 -37
- data/test/stripe/three_d_secure_test.rb +22 -0
- data/test/stripe/transfer_test.rb +8 -0
- data/test/stripe/util_test.rb +48 -16
- data/test/stripe_test.rb +58 -0
- data/test/test_data.rb +337 -27
- data/test/test_helper.rb +7 -3
- metadata +47 -133
- data/README.rdoc +0 -68
- data/gemfiles/default-with-activesupport.gemfile +0 -10
- data/gemfiles/json.gemfile +0 -12
- data/gemfiles/yajl.gemfile +0 -12
- data/lib/stripe/api_operations/update.rb +0 -58
- data/lib/stripe/errors/api_connection_error.rb +0 -4
- data/lib/stripe/errors/api_error.rb +0 -4
- data/lib/stripe/errors/authentication_error.rb +0 -4
- data/lib/stripe/errors/card_error.rb +0 -12
- data/lib/stripe/errors/invalid_request_error.rb +0 -11
- data/lib/stripe/errors/rate_limit_error.rb +0 -4
- 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/
|
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
|
-
#
|
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
|
-
|
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/
|
51
|
+
require 'stripe/order'
|
52
|
+
require 'stripe/order_return'
|
35
53
|
require 'stripe/plan'
|
36
|
-
require 'stripe/
|
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/
|
55
|
-
|
56
|
-
|
57
|
-
require 'stripe/
|
58
|
-
require 'stripe/
|
59
|
-
require 'stripe/
|
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
|
-
@
|
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
|
-
:
|
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
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
151
|
-
|
152
|
-
|
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
|
-
|
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
|
-
|
274
|
+
response
|
161
275
|
end
|
162
276
|
|
163
|
-
|
164
|
-
|
165
|
-
|
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
|
-
|
288
|
+
get_uname_from_system
|
189
289
|
when /mswin|mingw/i
|
190
|
-
|
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.
|
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.
|
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.
|
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.
|
11
|
-
s.email =
|
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', '
|
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")
|