stripe 3.0.3 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +23 -0
- data/Gemfile +1 -2
- data/History.txt +4 -0
- data/README.md +28 -3
- data/Rakefile +0 -12
- data/VERSION +1 -1
- data/lib/stripe.rb +24 -0
- data/lib/stripe/stripe_client.rb +166 -19
- data/lib/stripe/util.rb +127 -8
- data/lib/stripe/version.rb +1 -1
- data/test/api_stub_helpers.rb +0 -125
- data/test/stripe/account_test.rb +11 -13
- data/test/stripe/alipay_account_test.rb +2 -4
- data/test/stripe/api_resource_test.rb +112 -76
- data/test/stripe/apple_pay_domain_test.rb +4 -6
- data/test/stripe/application_fee_refund_test.rb +2 -5
- data/test/stripe/application_fee_test.rb +0 -2
- data/test/stripe/bank_account_test.rb +8 -13
- data/test/stripe/bitcoin_receiver_test.rb +13 -16
- data/test/stripe/bitcoin_transaction_test.rb +2 -4
- data/test/stripe/charge_test.rb +9 -11
- data/test/stripe/country_spec_test.rb +2 -4
- data/test/stripe/coupon_test.rb +6 -8
- data/test/stripe/customer_card_test.rb +13 -9
- data/test/stripe/customer_test.rb +16 -18
- data/test/stripe/dispute_test.rb +8 -10
- data/test/stripe/file_upload_test.rb +3 -3
- data/test/stripe/invoice_item_test.rb +9 -11
- data/test/stripe/invoice_line_item_test.rb +0 -1
- data/test/stripe/invoice_test.rb +39 -21
- data/test/stripe/list_object_test.rb +1 -1
- data/test/stripe/login_link_test.rb +6 -7
- data/test/stripe/order_return_test.rb +2 -4
- data/test/stripe/order_test.rb +10 -12
- data/test/stripe/payout_test.rb +7 -9
- data/test/stripe/plan_test.rb +8 -10
- data/test/stripe/product_test.rb +8 -10
- data/test/stripe/recipient_card_test.rb +13 -9
- data/test/stripe/recipient_test.rb +8 -10
- data/test/stripe/refund_test.rb +7 -9
- data/test/stripe/reversal_test.rb +5 -7
- data/test/stripe/sku_test.rb +9 -11
- data/test/stripe/source_test.rb +16 -15
- data/test/stripe/stripe_client_test.rb +190 -26
- data/test/stripe/subscription_item_test.rb +12 -14
- data/test/stripe/subscription_test.rb +10 -12
- data/test/stripe/three_d_secure_test.rb +3 -5
- data/test/stripe/transfer_test.rb +7 -9
- data/test/stripe/util_test.rb +164 -0
- data/test/test_helper.rb +33 -18
- metadata +2 -8
- data/openapi/fixtures.json +0 -1896
- data/openapi/fixtures.yaml +0 -1505
- data/openapi/spec2.json +0 -24601
- data/openapi/spec2.yaml +0 -18801
- data/test/api_fixtures.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e0d93e7805ba65b7f5dcfe753298a8be427fc1d
|
4
|
+
data.tar.gz: 3a59dcb60834e656c55f37368c8f957bb9d23486
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a769766ed97097ae745300710ad092bc6646ec85d98f70686ffe71758159667e67f7758c6550a00ac74269978562a2dcf44162bc681960fb59c67ffbc04a525
|
7
|
+
data.tar.gz: 2c6782650e0069887f14fa28be69e135f2e4ccaeec68ce69422972cde7e8fc28e2310de86c903c88ade3017998cb9a11a68c5ffc921bf6098deb7877de5bd85e
|
data/.travis.yml
CHANGED
@@ -13,3 +13,26 @@ notifications:
|
|
13
13
|
on_success: never
|
14
14
|
|
15
15
|
sudo: false
|
16
|
+
|
17
|
+
env:
|
18
|
+
global:
|
19
|
+
- STRIPE_MOCK_VERSION=0.1.12
|
20
|
+
|
21
|
+
cache:
|
22
|
+
directories:
|
23
|
+
- stripe-mock
|
24
|
+
|
25
|
+
before_install:
|
26
|
+
# Unpack and start stripe-mock so that the test suite can talk to it
|
27
|
+
- |
|
28
|
+
if [ ! -d "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}" ]; then
|
29
|
+
mkdir -p stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/
|
30
|
+
curl -L "https://github.com/stripe/stripe-mock/releases/download/v${STRIPE_MOCK_VERSION}/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz" -o "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz"
|
31
|
+
tar -zxf "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz" -C "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/"
|
32
|
+
fi
|
33
|
+
- |
|
34
|
+
stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/stripe-mock > /dev/null &
|
35
|
+
STRIPE_MOCK_PID=$!
|
36
|
+
|
37
|
+
script:
|
38
|
+
- bundle exec rake
|
data/Gemfile
CHANGED
@@ -3,12 +3,11 @@ source "https://rubygems.org"
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
group :development do
|
6
|
-
gem 'committee', '2.0.0.pre6'
|
7
6
|
gem 'mocha', '~> 0.13.2'
|
8
7
|
gem 'rake'
|
9
8
|
gem 'shoulda-context'
|
10
|
-
gem 'sinatra'
|
11
9
|
gem 'test-unit'
|
10
|
+
gem 'timecop'
|
12
11
|
gem 'webmock'
|
13
12
|
|
14
13
|
# Rack 2.0+ requires Ruby >= 2.2.2 which is problematic for the test suite on
|
data/History.txt
CHANGED
data/README.md
CHANGED
@@ -135,6 +135,24 @@ Please take care to set conservative read timeouts. Some API requests can take
|
|
135
135
|
some time, and a short timeout increases the likelihood of a problem within our
|
136
136
|
servers.
|
137
137
|
|
138
|
+
### Logging
|
139
|
+
|
140
|
+
The library can be configured to emit logging that will give you better insight
|
141
|
+
into what it's doing. The `info` logging level is usually most appropriate for
|
142
|
+
production use, but `debug` is also available for more verbosity.
|
143
|
+
|
144
|
+
There are a few options for enabling it:
|
145
|
+
|
146
|
+
1. Set the environment variable `STRIPE_LOG` to the value `debug` or `info`:
|
147
|
+
```
|
148
|
+
$ export STRIPE_LOG=info
|
149
|
+
```
|
150
|
+
|
151
|
+
2. Set `Stripe.log_level`:
|
152
|
+
``` ruby
|
153
|
+
Stripe.log_level = "info"
|
154
|
+
```
|
155
|
+
|
138
156
|
### Writing a Plugin
|
139
157
|
|
140
158
|
If you're writing a plugin that uses the library, we'd appreciate it if you
|
@@ -147,6 +165,13 @@ API.
|
|
147
165
|
|
148
166
|
## Development
|
149
167
|
|
168
|
+
The test suite depends on [stripe-mock], so make sure to fetch and run it from a
|
169
|
+
background terminal ([stripe-mock's README][stripe-mock] also contains
|
170
|
+
instructions for installing via Homebrew and other methods):
|
171
|
+
|
172
|
+
go get -u github.com/stripe/stripe-mock
|
173
|
+
stripe-mock
|
174
|
+
|
150
175
|
Run all tests:
|
151
176
|
|
152
177
|
bundle exec rake
|
@@ -163,15 +188,15 @@ Update bundled CA certificates from the [Mozilla cURL release][curl]:
|
|
163
188
|
|
164
189
|
bundle exec rake update_certs
|
165
190
|
|
166
|
-
Update bundled
|
167
|
-
|
168
|
-
bundle exec rake update_openapi
|
191
|
+
Update the bundled [stripe-mock] by editing the version number found in
|
192
|
+
`.travis.yml`.
|
169
193
|
|
170
194
|
[api-keys]: https://dashboard.stripe.com/account/apikeys
|
171
195
|
[connect]: https://stripe.com/connect
|
172
196
|
[curl]: http://curl.haxx.se/docs/caextract.html
|
173
197
|
[faraday]: https://github.com/lostisland/faraday
|
174
198
|
[idempotency-keys]: https://stripe.com/docs/api/ruby#idempotent_requests
|
199
|
+
[stripe-mock]: https://github.com/stripe/stripe-mock
|
175
200
|
|
176
201
|
<!--
|
177
202
|
# vim: set tw=79:
|
data/Rakefile
CHANGED
@@ -14,18 +14,6 @@ task :update_certs do
|
|
14
14
|
File.expand_path("../lib/data/ca-certificates.crt", __FILE__)
|
15
15
|
end
|
16
16
|
|
17
|
-
desc "Update OpenAPI specification"
|
18
|
-
task :update_openapi do
|
19
|
-
require "faraday"
|
20
|
-
|
21
|
-
["fixtures.json", "fixtures.yaml", "spec2.json", "spec2.yaml"].map { |file|
|
22
|
-
Thread.new do
|
23
|
-
fetch_file "https://raw.githubusercontent.com/stripe/openapi/master/openapi/#{file}",
|
24
|
-
File.expand_path("../openapi/#{file}", __FILE__)
|
25
|
-
end
|
26
|
-
}.map { |t| t.join }
|
27
|
-
end
|
28
|
-
|
29
17
|
#
|
30
18
|
# helpers
|
31
19
|
#
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.0
|
1
|
+
3.1.0
|
data/lib/stripe.rb
CHANGED
@@ -83,6 +83,8 @@ module Stripe
|
|
83
83
|
@connect_base = 'https://connect.stripe.com'
|
84
84
|
@uploads_base = 'https://uploads.stripe.com'
|
85
85
|
|
86
|
+
@log_level = nil
|
87
|
+
|
86
88
|
@max_network_retries = 0
|
87
89
|
@max_network_retry_delay = 2
|
88
90
|
@initial_network_retry_delay = 0.5
|
@@ -142,6 +144,24 @@ module Stripe
|
|
142
144
|
end
|
143
145
|
end
|
144
146
|
|
147
|
+
LEVEL_DEBUG = "debug"
|
148
|
+
LEVEL_INFO = "info"
|
149
|
+
|
150
|
+
# When set prompts the library to log some extra information to $stdout about
|
151
|
+
# what it's doing. For example, it'll produce information about requests,
|
152
|
+
# responses, and errors that are received. Valid log levels are `debug` and
|
153
|
+
# `info`, with `debug` being a little more verbose in places.
|
154
|
+
def self.log_level
|
155
|
+
@log_level
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.log_level=(val)
|
159
|
+
if val != nil && ![LEVEL_DEBUG, LEVEL_INFO].include?(val)
|
160
|
+
raise ArgumentError, "log_level should only be set to `nil`, `debug` or `info`"
|
161
|
+
end
|
162
|
+
@log_level = val
|
163
|
+
end
|
164
|
+
|
145
165
|
def self.max_network_retries
|
146
166
|
@max_network_retries
|
147
167
|
end
|
@@ -174,3 +194,7 @@ module Stripe
|
|
174
194
|
deprecate :uri_encode, "Stripe::Util#encode_parameters", 2016, 01
|
175
195
|
end
|
176
196
|
end
|
197
|
+
|
198
|
+
unless ENV["STRIPE_LOG"].nil?
|
199
|
+
Stripe.log_level = ENV["STRIPE_LOG"]
|
200
|
+
end
|
data/lib/stripe/stripe_client.rb
CHANGED
@@ -109,7 +109,7 @@ module Stripe
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
def execute_request(method,
|
112
|
+
def execute_request(method, path,
|
113
113
|
api_base: nil, api_key: nil, headers: {}, params: {})
|
114
114
|
|
115
115
|
api_base ||= Stripe.api_base
|
@@ -118,7 +118,7 @@ module Stripe
|
|
118
118
|
check_api_key!(api_key)
|
119
119
|
|
120
120
|
params = Util.objects_to_ids(params)
|
121
|
-
url = api_url(
|
121
|
+
url = api_url(path, api_base)
|
122
122
|
|
123
123
|
case method.to_s.downcase.to_sym
|
124
124
|
when :get, :head, :delete
|
@@ -133,14 +133,22 @@ module Stripe
|
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
136
|
+
headers = request_headers(api_key, method).
|
137
|
+
update(Util.normalize_headers(headers))
|
138
|
+
|
139
|
+
# stores information on the request we're about to make so that we don't
|
140
|
+
# have to pass as many parameters around for logging.
|
141
|
+
context = RequestLogContext.new(
|
142
|
+
api_key: api_key,
|
143
|
+
api_version: headers["Stripe-Version"] || Stripe.api_version,
|
144
|
+
idempotency_key: headers["Idempotency-Key"],
|
145
|
+
method: method,
|
146
|
+
path: path,
|
147
|
+
payload: payload,
|
148
|
+
)
|
149
|
+
|
150
|
+
http_resp = execute_request_with_rescues(api_base, context) do
|
151
|
+
conn.run_request(method, url, payload, headers) do |req|
|
144
152
|
req.options.open_timeout = Stripe.open_timeout
|
145
153
|
req.options.timeout = Stripe.read_timeout
|
146
154
|
end
|
@@ -180,14 +188,31 @@ module Stripe
|
|
180
188
|
end
|
181
189
|
end
|
182
190
|
|
183
|
-
def execute_request_with_rescues(api_base,
|
191
|
+
def execute_request_with_rescues(api_base, context, &block)
|
192
|
+
retry_count = 0
|
184
193
|
begin
|
194
|
+
request_start = Time.now
|
195
|
+
log_request(context)
|
185
196
|
resp = block.call
|
197
|
+
context = context.dup_from_response(resp)
|
198
|
+
log_response(context, request_start, resp.status, resp.body)
|
186
199
|
|
187
200
|
# We rescue all exceptions from a request so that we have an easy spot to
|
188
201
|
# implement our retry logic across the board. We'll re-raise if it's a type
|
189
202
|
# of exception that we didn't expect to handle.
|
190
203
|
rescue => e
|
204
|
+
# If we modify context we copy it into a new variable so as not to
|
205
|
+
# taint the original on a retry.
|
206
|
+
error_context = context
|
207
|
+
|
208
|
+
if e.respond_to?(:response) && e.response
|
209
|
+
error_context = context.dup_from_response(e.response)
|
210
|
+
log_response(error_context, request_start,
|
211
|
+
e.response[:status], e.response[:body])
|
212
|
+
else
|
213
|
+
log_response_error(error_context, request_start, e)
|
214
|
+
end
|
215
|
+
|
191
216
|
if self.class.should_retry?(e, retry_count)
|
192
217
|
retry_count = retry_count + 1
|
193
218
|
sleep self.class.sleep_time(retry_count)
|
@@ -197,9 +222,9 @@ module Stripe
|
|
197
222
|
case e
|
198
223
|
when Faraday::ClientError
|
199
224
|
if e.response
|
200
|
-
handle_error_response(e.response)
|
225
|
+
handle_error_response(e.response, error_context)
|
201
226
|
else
|
202
|
-
handle_network_error(e, retry_count, api_base)
|
227
|
+
handle_network_error(e, error_context, retry_count, api_base)
|
203
228
|
end
|
204
229
|
|
205
230
|
# Only handle errors when we know we can do so, and re-raise otherwise.
|
@@ -229,7 +254,7 @@ module Stripe
|
|
229
254
|
str
|
230
255
|
end
|
231
256
|
|
232
|
-
def handle_error_response(http_resp)
|
257
|
+
def handle_error_response(http_resp, context)
|
233
258
|
begin
|
234
259
|
resp = StripeResponse.from_faraday_hash(http_resp)
|
235
260
|
error_data = resp.data[:error]
|
@@ -243,16 +268,26 @@ module Stripe
|
|
243
268
|
end
|
244
269
|
|
245
270
|
if error_data.is_a?(String)
|
246
|
-
error = specific_oauth_error(resp, error_data)
|
271
|
+
error = specific_oauth_error(resp, error_data, context)
|
247
272
|
else
|
248
|
-
error = specific_api_error(resp, error_data)
|
273
|
+
error = specific_api_error(resp, error_data, context)
|
249
274
|
end
|
250
275
|
|
251
276
|
error.response = resp
|
252
277
|
raise(error)
|
253
278
|
end
|
254
279
|
|
255
|
-
def specific_api_error(resp, error_data)
|
280
|
+
def specific_api_error(resp, error_data, context)
|
281
|
+
Util.log_info('Stripe API error',
|
282
|
+
status: resp.http_status,
|
283
|
+
error_code: error_data['code'],
|
284
|
+
error_message: error_data['message'],
|
285
|
+
error_param: error_data['param'],
|
286
|
+
error_type: error_data['type'],
|
287
|
+
idempotency_key: context.idempotency_key,
|
288
|
+
request_id: context.request_id
|
289
|
+
)
|
290
|
+
|
256
291
|
case resp.http_status
|
257
292
|
when 400, 404
|
258
293
|
error = InvalidRequestError.new(
|
@@ -297,9 +332,17 @@ module Stripe
|
|
297
332
|
|
298
333
|
# Attempts to look at a response's error code and return an OAuth error if
|
299
334
|
# one matches. Will return `nil` if the code isn't recognized.
|
300
|
-
def specific_oauth_error(resp, error_code)
|
335
|
+
def specific_oauth_error(resp, error_code, context)
|
301
336
|
description = resp.data[:error_description] || error_code
|
302
337
|
|
338
|
+
Util.log_info('Stripe OAuth error',
|
339
|
+
status: resp.http_status,
|
340
|
+
error_code: error_code,
|
341
|
+
error_description: description,
|
342
|
+
idempotency_key: context.idempotency_key,
|
343
|
+
request_id: context.request_id
|
344
|
+
)
|
345
|
+
|
303
346
|
args = [error_code, description, {
|
304
347
|
http_status: resp.http_status, http_body: resp.http_body,
|
305
348
|
json_body: resp.data, http_headers: resp.http_headers
|
@@ -319,7 +362,13 @@ module Stripe
|
|
319
362
|
end
|
320
363
|
end
|
321
364
|
|
322
|
-
def handle_network_error(e, retry_count, api_base=nil)
|
365
|
+
def handle_network_error(e, context, retry_count, api_base=nil)
|
366
|
+
Util.log_info('Stripe OAuth error',
|
367
|
+
error_message: e.message,
|
368
|
+
idempotency_key: context.idempotency_key,
|
369
|
+
request_id: context.request_id
|
370
|
+
)
|
371
|
+
|
323
372
|
case e
|
324
373
|
when Faraday::ConnectionFailed
|
325
374
|
message = "Unexpected error communicating when trying to connect to Stripe. " \
|
@@ -388,6 +437,104 @@ module Stripe
|
|
388
437
|
headers
|
389
438
|
end
|
390
439
|
|
440
|
+
def log_request(context)
|
441
|
+
Util.log_info("Request to Stripe API",
|
442
|
+
api_version: context.api_version,
|
443
|
+
idempotency_key: context.idempotency_key,
|
444
|
+
method: context.method,
|
445
|
+
path: context.path
|
446
|
+
)
|
447
|
+
Util.log_debug("Request details",
|
448
|
+
body: context.payload,
|
449
|
+
idempotency_key: context.idempotency_key
|
450
|
+
)
|
451
|
+
end
|
452
|
+
private :log_request
|
453
|
+
|
454
|
+
def log_response(context, request_start, status, body)
|
455
|
+
Util.log_info("Response from Stripe API",
|
456
|
+
api_version: context.api_version,
|
457
|
+
elapsed: Time.now - request_start,
|
458
|
+
idempotency_key: context.idempotency_key,
|
459
|
+
method: context.method,
|
460
|
+
path: context.path,
|
461
|
+
request_id: context.request_id,
|
462
|
+
status: status
|
463
|
+
)
|
464
|
+
Util.log_debug("Response details",
|
465
|
+
body: body,
|
466
|
+
idempotency_key: context.idempotency_key,
|
467
|
+
request_id: context.request_id,
|
468
|
+
)
|
469
|
+
if context.request_id
|
470
|
+
Util.log_debug("Dashboard link for request",
|
471
|
+
idempotency_key: context.idempotency_key,
|
472
|
+
request_id: context.request_id,
|
473
|
+
url: Util.request_id_dashboard_url(context.request_id, context.api_key)
|
474
|
+
)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
private :log_response
|
478
|
+
|
479
|
+
def log_response_error(context, request_start, e)
|
480
|
+
Util.log_info("Request error",
|
481
|
+
elapsed: Time.now - request_start,
|
482
|
+
error_message: e.message,
|
483
|
+
idempotency_key: context.idempotency_key,
|
484
|
+
method: context.method,
|
485
|
+
path: context.path,
|
486
|
+
)
|
487
|
+
end
|
488
|
+
private :log_response_error
|
489
|
+
|
490
|
+
# RequestLogContext stores information about a request that's begin made so
|
491
|
+
# that we can log certain information. It's useful because it means that we
|
492
|
+
# don't have to pass around as many parameters.
|
493
|
+
class RequestLogContext
|
494
|
+
attr_accessor :api_key
|
495
|
+
attr_accessor :api_version
|
496
|
+
attr_accessor :idempotency_key
|
497
|
+
attr_accessor :method
|
498
|
+
attr_accessor :path
|
499
|
+
attr_accessor :payload
|
500
|
+
attr_accessor :request_id
|
501
|
+
|
502
|
+
def initialize(api_key: nil, api_version: nil, idempotency_key: nil,
|
503
|
+
method: nil, path: nil, payload: nil)
|
504
|
+
self.api_key = api_key
|
505
|
+
self.api_version = api_version
|
506
|
+
self.idempotency_key = idempotency_key
|
507
|
+
self.method = method
|
508
|
+
self.path = path
|
509
|
+
self.payload = payload
|
510
|
+
end
|
511
|
+
|
512
|
+
# The idea with this method is that we might want to update some of
|
513
|
+
# context information because a response that we've received from the API
|
514
|
+
# contains information that's more authoritative than what we started
|
515
|
+
# with for a request. For example, we should trust whatever came back in
|
516
|
+
# a `Stripe-Version` header beyond what configuration information that we
|
517
|
+
# might have had available.
|
518
|
+
def dup_from_response(resp)
|
519
|
+
return self if resp.nil?
|
520
|
+
|
521
|
+
# Faraday's API is a little unusual. Normally it'll produce a response
|
522
|
+
# object with a `headers` method, but on error what it puts into
|
523
|
+
# `e.response` is an untyped `Hash`.
|
524
|
+
headers = if resp.is_a?(Faraday::Response)
|
525
|
+
resp.headers
|
526
|
+
else
|
527
|
+
resp[:headers]
|
528
|
+
end
|
529
|
+
|
530
|
+
context = self.dup
|
531
|
+
context.api_version = headers["Stripe-Version"]
|
532
|
+
context.idempotency_key = headers["Idempotency-Key"]
|
533
|
+
context.request_id = headers["Request-Id"]
|
534
|
+
context
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
391
538
|
# SystemProfiler extracts information about the system that we're running
|
392
539
|
# in so that we can generate a rich user agent header to help debug
|
393
540
|
# integrations.
|