stripe 3.0.3 → 3.1.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/.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.
|