stripe 5.28.0 → 8.7.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/CHANGELOG.md +286 -0
- data/Gemfile +5 -3
- data/Makefile +13 -0
- data/OPENAPI_VERSION +1 -0
- data/README.md +43 -5
- data/VERSION +1 -1
- data/lib/data/ca-certificates.crt +1241 -1937
- data/lib/stripe/api_operations/create.rb +6 -2
- data/lib/stripe/api_operations/delete.rb +12 -7
- data/lib/stripe/api_operations/list.rb +6 -9
- data/lib/stripe/api_operations/nested_resource.rb +62 -34
- data/lib/stripe/api_operations/request.rb +45 -2
- data/lib/stripe/api_operations/save.rb +13 -3
- data/lib/stripe/api_operations/search.rb +16 -0
- data/lib/stripe/api_resource.rb +11 -18
- data/lib/stripe/api_resource_test_helpers.rb +49 -0
- data/lib/stripe/api_version.rb +8 -0
- data/lib/stripe/connection_manager.rb +49 -11
- data/lib/stripe/error_object.rb +2 -3
- data/lib/stripe/instrumentation.rb +46 -6
- data/lib/stripe/oauth.rb +4 -3
- data/lib/stripe/object_types.rb +36 -10
- data/lib/stripe/resources/account.rb +39 -17
- data/lib/stripe/resources/account_link.rb +4 -0
- data/lib/stripe/resources/application_fee_refund.rb +7 -1
- data/lib/stripe/resources/apps/secret.rb +40 -0
- data/lib/stripe/resources/balance.rb +11 -0
- data/lib/stripe/resources/balance_transaction.rb +4 -0
- data/lib/stripe/resources/bank_account.rb +7 -0
- data/lib/stripe/resources/billing_portal/configuration.rb +15 -0
- data/lib/stripe/resources/billing_portal/session.rb +14 -0
- data/lib/stripe/resources/capability.rb +5 -1
- data/lib/stripe/resources/card.rb +6 -3
- data/lib/stripe/resources/cash_balance.rb +23 -0
- data/lib/stripe/resources/charge.rb +23 -3
- data/lib/stripe/resources/checkout/session.rb +49 -2
- data/lib/stripe/resources/country_spec.rb +6 -0
- data/lib/stripe/resources/coupon.rb +3 -0
- data/lib/stripe/resources/credit_note.rb +27 -9
- data/lib/stripe/resources/credit_note_line_item.rb +2 -0
- data/lib/stripe/resources/customer.rb +132 -4
- data/lib/stripe/resources/customer_balance_transaction.rb +9 -2
- data/lib/stripe/resources/customer_cash_balance_transaction.rb +14 -0
- data/lib/stripe/resources/discount.rb +5 -0
- data/lib/stripe/resources/dispute.rb +17 -3
- data/lib/stripe/resources/event.rb +29 -0
- data/lib/stripe/resources/exchange_rate.rb +11 -0
- data/lib/stripe/resources/file.rb +9 -1
- data/lib/stripe/resources/file_link.rb +3 -0
- data/lib/stripe/resources/financial_connections/account.rb +67 -0
- data/lib/stripe/resources/financial_connections/account_owner.rb +11 -0
- data/lib/stripe/resources/financial_connections/account_ownership.rb +11 -0
- data/lib/stripe/resources/financial_connections/session.rb +13 -0
- data/lib/stripe/resources/funding_instructions.rb +21 -0
- data/lib/stripe/resources/identity/verification_report.rb +23 -0
- data/lib/stripe/resources/identity/verification_session.rb +61 -0
- data/lib/stripe/resources/invoice.rb +105 -17
- data/lib/stripe/resources/invoice_item.rb +11 -0
- data/lib/stripe/resources/invoice_line_item.rb +1 -0
- data/lib/stripe/resources/issuing/authorization.rb +25 -5
- data/lib/stripe/resources/issuing/card.rb +78 -8
- data/lib/stripe/resources/issuing/cardholder.rb +3 -0
- data/lib/stripe/resources/issuing/dispute.rb +13 -3
- data/lib/stripe/resources/issuing/transaction.rb +5 -0
- data/lib/stripe/resources/line_item.rb +2 -0
- data/lib/stripe/resources/login_link.rb +2 -0
- data/lib/stripe/resources/mandate.rb +1 -0
- data/lib/stripe/resources/payment_intent.rb +103 -6
- data/lib/stripe/resources/payment_link.rb +35 -0
- data/lib/stripe/resources/payment_method.rb +25 -5
- data/lib/stripe/resources/payout.rb +28 -5
- data/lib/stripe/resources/person.rb +7 -0
- data/lib/stripe/resources/plan.rb +8 -0
- data/lib/stripe/resources/price.rb +15 -0
- data/lib/stripe/resources/product.rb +17 -0
- data/lib/stripe/resources/promotion_code.rb +2 -0
- data/lib/stripe/resources/quote.rb +128 -0
- data/lib/stripe/resources/radar/early_fraud_warning.rb +4 -0
- data/lib/stripe/resources/radar/value_list.rb +3 -0
- data/lib/stripe/resources/radar/value_list_item.rb +3 -0
- data/lib/stripe/resources/refund.rb +49 -0
- data/lib/stripe/resources/reporting/report_run.rb +8 -0
- data/lib/stripe/resources/reporting/report_type.rb +8 -1
- data/lib/stripe/resources/reversal.rb +16 -2
- data/lib/stripe/resources/review.rb +14 -3
- data/lib/stripe/resources/setup_attempt.rb +4 -0
- data/lib/stripe/resources/setup_intent.rb +60 -5
- data/lib/stripe/resources/shipping_rate.rb +14 -0
- data/lib/stripe/resources/sigma/scheduled_query_run.rb +4 -0
- data/lib/stripe/resources/source.rb +26 -6
- data/lib/stripe/resources/source_transaction.rb +5 -0
- data/lib/stripe/resources/subscription.rb +57 -3
- data/lib/stripe/resources/subscription_item.rb +2 -7
- data/lib/stripe/resources/subscription_schedule.rb +23 -5
- data/lib/stripe/resources/tax/calculation.rb +33 -0
- data/lib/stripe/resources/tax/calculation_line_item.rb +10 -0
- data/lib/stripe/resources/tax/settings.rb +15 -0
- data/lib/stripe/resources/tax/transaction.rb +49 -0
- data/lib/stripe/resources/tax/transaction_line_item.rb +10 -0
- data/lib/stripe/resources/tax_code.rb +11 -0
- data/lib/stripe/resources/tax_id.rb +5 -0
- data/lib/stripe/resources/tax_rate.rb +3 -0
- data/lib/stripe/resources/terminal/configuration.rb +16 -0
- data/lib/stripe/resources/terminal/connection_token.rb +3 -0
- data/lib/stripe/resources/terminal/location.rb +3 -0
- data/lib/stripe/resources/terminal/reader.rb +119 -0
- data/lib/stripe/resources/test_helpers/test_clock.rb +35 -0
- data/lib/stripe/resources/token.rb +20 -0
- data/lib/stripe/resources/topup.rb +15 -3
- data/lib/stripe/resources/transfer.rb +10 -11
- data/lib/stripe/resources/treasury/credit_reversal.rb +14 -0
- data/lib/stripe/resources/treasury/debit_reversal.rb +14 -0
- data/lib/stripe/resources/treasury/financial_account.rb +52 -0
- data/lib/stripe/resources/treasury/financial_account_features.rb +12 -0
- data/lib/stripe/resources/treasury/inbound_transfer.rb +94 -0
- data/lib/stripe/resources/treasury/outbound_payment.rb +96 -0
- data/lib/stripe/resources/treasury/outbound_transfer.rb +100 -0
- data/lib/stripe/resources/treasury/received_credit.rb +30 -0
- data/lib/stripe/resources/treasury/received_debit.rb +30 -0
- data/lib/stripe/resources/treasury/transaction.rb +13 -0
- data/lib/stripe/resources/treasury/transaction_entry.rb +17 -0
- data/lib/stripe/resources/usage_record.rb +5 -0
- data/lib/stripe/resources/usage_record_summary.rb +1 -0
- data/lib/stripe/resources/webhook_endpoint.rb +7 -0
- data/lib/stripe/resources.rb +34 -10
- data/lib/stripe/search_result_object.rb +86 -0
- data/lib/stripe/stripe_client.rb +267 -121
- data/lib/stripe/stripe_configuration.rb +29 -10
- data/lib/stripe/stripe_object.rb +25 -2
- data/lib/stripe/stripe_response.rb +80 -52
- data/lib/stripe/util.rb +100 -9
- data/lib/stripe/version.rb +1 -1
- data/lib/stripe.rb +27 -23
- data/stripe.gemspec +12 -5
- metadata +46 -204
- data/.editorconfig +0 -10
- data/.gitattributes +0 -4
- data/.github/ISSUE_TEMPLATE.md +0 -5
- data/.gitignore +0 -8
- data/.rubocop.yml +0 -80
- data/.rubocop_todo.yml +0 -33
- data/.travis.yml +0 -38
- data/.vscode/extensions.json +0 -7
- data/.vscode/settings.json +0 -8
- data/lib/stripe/resources/bitcoin_receiver.rb +0 -24
- data/lib/stripe/resources/bitcoin_transaction.rb +0 -15
- data/lib/stripe/resources/issuing/card_details.rb +0 -9
- data/lib/stripe/resources/order.rb +0 -33
- data/lib/stripe/resources/order_return.rb +0 -10
- data/lib/stripe/resources/recipient.rb +0 -14
- data/lib/stripe/resources/sku.rb +0 -13
- data/lib/stripe/resources/three_d_secure.rb +0 -14
- data/test/openapi/README.md +0 -9
- data/test/stripe/account_link_test.rb +0 -18
- data/test/stripe/account_test.rb +0 -412
- data/test/stripe/alipay_account_test.rb +0 -37
- data/test/stripe/api_operations_test.rb +0 -80
- data/test/stripe/api_resource_test.rb +0 -646
- data/test/stripe/apple_pay_domain_test.rb +0 -46
- data/test/stripe/application_fee_refund_test.rb +0 -37
- data/test/stripe/application_fee_test.rb +0 -58
- data/test/stripe/balance_test.rb +0 -13
- data/test/stripe/balance_transaction_test.rb +0 -20
- data/test/stripe/bank_account_test.rb +0 -36
- data/test/stripe/billing_portal/session_test.rb +0 -18
- data/test/stripe/capability_test.rb +0 -45
- data/test/stripe/charge_test.rb +0 -64
- data/test/stripe/checkout/session_test.rb +0 -53
- data/test/stripe/connection_manager_test.rb +0 -167
- data/test/stripe/country_spec_test.rb +0 -20
- data/test/stripe/coupon_test.rb +0 -61
- data/test/stripe/credit_note_test.rb +0 -90
- data/test/stripe/customer_balance_transaction_test.rb +0 -37
- data/test/stripe/customer_card_test.rb +0 -48
- data/test/stripe/customer_test.rb +0 -226
- data/test/stripe/dispute_test.rb +0 -51
- data/test/stripe/ephemeral_key_test.rb +0 -93
- data/test/stripe/errors_test.rb +0 -53
- data/test/stripe/exchange_rate_test.rb +0 -20
- data/test/stripe/file_link_test.rb +0 -41
- data/test/stripe/file_test.rb +0 -87
- data/test/stripe/instrumentation_test.rb +0 -74
- data/test/stripe/invoice_item_test.rb +0 -66
- data/test/stripe/invoice_line_item_test.rb +0 -8
- data/test/stripe/invoice_test.rb +0 -229
- data/test/stripe/issuing/authorization_test.rb +0 -72
- data/test/stripe/issuing/card_test.rb +0 -74
- data/test/stripe/issuing/cardholder_test.rb +0 -53
- data/test/stripe/issuing/dispute_test.rb +0 -54
- data/test/stripe/issuing/transaction_test.rb +0 -48
- data/test/stripe/list_object_test.rb +0 -202
- data/test/stripe/login_link_test.rb +0 -37
- data/test/stripe/mandate_test.rb +0 -14
- data/test/stripe/multipart_encoder_test.rb +0 -130
- data/test/stripe/oauth_test.rb +0 -104
- data/test/stripe/order_return_test.rb +0 -21
- data/test/stripe/order_test.rb +0 -82
- data/test/stripe/payment_intent_test.rb +0 -107
- data/test/stripe/payment_method_test.rb +0 -84
- data/test/stripe/payout_test.rb +0 -72
- data/test/stripe/person_test.rb +0 -46
- data/test/stripe/plan_test.rb +0 -98
- data/test/stripe/price_test.rb +0 -48
- data/test/stripe/product_test.rb +0 -58
- data/test/stripe/promotion_code_test.rb +0 -42
- data/test/stripe/radar/early_fraud_warning_test.rb +0 -22
- data/test/stripe/radar/value_list_item_test.rb +0 -48
- data/test/stripe/radar/value_list_test.rb +0 -61
- data/test/stripe/recipient_test.rb +0 -62
- data/test/stripe/refund_test.rb +0 -39
- data/test/stripe/reporting/report_run_test.rb +0 -33
- data/test/stripe/reporting/report_type_test.rb +0 -22
- data/test/stripe/reversal_test.rb +0 -43
- data/test/stripe/review_test.rb +0 -27
- data/test/stripe/setup_attempt_test.rb +0 -16
- data/test/stripe/setup_intent_test.rb +0 -84
- data/test/stripe/sigma/scheduled_query_run_test.rb +0 -22
- data/test/stripe/sku_test.rb +0 -60
- data/test/stripe/source_test.rb +0 -119
- data/test/stripe/stripe_client_test.rb +0 -1291
- data/test/stripe/stripe_configuration_test.rb +0 -131
- data/test/stripe/stripe_object_test.rb +0 -500
- data/test/stripe/stripe_response_test.rb +0 -95
- data/test/stripe/subscription_item_test.rb +0 -84
- data/test/stripe/subscription_schedule_test.rb +0 -82
- data/test/stripe/subscription_test.rb +0 -80
- data/test/stripe/tax_id_test.rb +0 -31
- data/test/stripe/tax_rate_test.rb +0 -43
- data/test/stripe/terminal/connection_token_test.rb +0 -16
- data/test/stripe/terminal/location_test.rb +0 -68
- data/test/stripe/terminal/reader_test.rb +0 -62
- data/test/stripe/three_d_secure_test.rb +0 -23
- data/test/stripe/topup_test.rb +0 -62
- data/test/stripe/transfer_test.rb +0 -88
- data/test/stripe/usage_record_summary_test.rb +0 -29
- data/test/stripe/util_test.rb +0 -402
- data/test/stripe/webhook_endpoint_test.rb +0 -59
- data/test/stripe/webhook_test.rb +0 -135
- data/test/stripe_mock.rb +0 -78
- data/test/stripe_test.rb +0 -132
- data/test/test_data.rb +0 -61
- data/test/test_helper.rb +0 -77
data/lib/stripe/stripe_client.rb
CHANGED
@@ -9,19 +9,36 @@ 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
|
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
|
23
37
|
end
|
24
38
|
|
39
|
+
attr_reader :config
|
40
|
+
attr_reader :options
|
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
|
27
44
|
# inside of that block can find the currently active client. It's reset to
|
@@ -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`.
|
@@ -182,60 +213,9 @@ module Stripe
|
|
182
213
|
|
183
214
|
def execute_request(method, path,
|
184
215
|
api_base: nil, api_key: nil, headers: {}, params: {})
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
unless path.is_a?(String)
|
189
|
-
|
190
|
-
api_base ||= Stripe.api_base
|
191
|
-
api_key ||= Stripe.api_key
|
192
|
-
params = Util.objects_to_ids(params)
|
193
|
-
|
194
|
-
check_api_key!(api_key)
|
195
|
-
|
196
|
-
body_params = nil
|
197
|
-
query_params = nil
|
198
|
-
case method
|
199
|
-
when :get, :head, :delete
|
200
|
-
query_params = params
|
201
|
-
else
|
202
|
-
body_params = params
|
203
|
-
end
|
204
|
-
|
205
|
-
query_params, path = merge_query_params(query_params, path)
|
206
|
-
|
207
|
-
headers = request_headers(api_key, method)
|
208
|
-
.update(Util.normalize_headers(headers))
|
209
|
-
url = api_url(path, api_base)
|
210
|
-
|
211
|
-
# Merge given query parameters with any already encoded in the path.
|
212
|
-
query = query_params ? Util.encode_parameters(query_params) : nil
|
213
|
-
|
214
|
-
# Encoding body parameters is a little more complex because we may have
|
215
|
-
# to send a multipart-encoded body. `body_log` is produced separately as
|
216
|
-
# a log-friendly variant of the encoded form. File objects are displayed
|
217
|
-
# as such instead of as their file contents.
|
218
|
-
body, body_log =
|
219
|
-
body_params ? encode_body(body_params, headers) : [nil, nil]
|
220
|
-
|
221
|
-
# stores information on the request we're about to make so that we don't
|
222
|
-
# have to pass as many parameters around for logging.
|
223
|
-
context = RequestLogContext.new
|
224
|
-
context.account = headers["Stripe-Account"]
|
225
|
-
context.api_key = api_key
|
226
|
-
context.api_version = headers["Stripe-Version"]
|
227
|
-
context.body = body_log
|
228
|
-
context.idempotency_key = headers["Idempotency-Key"]
|
229
|
-
context.method = method
|
230
|
-
context.path = path
|
231
|
-
context.query = query
|
232
|
-
|
233
|
-
http_resp = execute_request_with_rescues(method, api_base, context) do
|
234
|
-
self.class.default_connection_manager.execute_request(method, url,
|
235
|
-
body: body,
|
236
|
-
headers: headers,
|
237
|
-
query: query)
|
238
|
-
end
|
216
|
+
http_resp, api_key = execute_request_internal(
|
217
|
+
method, path, api_base, api_key, headers, params
|
218
|
+
)
|
239
219
|
|
240
220
|
begin
|
241
221
|
resp = StripeResponse.from_net_http(http_resp)
|
@@ -246,13 +226,53 @@ module Stripe
|
|
246
226
|
# If being called from `StripeClient#request`, put the last response in
|
247
227
|
# thread-local memory so that it can be returned to the user. Don't store
|
248
228
|
# anything otherwise so that we don't leak memory.
|
249
|
-
|
250
|
-
|
229
|
+
store_last_response(object_id, resp)
|
230
|
+
|
231
|
+
[resp, api_key]
|
232
|
+
end
|
233
|
+
|
234
|
+
# Executes a request and returns the body as a stream instead of converting
|
235
|
+
# it to a StripeObject. This should be used for any request where we expect
|
236
|
+
# an arbitrary binary response.
|
237
|
+
#
|
238
|
+
# A `read_body_chunk` block can be passed, which will be called repeatedly
|
239
|
+
# with the body chunks read from the socket.
|
240
|
+
#
|
241
|
+
# If a block is passed, a StripeHeadersOnlyResponse is returned as the
|
242
|
+
# block is expected to do all the necessary body processing. If no block is
|
243
|
+
# passed, then a StripeStreamResponse is returned containing an IO stream
|
244
|
+
# with the response body.
|
245
|
+
def execute_request_stream(method, path,
|
246
|
+
api_base: nil, api_key: nil,
|
247
|
+
headers: {}, params: {},
|
248
|
+
&read_body_chunk_block)
|
249
|
+
unless block_given?
|
250
|
+
raise ArgumentError,
|
251
|
+
"execute_request_stream requires a read_body_chunk_block"
|
251
252
|
end
|
252
253
|
|
254
|
+
http_resp, api_key = execute_request_internal(
|
255
|
+
method, path, api_base, api_key, headers, params, &read_body_chunk_block
|
256
|
+
)
|
257
|
+
|
258
|
+
# When the read_body_chunk_block is given, we no longer have access to the
|
259
|
+
# response body at this point and so return a response object containing
|
260
|
+
# only the headers. This is because the body was consumed by the block.
|
261
|
+
resp = StripeHeadersOnlyResponse.from_net_http(http_resp)
|
262
|
+
|
253
263
|
[resp, api_key]
|
254
264
|
end
|
255
265
|
|
266
|
+
def store_last_response(object_id, resp)
|
267
|
+
return unless last_response_has_key?(object_id)
|
268
|
+
|
269
|
+
self.class.current_thread_context.last_responses[object_id] = resp
|
270
|
+
end
|
271
|
+
|
272
|
+
def last_response_has_key?(object_id)
|
273
|
+
self.class.current_thread_context.last_responses&.key?(object_id)
|
274
|
+
end
|
275
|
+
|
256
276
|
#
|
257
277
|
# private
|
258
278
|
#
|
@@ -328,11 +348,6 @@ module Stripe
|
|
328
348
|
# the user hasn't specified their own.
|
329
349
|
attr_accessor :default_client
|
330
350
|
|
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
351
|
# A temporary map of object IDs to responses from last executed API
|
337
352
|
# calls. Used to return a responses from calls to `StripeClient#request`.
|
338
353
|
#
|
@@ -345,6 +360,17 @@ module Stripe
|
|
345
360
|
# because that's wrapped in an `ensure` block, they should never leave
|
346
361
|
# garbage in `Thread.current`.
|
347
362
|
attr_accessor :last_responses
|
363
|
+
|
364
|
+
# A map of connection mangers for the thread. Normally shared between
|
365
|
+
# all `StripeClient` objects on a particular thread, and created so as to
|
366
|
+
# minimize the number of open connections that an application needs.
|
367
|
+
def default_connection_managers
|
368
|
+
@default_connection_managers ||= {}
|
369
|
+
end
|
370
|
+
|
371
|
+
def reset_connection_managers
|
372
|
+
@default_connection_managers = {}
|
373
|
+
end
|
348
374
|
end
|
349
375
|
|
350
376
|
# Access data stored for `StripeClient` within the thread's current
|
@@ -380,24 +406,110 @@ module Stripe
|
|
380
406
|
last_used_threshold =
|
381
407
|
Util.monotonic_time - CONNECTION_MANAGER_GC_LAST_USED_EXPIRY
|
382
408
|
|
383
|
-
|
409
|
+
pruned_contexts = []
|
410
|
+
@thread_contexts_with_connection_managers.each do |thread_context|
|
411
|
+
thread_context
|
412
|
+
.default_connection_managers
|
413
|
+
.each do |config_key, connection_manager|
|
414
|
+
next if connection_manager.last_used > last_used_threshold
|
415
|
+
|
416
|
+
connection_manager.clear
|
417
|
+
thread_context.default_connection_managers.delete(config_key)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
384
421
|
@thread_contexts_with_connection_managers.each do |thread_context|
|
385
|
-
|
386
|
-
next if connection_manager.last_used > last_used_threshold
|
422
|
+
next unless thread_context.default_connection_managers.empty?
|
387
423
|
|
388
|
-
|
389
|
-
thread_context.default_connection_manager = nil
|
390
|
-
pruned_thread_contexts << thread_context
|
424
|
+
pruned_contexts << thread_context
|
391
425
|
end
|
392
426
|
|
393
|
-
@thread_contexts_with_connection_managers -=
|
427
|
+
@thread_contexts_with_connection_managers -= pruned_contexts
|
394
428
|
@last_connection_manager_gc = Util.monotonic_time
|
395
429
|
|
396
|
-
|
430
|
+
pruned_contexts.count
|
431
|
+
end
|
432
|
+
|
433
|
+
private def execute_request_internal(method, path,
|
434
|
+
api_base, api_key, headers, params,
|
435
|
+
&read_body_chunk_block)
|
436
|
+
raise ArgumentError, "method should be a symbol" \
|
437
|
+
unless method.is_a?(Symbol)
|
438
|
+
raise ArgumentError, "path should be a string" \
|
439
|
+
unless path.is_a?(String)
|
440
|
+
|
441
|
+
api_base ||= config.api_base
|
442
|
+
api_key ||= config.api_key
|
443
|
+
params = Util.objects_to_ids(params)
|
444
|
+
|
445
|
+
check_api_key!(api_key)
|
446
|
+
|
447
|
+
body_params = nil
|
448
|
+
query_params = nil
|
449
|
+
case method
|
450
|
+
when :get, :head, :delete
|
451
|
+
query_params = params
|
452
|
+
else
|
453
|
+
body_params = params
|
454
|
+
end
|
455
|
+
|
456
|
+
query_params, path = merge_query_params(query_params, path)
|
457
|
+
|
458
|
+
headers = request_headers(api_key, method)
|
459
|
+
.update(Util.normalize_headers(headers))
|
460
|
+
url = api_url(path, api_base)
|
461
|
+
|
462
|
+
# Merge given query parameters with any already encoded in the path.
|
463
|
+
query = query_params ? Util.encode_parameters(query_params) : nil
|
464
|
+
|
465
|
+
# Encoding body parameters is a little more complex because we may have
|
466
|
+
# to send a multipart-encoded body. `body_log` is produced separately as
|
467
|
+
# a log-friendly variant of the encoded form. File objects are displayed
|
468
|
+
# as such instead of as their file contents.
|
469
|
+
body, body_log =
|
470
|
+
body_params ? encode_body(body_params, headers) : [nil, nil]
|
471
|
+
|
472
|
+
# stores information on the request we're about to make so that we don't
|
473
|
+
# have to pass as many parameters around for logging.
|
474
|
+
context = RequestLogContext.new
|
475
|
+
context.account = headers["Stripe-Account"]
|
476
|
+
context.api_key = api_key
|
477
|
+
context.api_version = headers["Stripe-Version"]
|
478
|
+
context.body = body_log
|
479
|
+
context.idempotency_key = headers["Idempotency-Key"]
|
480
|
+
context.method = method
|
481
|
+
context.path = path
|
482
|
+
context.query = query
|
483
|
+
|
484
|
+
# A block can be passed in to read the content directly from the response.
|
485
|
+
# We want to execute this block only when the response was actually
|
486
|
+
# successful. When it wasn't, we defer to the standard error handling as
|
487
|
+
# we have to read the body and parse the error JSON.
|
488
|
+
response_block =
|
489
|
+
if block_given?
|
490
|
+
lambda do |response|
|
491
|
+
unless should_handle_as_error(response.code.to_i)
|
492
|
+
response.read_body(&read_body_chunk_block)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
http_resp =
|
498
|
+
execute_request_with_rescues(method, api_base, headers, context) do
|
499
|
+
self.class
|
500
|
+
.default_connection_manager(config)
|
501
|
+
.execute_request(method, url,
|
502
|
+
body: body,
|
503
|
+
headers: headers,
|
504
|
+
query: query,
|
505
|
+
&response_block)
|
506
|
+
end
|
507
|
+
|
508
|
+
[http_resp, api_key]
|
397
509
|
end
|
398
510
|
|
399
511
|
private def api_url(url = "", api_base = nil)
|
400
|
-
(api_base ||
|
512
|
+
(api_base || config.api_base) + url
|
401
513
|
end
|
402
514
|
|
403
515
|
private def check_api_key!(api_key)
|
@@ -435,6 +547,7 @@ module Stripe
|
|
435
547
|
# that's more condusive to logging.
|
436
548
|
flattened_params =
|
437
549
|
flattened_params.map { |k, v| [k, v.is_a?(String) ? v : v.to_s] }.to_h
|
550
|
+
|
438
551
|
else
|
439
552
|
body = Util.encode_parameters(body_params)
|
440
553
|
end
|
@@ -448,7 +561,11 @@ module Stripe
|
|
448
561
|
[body, body_log]
|
449
562
|
end
|
450
563
|
|
451
|
-
private def
|
564
|
+
private def should_handle_as_error(http_status)
|
565
|
+
http_status >= 400
|
566
|
+
end
|
567
|
+
|
568
|
+
private def execute_request_with_rescues(method, api_base, headers, context)
|
452
569
|
num_retries = 0
|
453
570
|
|
454
571
|
begin
|
@@ -465,13 +582,15 @@ module Stripe
|
|
465
582
|
http_status = resp.code.to_i
|
466
583
|
context = context.dup_from_response_headers(resp)
|
467
584
|
|
468
|
-
|
585
|
+
if should_handle_as_error(http_status)
|
586
|
+
handle_error_response(resp, context)
|
587
|
+
end
|
469
588
|
|
470
|
-
log_response(context, request_start, http_status, resp.body)
|
589
|
+
log_response(context, request_start, http_status, resp.body, resp)
|
471
590
|
notify_request_end(context, request_duration, http_status,
|
472
|
-
num_retries, user_data)
|
591
|
+
num_retries, user_data, resp, headers)
|
473
592
|
|
474
|
-
if
|
593
|
+
if config.enable_telemetry? && context.request_id
|
475
594
|
request_duration_ms = (request_duration * 1000).to_i
|
476
595
|
@last_request_metrics =
|
477
596
|
StripeRequestMetrics.new(context.request_id, request_duration_ms)
|
@@ -491,16 +610,19 @@ module Stripe
|
|
491
610
|
error_context = context.dup_from_response_headers(e.http_headers)
|
492
611
|
http_status = resp.code.to_i
|
493
612
|
log_response(error_context, request_start,
|
494
|
-
e.http_status, e.http_body)
|
613
|
+
e.http_status, e.http_body, resp)
|
495
614
|
else
|
496
615
|
log_response_error(error_context, request_start, e)
|
497
616
|
end
|
498
617
|
notify_request_end(context, request_duration, http_status, num_retries,
|
499
|
-
user_data)
|
618
|
+
user_data, resp, headers)
|
500
619
|
|
501
|
-
if self.class.should_retry?(e,
|
620
|
+
if self.class.should_retry?(e,
|
621
|
+
method: method,
|
622
|
+
num_retries: num_retries,
|
623
|
+
config: config)
|
502
624
|
num_retries += 1
|
503
|
-
sleep self.class.sleep_time(num_retries)
|
625
|
+
sleep self.class.sleep_time(num_retries, config: config)
|
504
626
|
retry
|
505
627
|
end
|
506
628
|
|
@@ -536,16 +658,24 @@ module Stripe
|
|
536
658
|
end
|
537
659
|
|
538
660
|
private def notify_request_end(context, duration, http_status, num_retries,
|
539
|
-
user_data)
|
661
|
+
user_data, resp, headers)
|
540
662
|
return if !Instrumentation.any_subscribers?(:request_end) &&
|
541
663
|
!Instrumentation.any_subscribers?(:request)
|
542
664
|
|
543
|
-
|
665
|
+
request_context = Stripe::Instrumentation::RequestContext.new(
|
544
666
|
duration: duration,
|
667
|
+
context: context,
|
668
|
+
header: headers
|
669
|
+
)
|
670
|
+
response_context = Stripe::Instrumentation::ResponseContext.new(
|
545
671
|
http_status: http_status,
|
546
|
-
|
672
|
+
response: resp
|
673
|
+
)
|
674
|
+
|
675
|
+
event = Instrumentation::RequestEndEvent.new(
|
676
|
+
request_context: request_context,
|
677
|
+
response_context: response_context,
|
547
678
|
num_retries: num_retries,
|
548
|
-
path: context.path,
|
549
679
|
user_data: user_data || {}
|
550
680
|
)
|
551
681
|
Stripe::Instrumentation.notify(:request_end, event)
|
@@ -622,7 +752,8 @@ module Stripe
|
|
622
752
|
error_param: error_data[:param],
|
623
753
|
error_type: error_data[:type],
|
624
754
|
idempotency_key: context.idempotency_key,
|
625
|
-
request_id: context.request_id
|
755
|
+
request_id: context.request_id,
|
756
|
+
config: config)
|
626
757
|
|
627
758
|
# The standard set of arguments that can be used to initialize most of
|
628
759
|
# the exceptions.
|
@@ -671,7 +802,8 @@ module Stripe
|
|
671
802
|
error_code: error_code,
|
672
803
|
error_description: description,
|
673
804
|
idempotency_key: context.idempotency_key,
|
674
|
-
request_id: context.request_id
|
805
|
+
request_id: context.request_id,
|
806
|
+
config: config)
|
675
807
|
|
676
808
|
args = {
|
677
809
|
http_status: resp.http_status, http_body: resp.http_body,
|
@@ -703,7 +835,8 @@ module Stripe
|
|
703
835
|
Util.log_error("Stripe network error",
|
704
836
|
error_message: error.message,
|
705
837
|
idempotency_key: context.idempotency_key,
|
706
|
-
request_id: context.request_id
|
838
|
+
request_id: context.request_id,
|
839
|
+
config: config)
|
707
840
|
|
708
841
|
errors, message = NETWORK_ERROR_MESSAGES_MAP.detect do |(e, _)|
|
709
842
|
error.is_a?(e)
|
@@ -714,7 +847,7 @@ module Stripe
|
|
714
847
|
"with Stripe. Please let us know at support@stripe.com."
|
715
848
|
end
|
716
849
|
|
717
|
-
api_base ||=
|
850
|
+
api_base ||= config.api_base
|
718
851
|
message = message % api_base
|
719
852
|
|
720
853
|
message += " Request was retried #{num_retries} times." if num_retries > 0
|
@@ -735,7 +868,7 @@ module Stripe
|
|
735
868
|
"Content-Type" => "application/x-www-form-urlencoded",
|
736
869
|
}
|
737
870
|
|
738
|
-
if
|
871
|
+
if config.enable_telemetry? && !@last_request_metrics.nil?
|
739
872
|
headers["X-Stripe-Client-Telemetry"] = JSON.generate(
|
740
873
|
last_request_metrics: @last_request_metrics.payload
|
741
874
|
)
|
@@ -743,12 +876,12 @@ module Stripe
|
|
743
876
|
|
744
877
|
# It is only safe to retry network failures on post and delete
|
745
878
|
# requests if we add an Idempotency-Key header
|
746
|
-
if %i[post delete].include?(method) &&
|
879
|
+
if %i[post delete].include?(method) && config.max_network_retries > 0
|
747
880
|
headers["Idempotency-Key"] ||= SecureRandom.uuid
|
748
881
|
end
|
749
882
|
|
750
|
-
headers["Stripe-Version"] =
|
751
|
-
headers["Stripe-Account"] =
|
883
|
+
headers["Stripe-Version"] = config.api_version if config.api_version
|
884
|
+
headers["Stripe-Account"] = config.stripe_account if config.stripe_account
|
752
885
|
|
753
886
|
user_agent = @system_profiler.user_agent
|
754
887
|
begin
|
@@ -772,14 +905,19 @@ module Stripe
|
|
772
905
|
idempotency_key: context.idempotency_key,
|
773
906
|
method: context.method,
|
774
907
|
num_retries: num_retries,
|
775
|
-
path: context.path
|
908
|
+
path: context.path,
|
909
|
+
config: config)
|
776
910
|
Util.log_debug("Request details",
|
777
911
|
body: context.body,
|
778
912
|
idempotency_key: context.idempotency_key,
|
779
|
-
query: context.query
|
913
|
+
query: context.query,
|
914
|
+
config: config,
|
915
|
+
process_id: Process.pid,
|
916
|
+
thread_object_id: Thread.current.object_id,
|
917
|
+
log_timestamp: Util.monotonic_time)
|
780
918
|
end
|
781
919
|
|
782
|
-
private def log_response(context, request_start, status, body)
|
920
|
+
private def log_response(context, request_start, status, body, resp)
|
783
921
|
Util.log_info("Response from Stripe API",
|
784
922
|
account: context.account,
|
785
923
|
api_version: context.api_version,
|
@@ -788,11 +926,17 @@ module Stripe
|
|
788
926
|
method: context.method,
|
789
927
|
path: context.path,
|
790
928
|
request_id: context.request_id,
|
791
|
-
status: status
|
929
|
+
status: status,
|
930
|
+
config: config)
|
792
931
|
Util.log_debug("Response details",
|
793
932
|
body: body,
|
794
933
|
idempotency_key: context.idempotency_key,
|
795
|
-
request_id: context.request_id
|
934
|
+
request_id: context.request_id,
|
935
|
+
config: config,
|
936
|
+
process_id: Process.pid,
|
937
|
+
thread_object_id: Thread.current.object_id,
|
938
|
+
response_object_id: resp.object_id,
|
939
|
+
log_timestamp: Util.monotonic_time)
|
796
940
|
|
797
941
|
return unless context.request_id
|
798
942
|
|
@@ -800,7 +944,8 @@ module Stripe
|
|
800
944
|
idempotency_key: context.idempotency_key,
|
801
945
|
request_id: context.request_id,
|
802
946
|
url: Util.request_id_dashboard_url(context.request_id,
|
803
|
-
context.api_key)
|
947
|
+
context.api_key),
|
948
|
+
config: config)
|
804
949
|
end
|
805
950
|
|
806
951
|
private def log_response_error(context, request_start, error)
|
@@ -810,7 +955,8 @@ module Stripe
|
|
810
955
|
error_message: error.message,
|
811
956
|
idempotency_key: context.idempotency_key,
|
812
957
|
method: context.method,
|
813
|
-
path: context.path
|
958
|
+
path: context.path,
|
959
|
+
config: config)
|
814
960
|
end
|
815
961
|
|
816
962
|
# RequestLogContext stores information about a request that's begin made so
|