stripe 5.27.0 → 5.31.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +34 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +1 -1
- data/CHANGELOG.md +19 -0
- data/Gemfile +0 -1
- data/README.md +6 -2
- data/VERSION +1 -1
- data/lib/stripe.rb +23 -21
- data/lib/stripe/connection_manager.rb +12 -7
- data/lib/stripe/oauth.rb +4 -3
- data/lib/stripe/object_types.rb +1 -0
- data/lib/stripe/resources.rb +1 -0
- data/lib/stripe/resources/account.rb +3 -8
- data/lib/stripe/resources/billing_portal/configuration.rb +14 -0
- data/lib/stripe/resources/customer.rb +6 -1
- data/lib/stripe/resources/file.rb +2 -1
- data/lib/stripe/stripe_client.rb +131 -64
- data/lib/stripe/stripe_configuration.rb +35 -8
- data/lib/stripe/stripe_object.rb +23 -0
- data/lib/stripe/util.rb +12 -6
- data/lib/stripe/version.rb +1 -1
- data/test/stripe/billing_portal/configuration_test.rb +37 -0
- data/test/stripe/connection_manager_test.rb +47 -0
- data/test/stripe/customer_test.rb +2 -2
- data/test/stripe/oauth_test.rb +45 -0
- data/test/stripe/stripe_client_test.rb +188 -23
- data/test/stripe/stripe_configuration_test.rb +40 -9
- data/test/stripe/stripe_object_test.rb +10 -0
- data/test/stripe_test.rb +18 -0
- data/test/test_helper.rb +3 -4
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e821f192b447738c35b91b15911de51e0cd138c0e999230747c43e04a1b97c7
|
4
|
+
data.tar.gz: ba43636f6bf6e95dfed2daa8078f22d81a4dca5b056dacf12bc979925c6a7786
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9d7e86de7356f7e6a8a929304b330c7692686a31596af70e2ed91c5e15c024102ed9a32f9a81ec07ac585f12a6a853cd28acee37ea95523e1b8788d7041e130
|
7
|
+
data.tar.gz: 7ac1ca50526e7dce9f45cd4179dc17f5965ce24f5702dc1605bd449f25f852827b8f24ba6dbb32983142f34037296cd8c0c28927da576915fbbc42e8022f8892
|
@@ -0,0 +1,34 @@
|
|
1
|
+
on:
|
2
|
+
push:
|
3
|
+
branches: [ master ]
|
4
|
+
pull_request:
|
5
|
+
branches: [ master ]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
lint:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v2
|
13
|
+
- name: Set up Ruby
|
14
|
+
uses: ruby/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
ruby-version: 2.7
|
17
|
+
- name: rubocop
|
18
|
+
run: bundle install && bundle exec rake rubocop
|
19
|
+
|
20
|
+
test:
|
21
|
+
runs-on: ubuntu-latest
|
22
|
+
strategy:
|
23
|
+
matrix:
|
24
|
+
ruby-version: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0, jruby-9.2.16.0]
|
25
|
+
steps:
|
26
|
+
- uses: actions/checkout@v2
|
27
|
+
- name: Set up Ruby
|
28
|
+
uses: ruby/setup-ruby@v1
|
29
|
+
with:
|
30
|
+
ruby-version: ${{ matrix.ruby-version }}
|
31
|
+
- name: Start stripe-mock
|
32
|
+
run: docker run -d -p 12111-12112:12111-12112 stripemock/stripe-mock && sleep 5
|
33
|
+
- name: test
|
34
|
+
run: bundle install && bundle exec rake test
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 5.31.0 - 2021-04-02
|
4
|
+
* [#968](https://github.com/stripe/stripe-ruby/pull/968) Allow StripeClient to be configured per instance
|
5
|
+
* [#971](https://github.com/stripe/stripe-ruby/pull/971) On config change, only clear connection managers for changed config
|
6
|
+
* [#972](https://github.com/stripe/stripe-ruby/pull/972) Rename `Stripe.configuration` to `Stripe.config`
|
7
|
+
* [#970](https://github.com/stripe/stripe-ruby/pull/970) Reserve some critical field names when adding `StripeObject` accessors
|
8
|
+
* [#967](https://github.com/stripe/stripe-ruby/pull/967) CI: github actions
|
9
|
+
|
10
|
+
## 5.30.0 - 2021-02-22
|
11
|
+
* [#965](https://github.com/stripe/stripe-ruby/pull/965) Add support for the Billing Portal Configuration API
|
12
|
+
|
13
|
+
## 5.29.1 - 2021-02-09
|
14
|
+
* [#964](https://github.com/stripe/stripe-ruby/pull/964) Fix return value of `Customer#delete_discount`
|
15
|
+
|
16
|
+
## 5.29.0 - 2021-01-05
|
17
|
+
* [#952](https://github.com/stripe/stripe-ruby/pull/952) Allow client_id configuration on instance config
|
18
|
+
|
19
|
+
## 5.28.0 - 2020-10-14
|
20
|
+
* [#950](https://github.com/stripe/stripe-ruby/pull/950) Add configuration option for `write_timeout` for connections on Ruby 2.6+
|
21
|
+
|
3
22
|
## 5.27.0 - 2020-10-14
|
4
23
|
* [#951](https://github.com/stripe/stripe-ruby/pull/951) Add support for the Payout Reverse API
|
5
24
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/stripe.svg)](https://badge.fury.io/rb/stripe)
|
4
4
|
[![Build Status](https://travis-ci.org/stripe/stripe-ruby.svg?branch=master)](https://travis-ci.org/stripe/stripe-ruby)
|
5
|
-
[![Coverage Status](https://coveralls.io/repos/github/stripe/stripe-ruby/badge.svg?branch=master)](https://coveralls.io/github/stripe/stripe-ruby?branch=master)
|
6
5
|
|
7
6
|
The Stripe Ruby library provides convenient access to the Stripe API from
|
8
7
|
applications written in the Ruby language. It includes a pre-defined set of
|
@@ -21,6 +20,9 @@ The library also provides other features. For example:
|
|
21
20
|
|
22
21
|
See the [Ruby API docs](https://stripe.com/docs/api/ruby#intro).
|
23
22
|
|
23
|
+
See [video demonstrations][youtube-playlist] covering how to use the library.
|
24
|
+
|
25
|
+
|
24
26
|
## Installation
|
25
27
|
|
26
28
|
You don't need this source code unless you want to modify the gem. If you just
|
@@ -186,11 +188,12 @@ retries are safe.
|
|
186
188
|
|
187
189
|
### Configuring Timeouts
|
188
190
|
|
189
|
-
Open and
|
191
|
+
Open, read and write timeouts are configurable:
|
190
192
|
|
191
193
|
```ruby
|
192
194
|
Stripe.open_timeout = 30 # in seconds
|
193
195
|
Stripe.read_timeout = 80
|
196
|
+
Stripe.write_timeout = 30 # only supported on Ruby 2.6+
|
194
197
|
```
|
195
198
|
|
196
199
|
Please take care to set conservative read timeouts. Some API requests can take
|
@@ -339,6 +342,7 @@ Update the bundled [stripe-mock] by editing the version number found in
|
|
339
342
|
[idempotency-keys]: https://stripe.com/docs/api/ruby#idempotent_requests
|
340
343
|
[stripe-mock]: https://github.com/stripe/stripe-mock
|
341
344
|
[versioning]: https://stripe.com/docs/api/ruby#versioning
|
345
|
+
[youtube-playlist]: https://www.youtube.com/playlist?list=PLy1nL-pvL2M50RmP6ie-gdcSnfOuQCRYk
|
342
346
|
|
343
347
|
<!--
|
344
348
|
# vim: set tw=79:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
5.
|
1
|
+
5.31.0
|
data/lib/stripe.rb
CHANGED
@@ -57,34 +57,36 @@ module Stripe
|
|
57
57
|
|
58
58
|
@app_info = nil
|
59
59
|
|
60
|
-
@
|
60
|
+
@config = Stripe::StripeConfiguration.setup
|
61
61
|
|
62
62
|
class << self
|
63
63
|
extend Forwardable
|
64
64
|
|
65
|
+
attr_reader :config
|
66
|
+
|
65
67
|
# User configurable options
|
66
|
-
def_delegators :@
|
67
|
-
def_delegators :@
|
68
|
-
def_delegators :@
|
69
|
-
def_delegators :@
|
70
|
-
def_delegators :@
|
71
|
-
def_delegators :@
|
72
|
-
def_delegators :@
|
73
|
-
def_delegators :@
|
74
|
-
def_delegators :@
|
75
|
-
def_delegators :@
|
76
|
-
def_delegators :@
|
77
|
-
def_delegators :@
|
78
|
-
def_delegators :@
|
79
|
-
def_delegators :@
|
80
|
-
def_delegators :@
|
68
|
+
def_delegators :@config, :api_key, :api_key=
|
69
|
+
def_delegators :@config, :api_version, :api_version=
|
70
|
+
def_delegators :@config, :stripe_account, :stripe_account=
|
71
|
+
def_delegators :@config, :api_base, :api_base=
|
72
|
+
def_delegators :@config, :uploads_base, :uploads_base=
|
73
|
+
def_delegators :@config, :connect_base, :connect_base=
|
74
|
+
def_delegators :@config, :open_timeout, :open_timeout=
|
75
|
+
def_delegators :@config, :read_timeout, :read_timeout=
|
76
|
+
def_delegators :@config, :write_timeout, :write_timeout=
|
77
|
+
def_delegators :@config, :proxy, :proxy=
|
78
|
+
def_delegators :@config, :verify_ssl_certs, :verify_ssl_certs=
|
79
|
+
def_delegators :@config, :ca_bundle_path, :ca_bundle_path=
|
80
|
+
def_delegators :@config, :log_level, :log_level=
|
81
|
+
def_delegators :@config, :logger, :logger=
|
82
|
+
def_delegators :@config, :max_network_retries, :max_network_retries=
|
83
|
+
def_delegators :@config, :enable_telemetry=, :enable_telemetry?
|
84
|
+
def_delegators :@config, :client_id=, :client_id
|
81
85
|
|
82
86
|
# Internal configurations
|
83
|
-
def_delegators :@
|
84
|
-
def_delegators :@
|
85
|
-
def_delegators :@
|
86
|
-
|
87
|
-
attr_accessor :client_id
|
87
|
+
def_delegators :@config, :max_network_retry_delay
|
88
|
+
def_delegators :@config, :initial_network_retry_delay
|
89
|
+
def_delegators :@config, :ca_store
|
88
90
|
end
|
89
91
|
|
90
92
|
# Gets the application for a plugin that's identified some. See
|
@@ -15,8 +15,10 @@ module Stripe
|
|
15
15
|
# by `StripeClient` to determine whether a connection manager should be
|
16
16
|
# garbage collected or not.
|
17
17
|
attr_reader :last_used
|
18
|
+
attr_reader :config
|
18
19
|
|
19
|
-
def initialize
|
20
|
+
def initialize(config = Stripe.config)
|
21
|
+
@config = config
|
20
22
|
@active_connections = {}
|
21
23
|
@last_used = Util.monotonic_time
|
22
24
|
|
@@ -117,14 +119,17 @@ module Stripe
|
|
117
119
|
# reused Go's default for `DefaultTransport`.
|
118
120
|
connection.keep_alive_timeout = 30
|
119
121
|
|
120
|
-
connection.open_timeout =
|
121
|
-
connection.read_timeout =
|
122
|
+
connection.open_timeout = config.open_timeout
|
123
|
+
connection.read_timeout = config.read_timeout
|
124
|
+
if connection.respond_to?(:write_timeout=)
|
125
|
+
connection.write_timeout = config.write_timeout
|
126
|
+
end
|
122
127
|
|
123
128
|
connection.use_ssl = uri.scheme == "https"
|
124
129
|
|
125
|
-
if
|
130
|
+
if config.verify_ssl_certs
|
126
131
|
connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
127
|
-
connection.cert_store =
|
132
|
+
connection.cert_store = config.ca_store
|
128
133
|
else
|
129
134
|
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
130
135
|
warn_ssl_verify_none
|
@@ -138,10 +143,10 @@ module Stripe
|
|
138
143
|
# out those pieces to make passing them into a new connection a little less
|
139
144
|
# ugly.
|
140
145
|
private def proxy_parts
|
141
|
-
if
|
146
|
+
if config.proxy.nil?
|
142
147
|
[nil, nil, nil, nil]
|
143
148
|
else
|
144
|
-
u = URI.parse(
|
149
|
+
u = URI.parse(config.proxy)
|
145
150
|
[u.host, u.port, u.user, u.password]
|
146
151
|
end
|
147
152
|
end
|
data/lib/stripe/oauth.rb
CHANGED
@@ -7,8 +7,8 @@ module Stripe
|
|
7
7
|
|
8
8
|
def self.execute_resource_request(method, url, params, opts)
|
9
9
|
opts = Util.normalize_opts(opts)
|
10
|
-
opts[:client] ||= StripeClient.active_client
|
11
|
-
opts[:api_base] ||=
|
10
|
+
opts[:client] ||= opts[:client] || StripeClient.active_client
|
11
|
+
opts[:api_base] ||= opts[:client].config.connect_base
|
12
12
|
|
13
13
|
super(method, url, params, opts)
|
14
14
|
end
|
@@ -29,7 +29,8 @@ module Stripe
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.authorize_url(params = {}, opts = {})
|
32
|
-
|
32
|
+
client = opts[:client] || StripeClient.active_client
|
33
|
+
base = opts[:connect_base] || client.config.connect_base
|
33
34
|
|
34
35
|
path = "/oauth/authorize"
|
35
36
|
path = "/express" + path if opts[:express]
|
data/lib/stripe/object_types.rb
CHANGED
@@ -19,6 +19,7 @@ module Stripe
|
|
19
19
|
Balance::OBJECT_NAME => Balance,
|
20
20
|
BalanceTransaction::OBJECT_NAME => BalanceTransaction,
|
21
21
|
BankAccount::OBJECT_NAME => BankAccount,
|
22
|
+
BillingPortal::Configuration::OBJECT_NAME => BillingPortal::Configuration,
|
22
23
|
BillingPortal::Session::OBJECT_NAME => BillingPortal::Session,
|
23
24
|
BitcoinReceiver::OBJECT_NAME => BitcoinReceiver,
|
24
25
|
BitcoinTransaction::OBJECT_NAME => BitcoinTransaction,
|
data/lib/stripe/resources.rb
CHANGED
@@ -9,6 +9,7 @@ require "stripe/resources/application_fee_refund"
|
|
9
9
|
require "stripe/resources/balance"
|
10
10
|
require "stripe/resources/balance_transaction"
|
11
11
|
require "stripe/resources/bank_account"
|
12
|
+
require "stripe/resources/billing_portal/configuration"
|
12
13
|
require "stripe/resources/billing_portal/session"
|
13
14
|
require "stripe/resources/bitcoin_receiver"
|
14
15
|
require "stripe/resources/bitcoin_transaction"
|
@@ -45,12 +45,8 @@ module Stripe
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# @override To make id optional
|
48
|
-
def self.retrieve(id =
|
49
|
-
id
|
50
|
-
nil
|
51
|
-
else
|
52
|
-
Util.check_string_argument!(id)
|
53
|
-
end
|
48
|
+
def self.retrieve(id = nil, opts = {})
|
49
|
+
Util.check_string_argument!(id) if id
|
54
50
|
|
55
51
|
# Account used to be a singleton, where this method's signature was
|
56
52
|
# `(opts={})`. For the sake of not breaking folks who pass in an OAuth
|
@@ -136,11 +132,10 @@ module Stripe
|
|
136
132
|
client_id: client_id,
|
137
133
|
stripe_user_id: id,
|
138
134
|
}
|
135
|
+
opts = @opts.merge(Util.normalize_opts(opts))
|
139
136
|
OAuth.deauthorize(params, opts)
|
140
137
|
end
|
141
138
|
|
142
|
-
ARGUMENT_NOT_PROVIDED = Object.new
|
143
|
-
|
144
139
|
private def serialize_additional_owners(legal_entity, additional_owners)
|
145
140
|
original_value =
|
146
141
|
legal_entity
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# File generated from our OpenAPI spec
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Stripe
|
5
|
+
module BillingPortal
|
6
|
+
class Configuration < APIResource
|
7
|
+
extend Stripe::APIOperations::Create
|
8
|
+
extend Stripe::APIOperations::List
|
9
|
+
include Stripe::APIOperations::Save
|
10
|
+
|
11
|
+
OBJECT_NAME = "billing_portal.configuration"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -28,9 +28,14 @@ module Stripe
|
|
28
28
|
alias detach_source delete_source
|
29
29
|
end
|
30
30
|
|
31
|
+
# Deletes a discount associated with the customer.
|
32
|
+
#
|
33
|
+
# Returns the deleted discount. The customer object is not updated,
|
34
|
+
# so you must call `refresh` on it to get a new version with the
|
35
|
+
# discount removed.
|
31
36
|
def delete_discount
|
32
37
|
resp, opts = execute_resource_request(:delete, resource_url + "/discount")
|
33
|
-
|
38
|
+
Util.convert_to_stripe_object(resp.data, opts)
|
34
39
|
end
|
35
40
|
end
|
36
41
|
end
|
@@ -25,8 +25,9 @@ module Stripe
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
config = opts[:client]&.config || Stripe.config
|
28
29
|
opts = {
|
29
|
-
api_base:
|
30
|
+
api_base: config.uploads_base,
|
30
31
|
content_type: MultipartEncoder::MULTIPART_FORM_DATA,
|
31
32
|
}.merge(Util.normalize_opts(opts))
|
32
33
|
super
|
data/lib/stripe/stripe_client.rb
CHANGED
@@ -9,18 +9,35 @@ module Stripe
|
|
9
9
|
class StripeClient
|
10
10
|
# A set of all known thread contexts across all threads and a mutex to
|
11
11
|
# synchronize global access to them.
|
12
|
-
@thread_contexts_with_connection_managers =
|
12
|
+
@thread_contexts_with_connection_managers = Set.new
|
13
13
|
@thread_contexts_with_connection_managers_mutex = Mutex.new
|
14
14
|
@last_connection_manager_gc = Util.monotonic_time
|
15
15
|
|
16
|
-
# Initializes a new
|
17
|
-
|
18
|
-
# Takes a connection manager object for backwards compatibility only, and
|
19
|
-
# that use is DEPRECATED.
|
20
|
-
def initialize(_connection_manager = nil)
|
16
|
+
# Initializes a new StripeClient
|
17
|
+
def initialize(config_arg = {})
|
21
18
|
@system_profiler = SystemProfiler.new
|
22
19
|
@last_request_metrics = nil
|
23
|
-
|
20
|
+
|
21
|
+
@config = case config_arg
|
22
|
+
when Hash
|
23
|
+
Stripe.config.reverse_duplicate_merge(config_arg)
|
24
|
+
when Stripe::ConnectionManager
|
25
|
+
# Supports accepting a connection manager object for backwards
|
26
|
+
# compatibility only, and that use is DEPRECATED.
|
27
|
+
Stripe.config.dup
|
28
|
+
when Stripe::StripeConfiguration
|
29
|
+
config_arg
|
30
|
+
when String
|
31
|
+
Stripe.config.reverse_duplicate_merge(
|
32
|
+
{ api_key: config_arg }
|
33
|
+
)
|
34
|
+
else
|
35
|
+
raise ArgumentError, "Can't handle argument: #{config_arg}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :config
|
40
|
+
attr_reader :options
|
24
41
|
|
25
42
|
# Gets a currently active `StripeClient`. Set for the current thread when
|
26
43
|
# `StripeClient#request` is being run so that API operations being executed
|
@@ -37,36 +54,51 @@ module Stripe
|
|
37
54
|
# clears them from internal tracking in all connection managers across all
|
38
55
|
# threads.
|
39
56
|
#
|
57
|
+
# If passed a `config` object, only clear connection managers for that
|
58
|
+
# particular configuration.
|
59
|
+
#
|
40
60
|
# For internal use only. Does not provide a stable API and may be broken
|
41
61
|
# with future non-major changes.
|
42
|
-
def self.clear_all_connection_managers
|
62
|
+
def self.clear_all_connection_managers(config: nil)
|
43
63
|
# Just a quick path for when configuration is being set for the first
|
44
64
|
# time before any connections have been opened. There is technically some
|
45
65
|
# potential for thread raciness here, but not in a practical sense.
|
46
66
|
return if @thread_contexts_with_connection_managers.empty?
|
47
67
|
|
48
68
|
@thread_contexts_with_connection_managers_mutex.synchronize do
|
69
|
+
pruned_contexts = Set.new
|
70
|
+
|
49
71
|
@thread_contexts_with_connection_managers.each do |thread_context|
|
50
72
|
# Note that the thread context itself is not destroyed, but we clear
|
51
73
|
# its connection manager and remove our reference to it. If it ever
|
52
74
|
# makes a new request we'll give it a new connection manager and
|
53
75
|
# it'll go back into `@thread_contexts_with_connection_managers`.
|
54
|
-
thread_context.
|
55
|
-
|
76
|
+
thread_context.default_connection_managers.reject! do |cm_config, cm|
|
77
|
+
if config.nil? || config.key == cm_config
|
78
|
+
cm.clear
|
79
|
+
true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if thread_context.default_connection_managers.empty?
|
84
|
+
pruned_contexts << thread_context
|
85
|
+
end
|
56
86
|
end
|
57
|
-
|
87
|
+
|
88
|
+
@thread_contexts_with_connection_managers.subtract(pruned_contexts)
|
58
89
|
end
|
59
90
|
end
|
60
91
|
|
61
92
|
# A default client for the current thread.
|
62
93
|
def self.default_client
|
63
|
-
current_thread_context.default_client ||= StripeClient.new
|
94
|
+
current_thread_context.default_client ||= StripeClient.new(Stripe.config)
|
64
95
|
end
|
65
96
|
|
66
|
-
# A default connection manager for the current thread
|
67
|
-
|
68
|
-
|
69
|
-
|
97
|
+
# A default connection manager for the current thread scoped to the
|
98
|
+
# configuration object that may be provided.
|
99
|
+
def self.default_connection_manager(config = Stripe.config)
|
100
|
+
current_thread_context.default_connection_managers[config.key] ||= begin
|
101
|
+
connection_manager = ConnectionManager.new(config)
|
70
102
|
|
71
103
|
@thread_contexts_with_connection_managers_mutex.synchronize do
|
72
104
|
maybe_gc_connection_managers
|
@@ -80,8 +112,9 @@ module Stripe
|
|
80
112
|
# Checks if an error is a problem that we should retry on. This includes
|
81
113
|
# both socket errors that may represent an intermittent problem and some
|
82
114
|
# special HTTP statuses.
|
83
|
-
def self.should_retry?(error,
|
84
|
-
|
115
|
+
def self.should_retry?(error,
|
116
|
+
method:, num_retries:, config: Stripe.config)
|
117
|
+
return false if num_retries >= config.max_network_retries
|
85
118
|
|
86
119
|
case error
|
87
120
|
when Net::OpenTimeout, Net::ReadTimeout
|
@@ -127,13 +160,13 @@ module Stripe
|
|
127
160
|
end
|
128
161
|
end
|
129
162
|
|
130
|
-
def self.sleep_time(num_retries)
|
163
|
+
def self.sleep_time(num_retries, config: Stripe.config)
|
131
164
|
# Apply exponential backoff with initial_network_retry_delay on the
|
132
165
|
# number of num_retries so far as inputs. Do not allow the number to
|
133
166
|
# exceed max_network_retry_delay.
|
134
167
|
sleep_seconds = [
|
135
|
-
|
136
|
-
|
168
|
+
config.initial_network_retry_delay * (2**(num_retries - 1)),
|
169
|
+
config.max_network_retry_delay,
|
137
170
|
].min
|
138
171
|
|
139
172
|
# Apply some jitter by randomizing the value in the range of
|
@@ -141,9 +174,7 @@ module Stripe
|
|
141
174
|
sleep_seconds *= (0.5 * (1 + rand))
|
142
175
|
|
143
176
|
# But never sleep less than the base sleep seconds.
|
144
|
-
|
145
|
-
|
146
|
-
sleep_seconds
|
177
|
+
[config.initial_network_retry_delay, sleep_seconds].max
|
147
178
|
end
|
148
179
|
|
149
180
|
# Gets the connection manager in use for the current `StripeClient`.
|
@@ -187,8 +218,8 @@ module Stripe
|
|
187
218
|
raise ArgumentError, "path should be a string" \
|
188
219
|
unless path.is_a?(String)
|
189
220
|
|
190
|
-
api_base ||=
|
191
|
-
api_key ||=
|
221
|
+
api_base ||= config.api_base
|
222
|
+
api_key ||= config.api_key
|
192
223
|
params = Util.objects_to_ids(params)
|
193
224
|
|
194
225
|
check_api_key!(api_key)
|
@@ -231,10 +262,12 @@ module Stripe
|
|
231
262
|
context.query = query
|
232
263
|
|
233
264
|
http_resp = execute_request_with_rescues(method, api_base, context) do
|
234
|
-
self.class
|
235
|
-
|
236
|
-
|
237
|
-
|
265
|
+
self.class
|
266
|
+
.default_connection_manager(config)
|
267
|
+
.execute_request(method, url,
|
268
|
+
body: body,
|
269
|
+
headers: headers,
|
270
|
+
query: query)
|
238
271
|
end
|
239
272
|
|
240
273
|
begin
|
@@ -246,13 +279,21 @@ module Stripe
|
|
246
279
|
# If being called from `StripeClient#request`, put the last response in
|
247
280
|
# thread-local memory so that it can be returned to the user. Don't store
|
248
281
|
# anything otherwise so that we don't leak memory.
|
249
|
-
|
250
|
-
self.class.current_thread_context.last_responses[object_id] = resp
|
251
|
-
end
|
282
|
+
store_last_response(object_id, resp)
|
252
283
|
|
253
284
|
[resp, api_key]
|
254
285
|
end
|
255
286
|
|
287
|
+
def store_last_response(object_id, resp)
|
288
|
+
return unless last_response_has_key?(object_id)
|
289
|
+
|
290
|
+
self.class.current_thread_context.last_responses[object_id] = resp
|
291
|
+
end
|
292
|
+
|
293
|
+
def last_response_has_key?(object_id)
|
294
|
+
self.class.current_thread_context.last_responses&.key?(object_id)
|
295
|
+
end
|
296
|
+
|
256
297
|
#
|
257
298
|
# private
|
258
299
|
#
|
@@ -328,11 +369,6 @@ module Stripe
|
|
328
369
|
# the user hasn't specified their own.
|
329
370
|
attr_accessor :default_client
|
330
371
|
|
331
|
-
# A default `ConnectionManager` for the thread. Normally shared between
|
332
|
-
# all `StripeClient` objects on a particular thread, and created so as to
|
333
|
-
# minimize the number of open connections that an application needs.
|
334
|
-
attr_accessor :default_connection_manager
|
335
|
-
|
336
372
|
# A temporary map of object IDs to responses from last executed API
|
337
373
|
# calls. Used to return a responses from calls to `StripeClient#request`.
|
338
374
|
#
|
@@ -345,6 +381,17 @@ module Stripe
|
|
345
381
|
# because that's wrapped in an `ensure` block, they should never leave
|
346
382
|
# garbage in `Thread.current`.
|
347
383
|
attr_accessor :last_responses
|
384
|
+
|
385
|
+
# A map of connection mangers for the thread. Normally shared between
|
386
|
+
# all `StripeClient` objects on a particular thread, and created so as to
|
387
|
+
# minimize the number of open connections that an application needs.
|
388
|
+
def default_connection_managers
|
389
|
+
@default_connection_managers ||= {}
|
390
|
+
end
|
391
|
+
|
392
|
+
def reset_connection_managers
|
393
|
+
@default_connection_managers = {}
|
394
|
+
end
|
348
395
|
end
|
349
396
|
|
350
397
|
# Access data stored for `StripeClient` within the thread's current
|
@@ -380,24 +427,32 @@ module Stripe
|
|
380
427
|
last_used_threshold =
|
381
428
|
Util.monotonic_time - CONNECTION_MANAGER_GC_LAST_USED_EXPIRY
|
382
429
|
|
383
|
-
|
430
|
+
pruned_contexts = []
|
431
|
+
@thread_contexts_with_connection_managers.each do |thread_context|
|
432
|
+
thread_context
|
433
|
+
.default_connection_managers
|
434
|
+
.each do |config_key, connection_manager|
|
435
|
+
next if connection_manager.last_used > last_used_threshold
|
436
|
+
|
437
|
+
connection_manager.clear
|
438
|
+
thread_context.default_connection_managers.delete(config_key)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
384
442
|
@thread_contexts_with_connection_managers.each do |thread_context|
|
385
|
-
|
386
|
-
next if connection_manager.last_used > last_used_threshold
|
443
|
+
next unless thread_context.default_connection_managers.empty?
|
387
444
|
|
388
|
-
|
389
|
-
thread_context.default_connection_manager = nil
|
390
|
-
pruned_thread_contexts << thread_context
|
445
|
+
pruned_contexts << thread_context
|
391
446
|
end
|
392
447
|
|
393
|
-
@thread_contexts_with_connection_managers -=
|
448
|
+
@thread_contexts_with_connection_managers -= pruned_contexts
|
394
449
|
@last_connection_manager_gc = Util.monotonic_time
|
395
450
|
|
396
|
-
|
451
|
+
pruned_contexts.count
|
397
452
|
end
|
398
453
|
|
399
454
|
private def api_url(url = "", api_base = nil)
|
400
|
-
(api_base ||
|
455
|
+
(api_base || config.api_base) + url
|
401
456
|
end
|
402
457
|
|
403
458
|
private def check_api_key!(api_key)
|
@@ -471,7 +526,7 @@ module Stripe
|
|
471
526
|
notify_request_end(context, request_duration, http_status,
|
472
527
|
num_retries, user_data)
|
473
528
|
|
474
|
-
if
|
529
|
+
if config.enable_telemetry? && context.request_id
|
475
530
|
request_duration_ms = (request_duration * 1000).to_i
|
476
531
|
@last_request_metrics =
|
477
532
|
StripeRequestMetrics.new(context.request_id, request_duration_ms)
|
@@ -498,9 +553,12 @@ module Stripe
|
|
498
553
|
notify_request_end(context, request_duration, http_status, num_retries,
|
499
554
|
user_data)
|
500
555
|
|
501
|
-
if self.class.should_retry?(e,
|
556
|
+
if self.class.should_retry?(e,
|
557
|
+
method: method,
|
558
|
+
num_retries: num_retries,
|
559
|
+
config: config)
|
502
560
|
num_retries += 1
|
503
|
-
sleep self.class.sleep_time(num_retries)
|
561
|
+
sleep self.class.sleep_time(num_retries, config: config)
|
504
562
|
retry
|
505
563
|
end
|
506
564
|
|
@@ -622,7 +680,8 @@ module Stripe
|
|
622
680
|
error_param: error_data[:param],
|
623
681
|
error_type: error_data[:type],
|
624
682
|
idempotency_key: context.idempotency_key,
|
625
|
-
request_id: context.request_id
|
683
|
+
request_id: context.request_id,
|
684
|
+
config: config)
|
626
685
|
|
627
686
|
# The standard set of arguments that can be used to initialize most of
|
628
687
|
# the exceptions.
|
@@ -671,7 +730,8 @@ module Stripe
|
|
671
730
|
error_code: error_code,
|
672
731
|
error_description: description,
|
673
732
|
idempotency_key: context.idempotency_key,
|
674
|
-
request_id: context.request_id
|
733
|
+
request_id: context.request_id,
|
734
|
+
config: config)
|
675
735
|
|
676
736
|
args = {
|
677
737
|
http_status: resp.http_status, http_body: resp.http_body,
|
@@ -703,7 +763,8 @@ module Stripe
|
|
703
763
|
Util.log_error("Stripe network error",
|
704
764
|
error_message: error.message,
|
705
765
|
idempotency_key: context.idempotency_key,
|
706
|
-
request_id: context.request_id
|
766
|
+
request_id: context.request_id,
|
767
|
+
config: config)
|
707
768
|
|
708
769
|
errors, message = NETWORK_ERROR_MESSAGES_MAP.detect do |(e, _)|
|
709
770
|
error.is_a?(e)
|
@@ -714,7 +775,7 @@ module Stripe
|
|
714
775
|
"with Stripe. Please let us know at support@stripe.com."
|
715
776
|
end
|
716
777
|
|
717
|
-
api_base ||=
|
778
|
+
api_base ||= config.api_base
|
718
779
|
message = message % api_base
|
719
780
|
|
720
781
|
message += " Request was retried #{num_retries} times." if num_retries > 0
|
@@ -735,7 +796,7 @@ module Stripe
|
|
735
796
|
"Content-Type" => "application/x-www-form-urlencoded",
|
736
797
|
}
|
737
798
|
|
738
|
-
if
|
799
|
+
if config.enable_telemetry? && !@last_request_metrics.nil?
|
739
800
|
headers["X-Stripe-Client-Telemetry"] = JSON.generate(
|
740
801
|
last_request_metrics: @last_request_metrics.payload
|
741
802
|
)
|
@@ -743,12 +804,12 @@ module Stripe
|
|
743
804
|
|
744
805
|
# It is only safe to retry network failures on post and delete
|
745
806
|
# requests if we add an Idempotency-Key header
|
746
|
-
if %i[post delete].include?(method) &&
|
807
|
+
if %i[post delete].include?(method) && config.max_network_retries > 0
|
747
808
|
headers["Idempotency-Key"] ||= SecureRandom.uuid
|
748
809
|
end
|
749
810
|
|
750
|
-
headers["Stripe-Version"] =
|
751
|
-
headers["Stripe-Account"] =
|
811
|
+
headers["Stripe-Version"] = config.api_version if config.api_version
|
812
|
+
headers["Stripe-Account"] = config.stripe_account if config.stripe_account
|
752
813
|
|
753
814
|
user_agent = @system_profiler.user_agent
|
754
815
|
begin
|
@@ -772,11 +833,13 @@ module Stripe
|
|
772
833
|
idempotency_key: context.idempotency_key,
|
773
834
|
method: context.method,
|
774
835
|
num_retries: num_retries,
|
775
|
-
path: context.path
|
836
|
+
path: context.path,
|
837
|
+
config: config)
|
776
838
|
Util.log_debug("Request details",
|
777
839
|
body: context.body,
|
778
840
|
idempotency_key: context.idempotency_key,
|
779
|
-
query: context.query
|
841
|
+
query: context.query,
|
842
|
+
config: config)
|
780
843
|
end
|
781
844
|
|
782
845
|
private def log_response(context, request_start, status, body)
|
@@ -788,11 +851,13 @@ module Stripe
|
|
788
851
|
method: context.method,
|
789
852
|
path: context.path,
|
790
853
|
request_id: context.request_id,
|
791
|
-
status: status
|
854
|
+
status: status,
|
855
|
+
config: config)
|
792
856
|
Util.log_debug("Response details",
|
793
857
|
body: body,
|
794
858
|
idempotency_key: context.idempotency_key,
|
795
|
-
request_id: context.request_id
|
859
|
+
request_id: context.request_id,
|
860
|
+
config: config)
|
796
861
|
|
797
862
|
return unless context.request_id
|
798
863
|
|
@@ -800,7 +865,8 @@ module Stripe
|
|
800
865
|
idempotency_key: context.idempotency_key,
|
801
866
|
request_id: context.request_id,
|
802
867
|
url: Util.request_id_dashboard_url(context.request_id,
|
803
|
-
context.api_key)
|
868
|
+
context.api_key),
|
869
|
+
config: config)
|
804
870
|
end
|
805
871
|
|
806
872
|
private def log_response_error(context, request_start, error)
|
@@ -810,7 +876,8 @@ module Stripe
|
|
810
876
|
error_message: error.message,
|
811
877
|
idempotency_key: context.idempotency_key,
|
812
878
|
method: context.method,
|
813
|
-
path: context.path
|
879
|
+
path: context.path,
|
880
|
+
config: config)
|
814
881
|
end
|
815
882
|
|
816
883
|
# RequestLogContext stores information about a request that's begin made so
|