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.
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