stripe 5.28.0 → 8.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +286 -0
  3. data/Gemfile +5 -3
  4. data/Makefile +13 -0
  5. data/OPENAPI_VERSION +1 -0
  6. data/README.md +43 -5
  7. data/VERSION +1 -1
  8. data/lib/data/ca-certificates.crt +1241 -1937
  9. data/lib/stripe/api_operations/create.rb +6 -2
  10. data/lib/stripe/api_operations/delete.rb +12 -7
  11. data/lib/stripe/api_operations/list.rb +6 -9
  12. data/lib/stripe/api_operations/nested_resource.rb +62 -34
  13. data/lib/stripe/api_operations/request.rb +45 -2
  14. data/lib/stripe/api_operations/save.rb +13 -3
  15. data/lib/stripe/api_operations/search.rb +16 -0
  16. data/lib/stripe/api_resource.rb +11 -18
  17. data/lib/stripe/api_resource_test_helpers.rb +49 -0
  18. data/lib/stripe/api_version.rb +8 -0
  19. data/lib/stripe/connection_manager.rb +49 -11
  20. data/lib/stripe/error_object.rb +2 -3
  21. data/lib/stripe/instrumentation.rb +46 -6
  22. data/lib/stripe/oauth.rb +4 -3
  23. data/lib/stripe/object_types.rb +36 -10
  24. data/lib/stripe/resources/account.rb +39 -17
  25. data/lib/stripe/resources/account_link.rb +4 -0
  26. data/lib/stripe/resources/application_fee_refund.rb +7 -1
  27. data/lib/stripe/resources/apps/secret.rb +40 -0
  28. data/lib/stripe/resources/balance.rb +11 -0
  29. data/lib/stripe/resources/balance_transaction.rb +4 -0
  30. data/lib/stripe/resources/bank_account.rb +7 -0
  31. data/lib/stripe/resources/billing_portal/configuration.rb +15 -0
  32. data/lib/stripe/resources/billing_portal/session.rb +14 -0
  33. data/lib/stripe/resources/capability.rb +5 -1
  34. data/lib/stripe/resources/card.rb +6 -3
  35. data/lib/stripe/resources/cash_balance.rb +23 -0
  36. data/lib/stripe/resources/charge.rb +23 -3
  37. data/lib/stripe/resources/checkout/session.rb +49 -2
  38. data/lib/stripe/resources/country_spec.rb +6 -0
  39. data/lib/stripe/resources/coupon.rb +3 -0
  40. data/lib/stripe/resources/credit_note.rb +27 -9
  41. data/lib/stripe/resources/credit_note_line_item.rb +2 -0
  42. data/lib/stripe/resources/customer.rb +132 -4
  43. data/lib/stripe/resources/customer_balance_transaction.rb +9 -2
  44. data/lib/stripe/resources/customer_cash_balance_transaction.rb +14 -0
  45. data/lib/stripe/resources/discount.rb +5 -0
  46. data/lib/stripe/resources/dispute.rb +17 -3
  47. data/lib/stripe/resources/event.rb +29 -0
  48. data/lib/stripe/resources/exchange_rate.rb +11 -0
  49. data/lib/stripe/resources/file.rb +9 -1
  50. data/lib/stripe/resources/file_link.rb +3 -0
  51. data/lib/stripe/resources/financial_connections/account.rb +67 -0
  52. data/lib/stripe/resources/financial_connections/account_owner.rb +11 -0
  53. data/lib/stripe/resources/financial_connections/account_ownership.rb +11 -0
  54. data/lib/stripe/resources/financial_connections/session.rb +13 -0
  55. data/lib/stripe/resources/funding_instructions.rb +21 -0
  56. data/lib/stripe/resources/identity/verification_report.rb +23 -0
  57. data/lib/stripe/resources/identity/verification_session.rb +61 -0
  58. data/lib/stripe/resources/invoice.rb +105 -17
  59. data/lib/stripe/resources/invoice_item.rb +11 -0
  60. data/lib/stripe/resources/invoice_line_item.rb +1 -0
  61. data/lib/stripe/resources/issuing/authorization.rb +25 -5
  62. data/lib/stripe/resources/issuing/card.rb +78 -8
  63. data/lib/stripe/resources/issuing/cardholder.rb +3 -0
  64. data/lib/stripe/resources/issuing/dispute.rb +13 -3
  65. data/lib/stripe/resources/issuing/transaction.rb +5 -0
  66. data/lib/stripe/resources/line_item.rb +2 -0
  67. data/lib/stripe/resources/login_link.rb +2 -0
  68. data/lib/stripe/resources/mandate.rb +1 -0
  69. data/lib/stripe/resources/payment_intent.rb +103 -6
  70. data/lib/stripe/resources/payment_link.rb +35 -0
  71. data/lib/stripe/resources/payment_method.rb +25 -5
  72. data/lib/stripe/resources/payout.rb +28 -5
  73. data/lib/stripe/resources/person.rb +7 -0
  74. data/lib/stripe/resources/plan.rb +8 -0
  75. data/lib/stripe/resources/price.rb +15 -0
  76. data/lib/stripe/resources/product.rb +17 -0
  77. data/lib/stripe/resources/promotion_code.rb +2 -0
  78. data/lib/stripe/resources/quote.rb +128 -0
  79. data/lib/stripe/resources/radar/early_fraud_warning.rb +4 -0
  80. data/lib/stripe/resources/radar/value_list.rb +3 -0
  81. data/lib/stripe/resources/radar/value_list_item.rb +3 -0
  82. data/lib/stripe/resources/refund.rb +49 -0
  83. data/lib/stripe/resources/reporting/report_run.rb +8 -0
  84. data/lib/stripe/resources/reporting/report_type.rb +8 -1
  85. data/lib/stripe/resources/reversal.rb +16 -2
  86. data/lib/stripe/resources/review.rb +14 -3
  87. data/lib/stripe/resources/setup_attempt.rb +4 -0
  88. data/lib/stripe/resources/setup_intent.rb +60 -5
  89. data/lib/stripe/resources/shipping_rate.rb +14 -0
  90. data/lib/stripe/resources/sigma/scheduled_query_run.rb +4 -0
  91. data/lib/stripe/resources/source.rb +26 -6
  92. data/lib/stripe/resources/source_transaction.rb +5 -0
  93. data/lib/stripe/resources/subscription.rb +57 -3
  94. data/lib/stripe/resources/subscription_item.rb +2 -7
  95. data/lib/stripe/resources/subscription_schedule.rb +23 -5
  96. data/lib/stripe/resources/tax/calculation.rb +33 -0
  97. data/lib/stripe/resources/tax/calculation_line_item.rb +10 -0
  98. data/lib/stripe/resources/tax/settings.rb +15 -0
  99. data/lib/stripe/resources/tax/transaction.rb +49 -0
  100. data/lib/stripe/resources/tax/transaction_line_item.rb +10 -0
  101. data/lib/stripe/resources/tax_code.rb +11 -0
  102. data/lib/stripe/resources/tax_id.rb +5 -0
  103. data/lib/stripe/resources/tax_rate.rb +3 -0
  104. data/lib/stripe/resources/terminal/configuration.rb +16 -0
  105. data/lib/stripe/resources/terminal/connection_token.rb +3 -0
  106. data/lib/stripe/resources/terminal/location.rb +3 -0
  107. data/lib/stripe/resources/terminal/reader.rb +119 -0
  108. data/lib/stripe/resources/test_helpers/test_clock.rb +35 -0
  109. data/lib/stripe/resources/token.rb +20 -0
  110. data/lib/stripe/resources/topup.rb +15 -3
  111. data/lib/stripe/resources/transfer.rb +10 -11
  112. data/lib/stripe/resources/treasury/credit_reversal.rb +14 -0
  113. data/lib/stripe/resources/treasury/debit_reversal.rb +14 -0
  114. data/lib/stripe/resources/treasury/financial_account.rb +52 -0
  115. data/lib/stripe/resources/treasury/financial_account_features.rb +12 -0
  116. data/lib/stripe/resources/treasury/inbound_transfer.rb +94 -0
  117. data/lib/stripe/resources/treasury/outbound_payment.rb +96 -0
  118. data/lib/stripe/resources/treasury/outbound_transfer.rb +100 -0
  119. data/lib/stripe/resources/treasury/received_credit.rb +30 -0
  120. data/lib/stripe/resources/treasury/received_debit.rb +30 -0
  121. data/lib/stripe/resources/treasury/transaction.rb +13 -0
  122. data/lib/stripe/resources/treasury/transaction_entry.rb +17 -0
  123. data/lib/stripe/resources/usage_record.rb +5 -0
  124. data/lib/stripe/resources/usage_record_summary.rb +1 -0
  125. data/lib/stripe/resources/webhook_endpoint.rb +7 -0
  126. data/lib/stripe/resources.rb +34 -10
  127. data/lib/stripe/search_result_object.rb +86 -0
  128. data/lib/stripe/stripe_client.rb +267 -121
  129. data/lib/stripe/stripe_configuration.rb +29 -10
  130. data/lib/stripe/stripe_object.rb +25 -2
  131. data/lib/stripe/stripe_response.rb +80 -52
  132. data/lib/stripe/util.rb +100 -9
  133. data/lib/stripe/version.rb +1 -1
  134. data/lib/stripe.rb +27 -23
  135. data/stripe.gemspec +12 -5
  136. metadata +46 -204
  137. data/.editorconfig +0 -10
  138. data/.gitattributes +0 -4
  139. data/.github/ISSUE_TEMPLATE.md +0 -5
  140. data/.gitignore +0 -8
  141. data/.rubocop.yml +0 -80
  142. data/.rubocop_todo.yml +0 -33
  143. data/.travis.yml +0 -38
  144. data/.vscode/extensions.json +0 -7
  145. data/.vscode/settings.json +0 -8
  146. data/lib/stripe/resources/bitcoin_receiver.rb +0 -24
  147. data/lib/stripe/resources/bitcoin_transaction.rb +0 -15
  148. data/lib/stripe/resources/issuing/card_details.rb +0 -9
  149. data/lib/stripe/resources/order.rb +0 -33
  150. data/lib/stripe/resources/order_return.rb +0 -10
  151. data/lib/stripe/resources/recipient.rb +0 -14
  152. data/lib/stripe/resources/sku.rb +0 -13
  153. data/lib/stripe/resources/three_d_secure.rb +0 -14
  154. data/test/openapi/README.md +0 -9
  155. data/test/stripe/account_link_test.rb +0 -18
  156. data/test/stripe/account_test.rb +0 -412
  157. data/test/stripe/alipay_account_test.rb +0 -37
  158. data/test/stripe/api_operations_test.rb +0 -80
  159. data/test/stripe/api_resource_test.rb +0 -646
  160. data/test/stripe/apple_pay_domain_test.rb +0 -46
  161. data/test/stripe/application_fee_refund_test.rb +0 -37
  162. data/test/stripe/application_fee_test.rb +0 -58
  163. data/test/stripe/balance_test.rb +0 -13
  164. data/test/stripe/balance_transaction_test.rb +0 -20
  165. data/test/stripe/bank_account_test.rb +0 -36
  166. data/test/stripe/billing_portal/session_test.rb +0 -18
  167. data/test/stripe/capability_test.rb +0 -45
  168. data/test/stripe/charge_test.rb +0 -64
  169. data/test/stripe/checkout/session_test.rb +0 -53
  170. data/test/stripe/connection_manager_test.rb +0 -167
  171. data/test/stripe/country_spec_test.rb +0 -20
  172. data/test/stripe/coupon_test.rb +0 -61
  173. data/test/stripe/credit_note_test.rb +0 -90
  174. data/test/stripe/customer_balance_transaction_test.rb +0 -37
  175. data/test/stripe/customer_card_test.rb +0 -48
  176. data/test/stripe/customer_test.rb +0 -226
  177. data/test/stripe/dispute_test.rb +0 -51
  178. data/test/stripe/ephemeral_key_test.rb +0 -93
  179. data/test/stripe/errors_test.rb +0 -53
  180. data/test/stripe/exchange_rate_test.rb +0 -20
  181. data/test/stripe/file_link_test.rb +0 -41
  182. data/test/stripe/file_test.rb +0 -87
  183. data/test/stripe/instrumentation_test.rb +0 -74
  184. data/test/stripe/invoice_item_test.rb +0 -66
  185. data/test/stripe/invoice_line_item_test.rb +0 -8
  186. data/test/stripe/invoice_test.rb +0 -229
  187. data/test/stripe/issuing/authorization_test.rb +0 -72
  188. data/test/stripe/issuing/card_test.rb +0 -74
  189. data/test/stripe/issuing/cardholder_test.rb +0 -53
  190. data/test/stripe/issuing/dispute_test.rb +0 -54
  191. data/test/stripe/issuing/transaction_test.rb +0 -48
  192. data/test/stripe/list_object_test.rb +0 -202
  193. data/test/stripe/login_link_test.rb +0 -37
  194. data/test/stripe/mandate_test.rb +0 -14
  195. data/test/stripe/multipart_encoder_test.rb +0 -130
  196. data/test/stripe/oauth_test.rb +0 -104
  197. data/test/stripe/order_return_test.rb +0 -21
  198. data/test/stripe/order_test.rb +0 -82
  199. data/test/stripe/payment_intent_test.rb +0 -107
  200. data/test/stripe/payment_method_test.rb +0 -84
  201. data/test/stripe/payout_test.rb +0 -72
  202. data/test/stripe/person_test.rb +0 -46
  203. data/test/stripe/plan_test.rb +0 -98
  204. data/test/stripe/price_test.rb +0 -48
  205. data/test/stripe/product_test.rb +0 -58
  206. data/test/stripe/promotion_code_test.rb +0 -42
  207. data/test/stripe/radar/early_fraud_warning_test.rb +0 -22
  208. data/test/stripe/radar/value_list_item_test.rb +0 -48
  209. data/test/stripe/radar/value_list_test.rb +0 -61
  210. data/test/stripe/recipient_test.rb +0 -62
  211. data/test/stripe/refund_test.rb +0 -39
  212. data/test/stripe/reporting/report_run_test.rb +0 -33
  213. data/test/stripe/reporting/report_type_test.rb +0 -22
  214. data/test/stripe/reversal_test.rb +0 -43
  215. data/test/stripe/review_test.rb +0 -27
  216. data/test/stripe/setup_attempt_test.rb +0 -16
  217. data/test/stripe/setup_intent_test.rb +0 -84
  218. data/test/stripe/sigma/scheduled_query_run_test.rb +0 -22
  219. data/test/stripe/sku_test.rb +0 -60
  220. data/test/stripe/source_test.rb +0 -119
  221. data/test/stripe/stripe_client_test.rb +0 -1291
  222. data/test/stripe/stripe_configuration_test.rb +0 -131
  223. data/test/stripe/stripe_object_test.rb +0 -500
  224. data/test/stripe/stripe_response_test.rb +0 -95
  225. data/test/stripe/subscription_item_test.rb +0 -84
  226. data/test/stripe/subscription_schedule_test.rb +0 -82
  227. data/test/stripe/subscription_test.rb +0 -80
  228. data/test/stripe/tax_id_test.rb +0 -31
  229. data/test/stripe/tax_rate_test.rb +0 -43
  230. data/test/stripe/terminal/connection_token_test.rb +0 -16
  231. data/test/stripe/terminal/location_test.rb +0 -68
  232. data/test/stripe/terminal/reader_test.rb +0 -62
  233. data/test/stripe/three_d_secure_test.rb +0 -23
  234. data/test/stripe/topup_test.rb +0 -62
  235. data/test/stripe/transfer_test.rb +0 -88
  236. data/test/stripe/usage_record_summary_test.rb +0 -29
  237. data/test/stripe/util_test.rb +0 -402
  238. data/test/stripe/webhook_endpoint_test.rb +0 -59
  239. data/test/stripe/webhook_test.rb +0 -135
  240. data/test/stripe_mock.rb +0 -78
  241. data/test/stripe_test.rb +0 -132
  242. data/test/test_data.rb +0 -61
  243. data/test/test_helper.rb +0 -77
@@ -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 `StripeClient`.
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.default_connection_manager.clear
55
- thread_context.default_connection_manager = nil
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
- @thread_contexts_with_connection_managers.clear
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
- def self.default_connection_manager
68
- current_thread_context.default_connection_manager ||= begin
69
- connection_manager = ConnectionManager.new
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, method:, num_retries:)
84
- return false if num_retries >= Stripe.max_network_retries
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
- Stripe.initial_network_retry_delay * (2**(num_retries - 1)),
136
- Stripe.max_network_retry_delay,
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
- sleep_seconds = [Stripe.initial_network_retry_delay, sleep_seconds].max
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
- raise ArgumentError, "method should be a symbol" \
186
- unless method.is_a?(Symbol)
187
- raise ArgumentError, "path should be a string" \
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
- if self.class.current_thread_context.last_responses&.key?(object_id)
250
- self.class.current_thread_context.last_responses[object_id] = resp
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
- pruned_thread_contexts = []
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
- connection_manager = thread_context.default_connection_manager
386
- next if connection_manager.last_used > last_used_threshold
422
+ next unless thread_context.default_connection_managers.empty?
387
423
 
388
- connection_manager.clear
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 -= pruned_thread_contexts
427
+ @thread_contexts_with_connection_managers -= pruned_contexts
394
428
  @last_connection_manager_gc = Util.monotonic_time
395
429
 
396
- pruned_thread_contexts.count
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 || Stripe.api_base) + url
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 execute_request_with_rescues(method, api_base, context)
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
- handle_error_response(resp, context) if http_status >= 400
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 Stripe.enable_telemetry? && context.request_id
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, method: method, num_retries: num_retries)
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
- event = Instrumentation::RequestEndEvent.new(
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
- method: context.method,
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 ||= Stripe.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 Stripe.enable_telemetry? && !@last_request_metrics.nil?
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) && Stripe.max_network_retries > 0
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"] = Stripe.api_version if Stripe.api_version
751
- headers["Stripe-Account"] = Stripe.stripe_account if Stripe.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