stripe 12.7.0.pre.beta.1 → 13.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (257) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +725 -1354
  3. data/OPENAPI_VERSION +1 -1
  4. data/README.md +4 -3
  5. data/VERSION +1 -1
  6. data/examples/README.md +11 -0
  7. data/examples/meter_event_stream.rb +47 -0
  8. data/examples/new_example.rb +24 -0
  9. data/examples/stripe_webhook_handler.rb +28 -0
  10. data/lib/stripe/api_operations/nested_resource.rb +1 -21
  11. data/lib/stripe/api_operations/request.rb +19 -70
  12. data/lib/stripe/api_operations/save.rb +4 -3
  13. data/lib/stripe/api_operations/singleton_save.rb +5 -2
  14. data/lib/stripe/api_requestor.rb +1131 -0
  15. data/lib/stripe/api_resource.rb +22 -14
  16. data/lib/stripe/api_version.rb +1 -2
  17. data/lib/stripe/connection_manager.rb +1 -1
  18. data/lib/stripe/errors.rb +8 -2
  19. data/lib/stripe/event_types.rb +14 -0
  20. data/lib/stripe/events/v1_billing_meter_error_report_triggered_event.rb +23 -0
  21. data/lib/stripe/events/v1_billing_meter_no_meter_found_event.rb +13 -0
  22. data/lib/stripe/list_object.rb +2 -3
  23. data/lib/stripe/oauth.rb +8 -15
  24. data/lib/stripe/object_types.rb +16 -21
  25. data/lib/stripe/request_options.rb +128 -0
  26. data/lib/stripe/resources/billing/credit_balance_summary.rb +14 -0
  27. data/lib/stripe/resources/billing/credit_balance_transaction.rb +26 -0
  28. data/lib/stripe/resources/billing/credit_grant.rb +88 -0
  29. data/lib/stripe/resources/customer.rb +2 -3
  30. data/lib/stripe/resources/file.rb +7 -5
  31. data/lib/stripe/resources/financial_connections/account.rb +0 -3
  32. data/lib/stripe/resources/invoice.rb +0 -81
  33. data/lib/stripe/resources/invoice_rendering_template.rb +2 -0
  34. data/lib/stripe/resources/payment_intent.rb +0 -50
  35. data/lib/stripe/resources/quote.rb +4 -108
  36. data/lib/stripe/resources/source.rb +3 -2
  37. data/lib/stripe/resources/subscription.rb +6 -6
  38. data/lib/stripe/resources/subscription_schedule.rb +0 -20
  39. data/lib/stripe/resources/terminal/reader.rb +0 -60
  40. data/lib/stripe/resources/v2/billing/meter_event.rb +16 -0
  41. data/lib/stripe/resources/v2/billing/meter_event_adjustment.rb +15 -0
  42. data/lib/stripe/resources/v2/billing/meter_event_session.rb +15 -0
  43. data/lib/stripe/resources/v2/event.rb +13 -0
  44. data/lib/stripe/resources.rb +9 -19
  45. data/lib/stripe/search_result_object.rb +1 -1
  46. data/lib/stripe/services/account_capability_service.rb +39 -0
  47. data/lib/stripe/services/account_external_account_service.rb +68 -0
  48. data/lib/stripe/services/account_link_service.rb +17 -0
  49. data/lib/stripe/services/account_login_link_service.rb +19 -0
  50. data/lib/stripe/services/account_person_service.rb +61 -0
  51. data/lib/stripe/services/account_service.rb +100 -0
  52. data/lib/stripe/services/account_session_service.rb +17 -0
  53. data/lib/stripe/services/apple_pay_domain_service.rb +50 -0
  54. data/lib/stripe/services/application_fee_refund_service.rb +60 -0
  55. data/lib/stripe/services/application_fee_service.rb +35 -0
  56. data/lib/stripe/services/apps/secret_service.rb +52 -0
  57. data/lib/stripe/services/apps_service.rb +13 -0
  58. data/lib/stripe/services/balance_service.rb +12 -0
  59. data/lib/stripe/services/balance_transaction_service.rb +32 -0
  60. data/lib/stripe/services/billing/alert_service.rb +74 -0
  61. data/lib/stripe/services/billing/credit_balance_summary_service.rb +19 -0
  62. data/lib/stripe/services/billing/credit_balance_transaction_service.rb +30 -0
  63. data/lib/stripe/services/billing/credit_grant_service.rb +74 -0
  64. data/lib/stripe/services/billing/meter_event_adjustment_service.rb +19 -0
  65. data/lib/stripe/services/billing/meter_event_service.rb +19 -0
  66. data/lib/stripe/services/billing/meter_event_summary_service.rb +19 -0
  67. data/lib/stripe/services/billing/meter_service.rb +81 -0
  68. data/lib/stripe/services/billing_portal/configuration_service.rb +52 -0
  69. data/lib/stripe/services/billing_portal/session_service.rb +19 -0
  70. data/lib/stripe/services/billing_portal_service.rb +14 -0
  71. data/lib/stripe/services/billing_service.rb +20 -0
  72. data/lib/stripe/services/charge_service.rb +69 -0
  73. data/lib/stripe/services/checkout/session_line_item_service.rb +19 -0
  74. data/lib/stripe/services/checkout/session_service.rb +72 -0
  75. data/lib/stripe/services/checkout_service.rb +13 -0
  76. data/lib/stripe/services/climate/order_service.rb +68 -0
  77. data/lib/stripe/services/climate/product_service.rb +30 -0
  78. data/lib/stripe/services/climate/supplier_service.rb +30 -0
  79. data/lib/stripe/services/climate_service.rb +15 -0
  80. data/lib/stripe/services/confirmation_token_service.rb +17 -0
  81. data/lib/stripe/services/country_spec_service.rb +28 -0
  82. data/lib/stripe/services/coupon_service.rb +51 -0
  83. data/lib/stripe/services/credit_note_line_item_service.rb +17 -0
  84. data/lib/stripe/services/credit_note_preview_lines_service.rb +17 -0
  85. data/lib/stripe/services/credit_note_service.rb +93 -0
  86. data/lib/stripe/services/customer_balance_transaction_service.rb +50 -0
  87. data/lib/stripe/services/customer_cash_balance_service.rb +28 -0
  88. data/lib/stripe/services/customer_cash_balance_transaction_service.rb +28 -0
  89. data/lib/stripe/services/customer_funding_instructions_service.rb +19 -0
  90. data/lib/stripe/services/customer_payment_method_service.rb +28 -0
  91. data/lib/stripe/services/customer_payment_source_service.rb +76 -0
  92. data/lib/stripe/services/customer_service.rb +89 -0
  93. data/lib/stripe/services/customer_session_service.rb +17 -0
  94. data/lib/stripe/services/customer_tax_id_service.rb +50 -0
  95. data/lib/stripe/services/dispute_service.rb +48 -0
  96. data/lib/stripe/services/entitlements/active_entitlement_service.rb +30 -0
  97. data/lib/stripe/services/entitlements/feature_service.rb +52 -0
  98. data/lib/stripe/services/entitlements_service.rb +14 -0
  99. data/lib/stripe/services/ephemeral_key_service.rb +28 -0
  100. data/lib/stripe/services/event_service.rb +22 -0
  101. data/lib/stripe/services/exchange_rate_service.rb +28 -0
  102. data/lib/stripe/services/file_link_service.rb +38 -0
  103. data/lib/stripe/services/file_service.rb +35 -0
  104. data/lib/stripe/services/financial_connections/account_owner_service.rb +19 -0
  105. data/lib/stripe/services/financial_connections/account_service.rb +81 -0
  106. data/lib/stripe/services/financial_connections/session_service.rb +30 -0
  107. data/lib/stripe/services/financial_connections/transaction_service.rb +30 -0
  108. data/lib/stripe/services/financial_connections_service.rb +15 -0
  109. data/lib/stripe/services/forwarding/request_service.rb +41 -0
  110. data/lib/stripe/services/forwarding_service.rb +13 -0
  111. data/lib/stripe/services/identity/verification_report_service.rb +30 -0
  112. data/lib/stripe/services/identity/verification_session_service.rb +106 -0
  113. data/lib/stripe/services/identity_service.rb +14 -0
  114. data/lib/stripe/services/invoice_item_service.rb +61 -0
  115. data/lib/stripe/services/invoice_line_item_service.rb +31 -0
  116. data/lib/stripe/services/invoice_rendering_template_service.rb +50 -0
  117. data/lib/stripe/services/invoice_service.rb +202 -0
  118. data/lib/stripe/services/invoice_upcoming_lines_service.rb +17 -0
  119. data/lib/stripe/services/issuing/authorization_service.rb +65 -0
  120. data/lib/stripe/services/issuing/card_service.rb +52 -0
  121. data/lib/stripe/services/issuing/cardholder_service.rb +52 -0
  122. data/lib/stripe/services/issuing/dispute_service.rb +63 -0
  123. data/lib/stripe/services/issuing/personalization_design_service.rb +52 -0
  124. data/lib/stripe/services/issuing/physical_bundle_service.rb +30 -0
  125. data/lib/stripe/services/issuing/token_service.rb +41 -0
  126. data/lib/stripe/services/issuing/transaction_service.rb +41 -0
  127. data/lib/stripe/services/issuing_service.rb +20 -0
  128. data/lib/stripe/services/mandate_service.rb +17 -0
  129. data/lib/stripe/services/oauth_service.rb +63 -0
  130. data/lib/stripe/services/payment_intent_service.rb +204 -0
  131. data/lib/stripe/services/payment_link_line_item_service.rb +17 -0
  132. data/lib/stripe/services/payment_link_service.rb +57 -0
  133. data/lib/stripe/services/payment_method_configuration_service.rb +50 -0
  134. data/lib/stripe/services/payment_method_domain_service.rb +66 -0
  135. data/lib/stripe/services/payment_method_service.rb +86 -0
  136. data/lib/stripe/services/payout_service.rb +66 -0
  137. data/lib/stripe/services/plan_service.rb +49 -0
  138. data/lib/stripe/services/price_service.rb +52 -0
  139. data/lib/stripe/services/product_feature_service.rb +50 -0
  140. data/lib/stripe/services/product_service.rb +70 -0
  141. data/lib/stripe/services/promotion_code_service.rb +50 -0
  142. data/lib/stripe/services/quote_computed_upfront_line_items_service.rb +17 -0
  143. data/lib/stripe/services/quote_line_item_service.rb +17 -0
  144. data/lib/stripe/services/quote_service.rb +92 -0
  145. data/lib/stripe/services/radar/early_fraud_warning_service.rb +32 -0
  146. data/lib/stripe/services/radar/value_list_item_service.rb +52 -0
  147. data/lib/stripe/services/radar/value_list_service.rb +63 -0
  148. data/lib/stripe/services/radar_service.rb +15 -0
  149. data/lib/stripe/services/refund_service.rb +63 -0
  150. data/lib/stripe/services/reporting/report_run_service.rb +41 -0
  151. data/lib/stripe/services/reporting/report_type_service.rb +30 -0
  152. data/lib/stripe/services/reporting_service.rb +14 -0
  153. data/lib/stripe/services/review_service.rb +33 -0
  154. data/lib/stripe/services/setup_attempt_service.rb +17 -0
  155. data/lib/stripe/services/setup_intent_service.rb +105 -0
  156. data/lib/stripe/services/shipping_rate_service.rb +50 -0
  157. data/lib/stripe/services/sigma/scheduled_query_run_service.rb +30 -0
  158. data/lib/stripe/services/sigma_service.rb +13 -0
  159. data/lib/stripe/services/source_service.rb +64 -0
  160. data/lib/stripe/services/source_transaction_service.rb +17 -0
  161. data/lib/stripe/services/subscription_item_service.rb +69 -0
  162. data/lib/stripe/services/subscription_item_usage_record_service.rb +23 -0
  163. data/lib/stripe/services/subscription_item_usage_record_summary_service.rb +19 -0
  164. data/lib/stripe/services/subscription_schedule_service.rb +72 -0
  165. data/lib/stripe/services/subscription_service.rb +127 -0
  166. data/lib/stripe/services/tax/calculation_line_item_service.rb +19 -0
  167. data/lib/stripe/services/tax/calculation_service.rb +37 -0
  168. data/lib/stripe/services/tax/registration_service.rb +54 -0
  169. data/lib/stripe/services/tax/settings_service.rb +30 -0
  170. data/lib/stripe/services/tax/transaction_line_item_service.rb +19 -0
  171. data/lib/stripe/services/tax/transaction_service.rb +48 -0
  172. data/lib/stripe/services/tax_code_service.rb +22 -0
  173. data/lib/stripe/services/tax_id_service.rb +38 -0
  174. data/lib/stripe/services/tax_rate_service.rb +38 -0
  175. data/lib/stripe/services/tax_service.rb +16 -0
  176. data/lib/stripe/services/terminal/configuration_service.rb +63 -0
  177. data/lib/stripe/services/terminal/connection_token_service.rb +19 -0
  178. data/lib/stripe/services/terminal/location_service.rb +64 -0
  179. data/lib/stripe/services/terminal/reader_service.rb +118 -0
  180. data/lib/stripe/services/terminal_service.rb +16 -0
  181. data/lib/stripe/services/test_helpers/confirmation_token_service.rb +19 -0
  182. data/lib/stripe/services/test_helpers/customer_service.rb +19 -0
  183. data/lib/stripe/services/test_helpers/issuing/authorization_service.rb +76 -0
  184. data/lib/stripe/services/test_helpers/issuing/card_service.rb +54 -0
  185. data/lib/stripe/services/test_helpers/issuing/personalization_design_service.rb +43 -0
  186. data/lib/stripe/services/test_helpers/issuing/transaction_service.rb +43 -0
  187. data/lib/stripe/services/test_helpers/issuing_service.rb +19 -0
  188. data/lib/stripe/services/test_helpers/refund_service.rb +19 -0
  189. data/lib/stripe/services/test_helpers/terminal/reader_service.rb +21 -0
  190. data/lib/stripe/services/test_helpers/terminal_service.rb +15 -0
  191. data/lib/stripe/services/test_helpers/test_clock_service.rb +63 -0
  192. data/lib/stripe/services/test_helpers/treasury/inbound_transfer_service.rb +43 -0
  193. data/lib/stripe/services/test_helpers/treasury/outbound_payment_service.rb +54 -0
  194. data/lib/stripe/services/test_helpers/treasury/outbound_transfer_service.rb +54 -0
  195. data/lib/stripe/services/test_helpers/treasury/received_credit_service.rb +21 -0
  196. data/lib/stripe/services/test_helpers/treasury/received_debit_service.rb +21 -0
  197. data/lib/stripe/services/test_helpers/treasury_service.rb +19 -0
  198. data/lib/stripe/services/test_helpers_service.rb +19 -0
  199. data/lib/stripe/services/token_service.rb +23 -0
  200. data/lib/stripe/services/topup_service.rb +49 -0
  201. data/lib/stripe/services/transfer_reversal_service.rb +56 -0
  202. data/lib/stripe/services/transfer_service.rb +47 -0
  203. data/lib/stripe/services/treasury/credit_reversal_service.rb +41 -0
  204. data/lib/stripe/services/treasury/debit_reversal_service.rb +41 -0
  205. data/lib/stripe/services/treasury/financial_account_features_service.rb +30 -0
  206. data/lib/stripe/services/treasury/financial_account_service.rb +59 -0
  207. data/lib/stripe/services/treasury/inbound_transfer_service.rb +52 -0
  208. data/lib/stripe/services/treasury/outbound_payment_service.rb +52 -0
  209. data/lib/stripe/services/treasury/outbound_transfer_service.rb +52 -0
  210. data/lib/stripe/services/treasury/received_credit_service.rb +30 -0
  211. data/lib/stripe/services/treasury/received_debit_service.rb +30 -0
  212. data/lib/stripe/services/treasury/transaction_entry_service.rb +30 -0
  213. data/lib/stripe/services/treasury/transaction_service.rb +30 -0
  214. data/lib/stripe/services/treasury_service.rb +22 -0
  215. data/lib/stripe/services/v1_services.rb +89 -0
  216. data/lib/stripe/services/v2/billing/meter_event_adjustment_service.rb +21 -0
  217. data/lib/stripe/services/v2/billing/meter_event_service.rb +21 -0
  218. data/lib/stripe/services/v2/billing/meter_event_session_service.rb +21 -0
  219. data/lib/stripe/services/v2/billing/meter_event_stream_service.rb +23 -0
  220. data/lib/stripe/services/v2/billing_service.rb +18 -0
  221. data/lib/stripe/services/v2/core/event_service.rb +32 -0
  222. data/lib/stripe/services/v2/core_service.rb +15 -0
  223. data/lib/stripe/services/v2_services.rb +14 -0
  224. data/lib/stripe/services/webhook_endpoint_service.rb +61 -0
  225. data/lib/stripe/services.rb +181 -0
  226. data/lib/stripe/singleton_api_resource.rb +1 -18
  227. data/lib/stripe/stripe_client.rb +51 -1067
  228. data/lib/stripe/stripe_configuration.rb +32 -20
  229. data/lib/stripe/stripe_object.rb +37 -18
  230. data/lib/stripe/stripe_service.rb +32 -0
  231. data/lib/stripe/thin_event.rb +17 -0
  232. data/lib/stripe/util.rb +69 -46
  233. data/lib/stripe/v2_list_object.rb +84 -0
  234. data/lib/stripe/version.rb +1 -1
  235. data/lib/stripe/webhook.rb +1 -1
  236. data/lib/stripe.rb +15 -54
  237. metadata +203 -24
  238. data/lib/stripe/request_signing_authenticator.rb +0 -79
  239. data/lib/stripe/resources/account_notice.rb +0 -32
  240. data/lib/stripe/resources/capital/financing_offer.rb +0 -49
  241. data/lib/stripe/resources/capital/financing_summary.rb +0 -15
  242. data/lib/stripe/resources/capital/financing_transaction.rb +0 -27
  243. data/lib/stripe/resources/financial_connections/account_inferred_balance.rb +0 -14
  244. data/lib/stripe/resources/financial_connections/institution.rb +0 -26
  245. data/lib/stripe/resources/gift_cards/card.rb +0 -59
  246. data/lib/stripe/resources/gift_cards/transaction.rb +0 -93
  247. data/lib/stripe/resources/invoice_payment.rb +0 -12
  248. data/lib/stripe/resources/issuing/credit_underwriting_record.rb +0 -88
  249. data/lib/stripe/resources/issuing/dispute_settlement_detail.rb +0 -26
  250. data/lib/stripe/resources/margin.rb +0 -37
  251. data/lib/stripe/resources/order.rb +0 -120
  252. data/lib/stripe/resources/quote_phase.rb +0 -32
  253. data/lib/stripe/resources/quote_preview_invoice.rb +0 -43
  254. data/lib/stripe/resources/quote_preview_subscription_schedule.rb +0 -11
  255. data/lib/stripe/resources/tax/association.rb +0 -24
  256. data/lib/stripe/resources/tax/form.rb +0 -49
  257. data/lib/stripe/resources/terminal/reader_collected_data.rb +0 -14
@@ -1,1098 +1,82 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "stripe/instrumentation"
3
+ require "json"
4
4
 
5
5
  module Stripe
6
- # StripeClient executes requests against the Stripe API and allows a user to
7
- # recover both a resource a call returns as well as a response object that
8
- # contains information on the HTTP call.
9
6
  class StripeClient
10
- # A set of all known thread contexts across all threads and a mutex to
11
- # synchronize global access to them.
12
- @thread_contexts_with_connection_managers = Set.new
13
- @thread_contexts_with_connection_managers_mutex = Mutex.new
14
- @last_connection_manager_gc = Util.monotonic_time
7
+ # attr_readers: The beginning of the section generated from our OpenAPI spec
8
+ attr_reader :v1
9
+ attr_reader :v2
15
10
 
16
- # Initializes a new StripeClient
17
- def initialize(config_arg = {})
18
- @system_profiler = SystemProfiler.new
19
- @last_request_metrics = nil
20
-
21
- # The following attribute is only used to log whether or not
22
- # StripeClient#request has been called. To be removed in a
23
- # future major version.
24
- @usage = []
25
-
26
- @config = case config_arg
27
- when Hash
28
- Stripe.config.reverse_duplicate_merge(config_arg)
29
- when Stripe::ConnectionManager
30
- # Supports accepting a connection manager object for backwards
31
- # compatibility only, and that use is DEPRECATED.
32
- Stripe.config.dup
33
- when Stripe::StripeConfiguration
34
- config_arg
35
- when String
36
- Stripe.config.reverse_duplicate_merge(
37
- { api_key: config_arg }
38
- )
39
- else
40
- raise ArgumentError, "Can't handle argument: #{config_arg}"
41
- end
42
- end
43
-
44
- attr_reader :config, :options
45
-
46
- # Gets a currently active `StripeClient`. Set for the current thread when
47
- # `StripeClient#request` is being run so that API operations being executed
48
- # inside of that block can find the currently active client. It's reset to
49
- # the original value (hopefully `nil`) after the block ends.
50
- #
51
- # For internal use only. Does not provide a stable API and may be broken
52
- # with future non-major changes.
53
- def self.active_client
54
- current_thread_context.active_client || default_client
55
- end
56
-
57
- # Finishes any active connections by closing their TCP connection and
58
- # clears them from internal tracking in all connection managers across all
59
- # threads.
60
- #
61
- # If passed a `config` object, only clear connection managers for that
62
- # particular configuration.
63
- #
64
- # For internal use only. Does not provide a stable API and may be broken
65
- # with future non-major changes.
66
- def self.clear_all_connection_managers(config: nil)
67
- # Just a quick path for when configuration is being set for the first
68
- # time before any connections have been opened. There is technically some
69
- # potential for thread raciness here, but not in a practical sense.
70
- return if @thread_contexts_with_connection_managers.empty?
71
-
72
- @thread_contexts_with_connection_managers_mutex.synchronize do
73
- pruned_contexts = Set.new
74
-
75
- @thread_contexts_with_connection_managers.each do |thread_context|
76
- # Note that the thread context itself is not destroyed, but we clear
77
- # its connection manager and remove our reference to it. If it ever
78
- # makes a new request we'll give it a new connection manager and
79
- # it'll go back into `@thread_contexts_with_connection_managers`.
80
- thread_context.default_connection_managers.reject! do |cm_config, cm|
81
- if config.nil? || config.key == cm_config
82
- cm.clear
83
- true
84
- end
85
- end
86
-
87
- pruned_contexts << thread_context if thread_context.default_connection_managers.empty?
88
- end
89
-
90
- @thread_contexts_with_connection_managers.subtract(pruned_contexts)
91
- end
92
- end
93
-
94
- # A default client for the current thread.
95
- def self.default_client
96
- current_thread_context.default_client ||= StripeClient.new(Stripe.config)
97
- end
98
-
99
- # A default connection manager for the current thread scoped to the
100
- # configuration object that may be provided.
101
- def self.default_connection_manager(config = Stripe.config)
102
- current_thread_context.default_connection_managers[config.key] ||= begin
103
- connection_manager = ConnectionManager.new(config)
104
-
105
- @thread_contexts_with_connection_managers_mutex.synchronize do
106
- maybe_gc_connection_managers
107
- @thread_contexts_with_connection_managers << current_thread_context
108
- end
109
-
110
- connection_manager
111
- end
112
- end
113
-
114
- # Checks if an error is a problem that we should retry on. This includes
115
- # both socket errors that may represent an intermittent problem and some
116
- # special HTTP statuses.
117
- def self.should_retry?(error,
118
- num_retries:, config: Stripe.config)
119
- return false if num_retries >= config.max_network_retries
120
-
121
- case error
122
- when Net::OpenTimeout, Net::ReadTimeout
123
- # Retry on timeout-related problems (either on open or read).
124
- true
125
- when EOFError, Errno::ECONNREFUSED, Errno::ECONNRESET, # rubocop:todo Lint/DuplicateBranch
126
- Errno::EHOSTUNREACH, Errno::ETIMEDOUT, SocketError
127
- # Destination refused the connection, the connection was reset, or a
128
- # variety of other connection failures. This could occur from a single
129
- # saturated server, so retry in case it's intermittent.
130
- true
131
- when Stripe::StripeError
132
- # The API may ask us not to retry (e.g. if doing so would be a no-op),
133
- # or advise us to retry (e.g. in cases of lock timeouts). Defer to
134
- # those instructions if given.
135
- return false if error.http_headers["stripe-should-retry"] == "false"
136
- return true if error.http_headers["stripe-should-retry"] == "true"
137
-
138
- # 409 Conflict
139
- return true if error.http_status == 409
140
-
141
- # 429 Too Many Requests
142
- #
143
- # There are a few different problems that can lead to a 429. The most
144
- # common is rate limiting, on which we *don't* want to retry because
145
- # that'd likely contribute to more contention problems. However, some
146
- # 429s are lock timeouts, which is when a request conflicted with
147
- # another request or an internal process on some particular object.
148
- # These 429s are safe to retry.
149
- return true if error.http_status == 429 && error.code == "lock_timeout"
150
-
151
- # Retry on 500, 503, and other internal errors.
152
- #
153
- # Note that we expect the stripe-should-retry header to be false
154
- # in most cases when a 500 is returned, since our idempotency framework
155
- # would typically replay it anyway.
156
- true if error.http_status >= 500
157
- else
158
- false
159
- end
160
- end
161
-
162
- def self.sleep_time(num_retries, config: Stripe.config)
163
- # Apply exponential backoff with initial_network_retry_delay on the
164
- # number of num_retries so far as inputs. Do not allow the number to
165
- # exceed max_network_retry_delay.
166
- sleep_seconds = [
167
- config.initial_network_retry_delay * (2**(num_retries - 1)),
168
- config.max_network_retry_delay,
169
- ].min
170
-
171
- # Apply some jitter by randomizing the value in the range of
172
- # (sleep_seconds / 2) to (sleep_seconds).
173
- sleep_seconds *= (0.5 * (1 + rand))
174
-
175
- # But never sleep less than the base sleep seconds.
176
- [config.initial_network_retry_delay, sleep_seconds].max
177
- end
178
-
179
- # Gets the connection manager in use for the current `StripeClient`.
180
- #
181
- # This method is DEPRECATED and for backwards compatibility only.
182
- def connection_manager
183
- self.class.default_connection_manager
184
- end
185
- extend Gem::Deprecate
186
- deprecate :connection_manager, :none, 2020, 9
187
-
188
- # Executes the API call within the given block. Usage looks like:
189
- #
190
- # client = StripeClient.new
191
- # charge, resp = client.request { Charge.create }
192
- #
193
-
194
- def request
195
- @usage = ["stripe_client_request"]
196
- old_stripe_client = self.class.current_thread_context.active_client
197
- self.class.current_thread_context.active_client = self
198
-
199
- if self.class.current_thread_context.last_responses&.key?(object_id)
200
- raise "calls to StripeClient#request cannot be nested within a thread"
201
- end
202
-
203
- self.class.current_thread_context.last_responses ||= {}
204
- self.class.current_thread_context.last_responses[object_id] = nil
205
-
206
- begin
207
- res = yield
208
- [res, self.class.current_thread_context.last_responses[object_id]]
209
- ensure
210
- @usage = []
211
- self.class.current_thread_context.active_client = old_stripe_client
212
- self.class.current_thread_context.last_responses.delete(object_id)
213
- end
214
- end
215
- deprecate :request, "the `last_response` property on the returned resource (see " \
216
- "https://github.com/stripe/stripe-ruby?tab=readme-ov-file#accessing-a-response-object " \
217
- "for usage examples)", 2024, 6
218
-
219
- def execute_request(method, path,
220
- api_base: nil, api_key: nil,
221
- headers: {}, params: {}, api_mode: nil, usage: [])
222
- http_resp, api_key = execute_request_internal(
223
- method, path, api_base, api_key, headers, params, api_mode, usage
224
- )
225
-
226
- begin
227
- resp = StripeResponse.from_net_http(http_resp)
228
- rescue JSON::ParserError
229
- raise general_api_error(http_resp.code.to_i, http_resp.body)
230
- end
231
-
232
- # If being called from `StripeClient#request`, put the last response in
233
- # thread-local memory so that it can be returned to the user. Don't store
234
- # anything otherwise so that we don't leak memory.
235
- store_last_response(object_id, resp)
236
-
237
- [resp, api_key]
238
- end
239
-
240
- # Executes a request and returns the body as a stream instead of converting
241
- # it to a StripeObject. This should be used for any request where we expect
242
- # an arbitrary binary response.
243
- #
244
- # A `read_body_chunk` block can be passed, which will be called repeatedly
245
- # with the body chunks read from the socket.
246
- #
247
- # If a block is passed, a StripeHeadersOnlyResponse is returned as the
248
- # block is expected to do all the necessary body processing. If no block is
249
- # passed, then a StripeStreamResponse is returned containing an IO stream
250
- # with the response body.
251
- def execute_request_stream(method, path,
252
- api_base: nil, api_key: nil, usage: [],
253
- headers: {}, params: {},
254
- api_mode: nil,
255
- &read_body_chunk_block)
256
- unless block_given?
257
- raise ArgumentError,
258
- "execute_request_stream requires a read_body_chunk_block"
259
- end
260
-
261
- http_resp, api_key = execute_request_internal(
262
- method, path, api_base, api_key,
263
- headers, params, api_mode, usage, &read_body_chunk_block
264
- )
265
-
266
- # When the read_body_chunk_block is given, we no longer have access to the
267
- # response body at this point and so return a response object containing
268
- # only the headers. This is because the body was consumed by the block.
269
- resp = StripeHeadersOnlyResponse.from_net_http(http_resp)
270
-
271
- [resp, api_key]
272
- end
273
-
274
- def store_last_response(object_id, resp)
275
- return unless last_response_has_key?(object_id)
276
-
277
- self.class.current_thread_context.last_responses[object_id] = resp
278
- end
279
-
280
- def last_response_has_key?(object_id)
281
- self.class.current_thread_context.last_responses&.key?(object_id)
282
- end
283
-
284
- #
285
- # private
286
- #
287
-
288
- # Time (in seconds) that a connection manager has not been used before it's
289
- # eligible for garbage collection.
290
- CONNECTION_MANAGER_GC_LAST_USED_EXPIRY = 120
291
-
292
- # How often to check (in seconds) for connection managers that haven't been
293
- # used in a long time and which should be garbage collected.
294
- CONNECTION_MANAGER_GC_PERIOD = 60
295
-
296
- ERROR_MESSAGE_CONNECTION =
297
- "Unexpected error communicating when trying to connect to " \
298
- "Stripe (%s). You may be seeing this message because your DNS is not " \
299
- "working or you don't have an internet connection. To check, try " \
300
- "running `host stripe.com` from the command line."
301
- ERROR_MESSAGE_SSL =
302
- "Could not establish a secure connection to Stripe (%s), you " \
303
- "may need to upgrade your OpenSSL version. To check, try running " \
304
- "`openssl s_client -connect api.stripe.com:443` from the command " \
305
- "line."
306
-
307
- # Common error suffix sared by both connect and read timeout messages.
308
- ERROR_MESSAGE_TIMEOUT_SUFFIX =
309
- "Please check your internet connection and try again. " \
310
- "If this problem persists, you should check Stripe's service " \
311
- "status at https://status.stripe.com, or let us know at " \
312
- "support@stripe.com."
313
-
314
- ERROR_MESSAGE_TIMEOUT_CONNECT = (
315
- "Timed out connecting to Stripe (%s). " +
316
- ERROR_MESSAGE_TIMEOUT_SUFFIX
317
- ).freeze
318
-
319
- ERROR_MESSAGE_TIMEOUT_READ = (
320
- "Timed out communicating with Stripe (%s). " +
321
- ERROR_MESSAGE_TIMEOUT_SUFFIX
322
- ).freeze
323
-
324
- # Maps types of exceptions that we're likely to see during a network
325
- # request to more user-friendly messages that we put in front of people.
326
- # The original error message is also appended onto the final exception for
327
- # full transparency.
328
- NETWORK_ERROR_MESSAGES_MAP = {
329
- EOFError => ERROR_MESSAGE_CONNECTION,
330
- Errno::ECONNREFUSED => ERROR_MESSAGE_CONNECTION,
331
- Errno::ECONNRESET => ERROR_MESSAGE_CONNECTION,
332
- Errno::EHOSTUNREACH => ERROR_MESSAGE_CONNECTION,
333
- Errno::ETIMEDOUT => ERROR_MESSAGE_TIMEOUT_CONNECT,
334
- SocketError => ERROR_MESSAGE_CONNECTION,
335
-
336
- Net::OpenTimeout => ERROR_MESSAGE_TIMEOUT_CONNECT,
337
- Net::ReadTimeout => ERROR_MESSAGE_TIMEOUT_READ,
338
-
339
- OpenSSL::SSL::SSLError => ERROR_MESSAGE_SSL,
340
- }.freeze
341
- private_constant :NETWORK_ERROR_MESSAGES_MAP
342
-
343
- # A record representing any data that `StripeClient` puts into
344
- # `Thread.current`. Making it a class likes this gives us a little extra
345
- # type safety and lets us document what each field does.
346
- #
347
- # For internal use only. Does not provide a stable API and may be broken
348
- # with future non-major changes.
349
- class ThreadContext
350
- # A `StripeClient` that's been flagged as currently active within a
351
- # thread by `StripeClient#request`. A client stays active until the
352
- # completion of the request block.
353
- attr_accessor :active_client
354
-
355
- # A default `StripeClient` object for the thread. Used in all cases where
356
- # the user hasn't specified their own.
357
- attr_accessor :default_client
358
-
359
- # A temporary map of object IDs to responses from last executed API
360
- # calls. Used to return a responses from calls to `StripeClient#request`.
361
- #
362
- # Stored in the thread data to make the use of a single `StripeClient`
363
- # object safe across multiple threads. Stored as a map so that multiple
364
- # `StripeClient` objects can run concurrently on the same thread.
365
- #
366
- # Responses are only left in as long as they're needed, which means
367
- # they're removed as soon as a call leaves `StripeClient#request`, and
368
- # because that's wrapped in an `ensure` block, they should never leave
369
- # garbage in `Thread.current`.
370
- attr_accessor :last_responses
371
-
372
- # A map of connection managers for the thread. Normally shared between
373
- # all `StripeClient` objects on a particular thread, and created so as to
374
- # minimize the number of open connections that an application needs.
375
- def default_connection_managers
376
- @default_connection_managers ||= {}
377
- end
378
-
379
- def reset_connection_managers
380
- @default_connection_managers = {}
381
- end
382
- end
383
-
384
- # Access data stored for `StripeClient` within the thread's current
385
- # context. Returns `ThreadContext`.
386
- #
387
- # For internal use only. Does not provide a stable API and may be broken
388
- # with future non-major changes.
389
- def self.current_thread_context
390
- Thread.current[:stripe_client__internal_use_only] ||= ThreadContext.new
391
- end
392
-
393
- # Garbage collects connection managers that haven't been used in some time,
394
- # with the idea being that we want to remove old connection managers that
395
- # belong to dead threads and the like.
396
- #
397
- # Prefixed with `maybe_` because garbage collection will only run
398
- # periodically so that we're not constantly engaged in busy work. If
399
- # connection managers live a little passed their useful age it's not
400
- # harmful, so it's not necessary to get them right away.
401
- #
402
- # For testability, returns `nil` if it didn't run and the number of
403
- # connection managers that were garbage collected otherwise.
404
- #
405
- # IMPORTANT: This method is not thread-safe and expects to be called inside
406
- # a lock on `@thread_contexts_with_connection_managers_mutex`.
407
- #
408
- # For internal use only. Does not provide a stable API and may be broken
409
- # with future non-major changes.
410
- def self.maybe_gc_connection_managers
411
- next_gc_time = @last_connection_manager_gc + CONNECTION_MANAGER_GC_PERIOD
412
- return nil if next_gc_time > Util.monotonic_time
413
-
414
- last_used_threshold =
415
- Util.monotonic_time - CONNECTION_MANAGER_GC_LAST_USED_EXPIRY
416
-
417
- pruned_contexts = []
418
- @thread_contexts_with_connection_managers.each do |thread_context|
419
- thread_context
420
- .default_connection_managers
421
- .each do |config_key, connection_manager|
422
- next if connection_manager.last_used > last_used_threshold
423
-
424
- connection_manager.clear
425
- thread_context.default_connection_managers.delete(config_key)
426
- end
427
- end
428
-
429
- @thread_contexts_with_connection_managers.each do |thread_context|
430
- next unless thread_context.default_connection_managers.empty?
431
-
432
- pruned_contexts << thread_context
433
- end
434
-
435
- @thread_contexts_with_connection_managers -= pruned_contexts
436
- @last_connection_manager_gc = Util.monotonic_time
437
-
438
- pruned_contexts.count
439
- end
440
-
441
- private def execute_request_internal(method, path,
442
- api_base, api_key, headers, params,
443
- api_mode, usage, &read_body_chunk_block)
444
- raise ArgumentError, "method should be a symbol" \
445
- unless method.is_a?(Symbol)
446
- raise ArgumentError, "path should be a string" \
447
- unless path.is_a?(String)
448
-
449
- api_base ||= config.api_base
450
- api_key ||= config.api_key
451
- authenticator ||= config.authenticator
452
- params = Util.objects_to_ids(params)
453
-
454
- check_keys!(api_key, authenticator)
455
-
456
- body_params = nil
457
- query_params = nil
458
- case method
459
- when :get, :head, :delete
460
- query_params = params
461
- else
462
- body_params = params
463
- end
464
-
465
- query_params, path = merge_query_params(query_params, path)
466
-
467
- headers = request_headers(api_key, method, api_mode)
468
- .update(Util.normalize_headers(headers))
469
-
470
- url = api_url(path, api_base)
471
-
472
- # Merge given query parameters with any already encoded in the path.
473
- query = query_params ? Util.encode_parameters(query_params) : nil
474
-
475
- # Encoding body parameters is a little more complex because we may have
476
- # to send a multipart-encoded body. `body_log` is produced separately as
477
- # a log-friendly variant of the encoded form. File objects are displayed
478
- # as such instead of as their file contents.
479
- body, body_log =
480
- body_params ? encode_body(body_params, headers, api_mode) : [nil, nil]
481
-
482
- authenticator.authenticate(method, headers, body) unless api_key
483
-
484
- # stores information on the request we're about to make so that we don't
485
- # have to pass as many parameters around for logging.
486
- context = RequestLogContext.new
487
- context.account = headers["Stripe-Account"]
488
- context.api_key = api_key
489
- context.authenticator = authenticator
490
- context.api_version = headers["Stripe-Version"]
491
- context.body = body_log
492
- context.idempotency_key = headers["Idempotency-Key"]
493
- context.method = method
494
- context.path = path
495
- context.query = query
496
-
497
- # A block can be passed in to read the content directly from the response.
498
- # We want to execute this block only when the response was actually
499
- # successful. When it wasn't, we defer to the standard error handling as
500
- # we have to read the body and parse the error JSON.
501
- response_block =
502
- if block_given?
503
- lambda do |response|
504
- response.read_body(&read_body_chunk_block) unless should_handle_as_error(response.code.to_i)
505
- end
506
- end
507
-
508
- http_resp =
509
- execute_request_with_rescues(api_base, headers, usage, context) do
510
- self.class
511
- .default_connection_manager(config)
512
- .execute_request(method, url,
513
- body: body,
514
- headers: headers,
515
- query: query,
516
- &response_block)
517
- end
518
-
519
- [http_resp, api_key]
520
- end
521
-
522
- private def api_url(url = "", api_base = nil)
523
- (api_base || config.api_base) + url
524
- end
11
+ # attr_readers: The end of the section generated from our OpenAPI spec
525
12
 
526
- private def check_keys!(api_key, authenticator)
527
- if api_key && authenticator
528
- raise AuthenticationError, "Can't specify both API key " \
529
- "and authenticator. Either set your API key" \
530
- 'using "Stripe.api_key = <API-KEY>", or set your authenticator ' \
531
- 'using "Stripe.authenticator = <AUTHENTICATOR>"' \
532
- end
533
-
534
- unless api_key || authenticator
535
- # Default to missing API key error message for general users.
13
+ # Initializes a new StripeClient
14
+ def initialize(api_key, # rubocop:todo Metrics/ParameterLists
15
+ stripe_account: nil,
16
+ stripe_context: nil,
17
+ stripe_version: nil,
18
+ api_base: nil,
19
+ uploads_base: nil,
20
+ connect_base: nil,
21
+ meter_events_base: nil,
22
+ client_id: nil)
23
+ unless api_key
536
24
  raise AuthenticationError, "No API key provided. " \
537
- 'Set your API key using "Stripe.api_key = <API-KEY>". ' \
25
+ 'Set your API key using "client = Stripe::StripeClient.new(<API-KEY>)". ' \
538
26
  "You can generate API keys from the Stripe web interface. " \
539
27
  "See https://stripe.com/api for details, or email " \
540
28
  "support@stripe.com if you have any questions."
541
29
  end
542
30
 
543
- return unless api_key =~ /\s/
544
-
545
- raise AuthenticationError, "Your API key is invalid, as it contains " \
546
- "whitespace. (HINT: You can double-check your API key from the " \
547
- "Stripe web interface. See https://stripe.com/api for details, or " \
548
- "email support@stripe.com if you have any questions.)"
549
- end
550
-
551
- # Encodes a set of body parameters using multipart if `Content-Type` is set
552
- # for that, or standard form-encoding otherwise. Returns the encoded body
553
- # and a version of the encoded body that's safe to be logged.
554
- private def encode_body(body_params, headers, api_mode)
555
- body = nil
556
- flattened_params = Util.flatten_params(body_params)
557
-
558
- if headers["Content-Type"] == MultipartEncoder::MULTIPART_FORM_DATA
559
- body, content_type = MultipartEncoder.encode(flattened_params)
560
-
561
- # Set a new content type that also includes the multipart boundary.
562
- # See `MultipartEncoder` for details.
563
- headers["Content-Type"] = content_type
564
-
565
- # `#to_s` any complex objects like files and the like to build output
566
- # that's more condusive to logging.
567
- flattened_params =
568
- flattened_params.map { |k, v| [k, v.is_a?(String) ? v : v.to_s] }.to_h
569
-
570
- elsif api_mode == :preview
571
- body = JSON.generate(body_params)
572
- headers["Content-Type"] = "application/json"
573
- else
574
- body = Util.encode_parameters(body_params)
575
- end
576
-
577
- body_log = if api_mode == :preview
578
- body
579
- else
580
- # We don't use `Util.encode_parameters` partly as an optimization (to
581
- # not redo work we've already done), and partly because the encoded
582
- # forms of certain characters introduce a lot of visual noise and it's
583
- # nice to have a clearer format for logs.
584
- flattened_params.map { |k, v| "#{k}=#{v}" }.join("&")
585
- end
31
+ config_opts = {
32
+ api_key: api_key,
33
+ stripe_account: stripe_account,
34
+ stripe_context: stripe_context,
35
+ api_version: stripe_version,
36
+ api_base: api_base,
37
+ uploads_base: uploads_base,
38
+ connect_base: connect_base,
39
+ meter_events_base: meter_events_base,
40
+ client_id: client_id,
41
+ }.reject { |_k, v| v.nil? }
586
42
 
587
- [body, body_log]
588
- end
43
+ @requestor = APIRequestor.new(config_opts)
589
44
 
590
- private def should_handle_as_error(http_status)
591
- http_status >= 400
45
+ # top-level services: The beginning of the section generated from our OpenAPI spec
46
+ @v1 = Stripe::V1Services.new(@requestor)
47
+ @v2 = Stripe::V2Services.new(@requestor)
48
+ # top-level services: The end of the section generated from our OpenAPI spec
592
49
  end
593
50
 
594
- private def execute_request_with_rescues(api_base, headers, usage, context)
595
- num_retries = 0
596
-
597
- begin
598
- request_start = nil
599
- user_data = nil
600
-
601
- log_request(context, num_retries)
602
- user_data = notify_request_begin(context)
603
-
604
- request_start = Util.monotonic_time
605
- resp = yield
606
- request_duration = Util.monotonic_time - request_start
607
-
608
- http_status = resp.code.to_i
609
- context = context.dup_from_response_headers(resp)
610
-
611
- handle_error_response(resp, context) if should_handle_as_error(http_status)
612
-
613
- log_response(context, request_start, http_status, resp.body, resp)
614
- notify_request_end(context, request_duration, http_status,
615
- num_retries, user_data, resp, headers)
616
-
617
- if config.enable_telemetry? && context.request_id
618
- request_duration_ms = (request_duration * 1000).to_i
619
- @last_request_metrics =
620
- StripeRequestMetrics.new(context.request_id, request_duration_ms, usage: usage + @usage)
621
- end
622
-
623
- # We rescue all exceptions from a request so that we have an easy spot to
624
- # implement our retry logic across the board. We'll re-raise if it's a
625
- # type of exception that we didn't expect to handle.
626
- rescue StandardError => e
627
- # If we modify context we copy it into a new variable so as not to
628
- # taint the original on a retry.
629
- error_context = context
630
- http_status = nil
631
- request_duration = Util.monotonic_time - request_start if request_start
632
-
633
- if e.is_a?(Stripe::StripeError)
634
- error_context = context.dup_from_response_headers(e.http_headers)
635
- http_status = resp.code.to_i
636
- log_response(error_context, request_start,
637
- e.http_status, e.http_body, resp)
638
- else
639
- log_response_error(error_context, request_start, e)
640
- end
641
- notify_request_end(context, request_duration, http_status, num_retries,
642
- user_data, resp, headers)
643
-
644
- if self.class.should_retry?(e,
645
- num_retries: num_retries,
646
- config: config)
647
- num_retries += 1
648
- sleep self.class.sleep_time(num_retries, config: config)
649
- retry
650
- end
651
-
652
- case e
653
- when Stripe::StripeError
654
- raise
655
- when *NETWORK_ERROR_MESSAGES_MAP.keys
656
- handle_network_error(e, error_context, num_retries, api_base)
657
-
658
- # Only handle errors when we know we can do so, and re-raise otherwise.
659
- # This should be pretty infrequent.
660
- else # rubocop:todo Lint/DuplicateBranch
661
- raise
662
- end
663
- end
664
-
665
- resp
666
- end
667
-
668
- private def notify_request_begin(context)
669
- return unless Instrumentation.any_subscribers?(:request_begin)
670
-
671
- event = Instrumentation::RequestBeginEvent.new(
672
- method: context.method,
673
- path: context.path,
674
- user_data: {}
675
- )
676
- Stripe::Instrumentation.notify(:request_begin, event)
677
-
678
- # This field may be set in the `request_begin` callback. If so, we'll
679
- # forward it onto `request_end`.
680
- event.user_data
681
- end
682
-
683
- private def notify_request_end(context, duration, http_status, num_retries,
684
- user_data, resp, headers)
685
- return if !Instrumentation.any_subscribers?(:request_end) &&
686
- !Instrumentation.any_subscribers?(:request)
687
-
688
- request_context = Stripe::Instrumentation::RequestContext.new(
689
- duration: duration,
690
- context: context,
691
- header: headers
692
- )
693
- response_context = Stripe::Instrumentation::ResponseContext.new(
694
- http_status: http_status,
695
- response: resp
696
- )
697
-
698
- event = Instrumentation::RequestEndEvent.new(
699
- request_context: request_context,
700
- response_context: response_context,
701
- num_retries: num_retries,
702
- user_data: user_data || {}
703
- )
704
- Stripe::Instrumentation.notify(:request_end, event)
705
-
706
- # The name before `request_begin` was also added. Provided for backwards
707
- # compatibility.
708
- Stripe::Instrumentation.notify(:request, event)
709
- end
710
-
711
- private def general_api_error(status, body)
712
- APIError.new("Invalid response object from API: #{body.inspect} " \
713
- "(HTTP response code was #{status})",
714
- http_status: status, http_body: body)
715
- end
716
-
717
- # Formats a plugin "app info" hash into a string that we can tack onto the
718
- # end of a User-Agent string where it'll be fairly prominent in places like
719
- # the Dashboard. Note that this formatting has been implemented to match
720
- # other libraries, and shouldn't be changed without universal consensus.
721
- private def format_app_info(info)
722
- str = info[:name]
723
- str = "#{str}/#{info[:version]}" unless info[:version].nil?
724
- str = "#{str} (#{info[:url]})" unless info[:url].nil?
725
- str
726
- end
727
-
728
- private def handle_error_response(http_resp, context)
729
- begin
730
- resp = StripeResponse.from_net_http(http_resp)
731
- error_data = resp.data[:error]
732
-
733
- raise StripeError, "Indeterminate error" unless error_data
734
- rescue JSON::ParserError, StripeError
735
- raise general_api_error(http_resp.code.to_i, http_resp.body)
736
- end
737
-
738
- error = if error_data.is_a?(String)
739
- specific_oauth_error(resp, error_data, context)
740
- else
741
- specific_api_error(resp, error_data, context)
742
- end
743
-
744
- error.response = resp
745
- raise(error)
746
- end
747
-
748
- # Works around an edge case where we end up with both query parameters from
749
- # parameteers and query parameters that were appended onto the end of the
750
- # given path.
751
- #
752
- # Decode any parameters that were added onto the end of a path and add them
753
- # to a unified query parameter hash so that all parameters end up in one
754
- # place and all of them are correctly included in the final request.
755
- private def merge_query_params(query_params, path)
756
- u = URI.parse(path)
757
-
758
- # Return original results if there was nothing to be found.
759
- return query_params, path if u.query.nil?
760
-
761
- query_params ||= {}
762
- query_params = Hash[URI.decode_www_form(u.query)].merge(query_params)
763
-
764
- # Reset the path minus any query parameters that were specified.
765
- path = u.path
766
-
767
- [query_params, path]
768
- end
769
-
770
- private def specific_api_error(resp, error_data, context)
771
- message = error_data[:message]
772
- Util.log_error("Stripe API error",
773
- status: resp.http_status,
774
- error_code: error_data[:code],
775
- error_message: message,
776
- error_param: error_data[:param],
777
- error_type: error_data[:type],
778
- idempotency_key: context.idempotency_key,
779
- request_id: context.request_id,
780
- config: config)
781
-
782
- # The standard set of arguments that can be used to initialize most of
783
- # the exceptions.
784
- opts = {
785
- http_body: resp.http_body,
786
- http_headers: resp.http_headers,
787
- http_status: resp.http_status,
788
- json_body: resp.data,
789
- code: error_data[:code],
790
- }
791
-
792
- case resp.http_status
793
- when 400, 404
794
- case error_data[:type]
795
- when "idempotency_error"
796
- IdempotencyError.new(message, **opts)
797
- else
798
- InvalidRequestError.new(
799
- message, error_data[:param],
800
- **opts
801
- )
802
- end
803
- when 401
804
- AuthenticationError.new(message, **opts)
805
- when 402
806
- CardError.new(
807
- message, error_data[:param],
808
- **opts
809
- )
810
- when 403
811
- PermissionError.new(message, **opts)
812
- when 429
813
- RateLimitError.new(message, **opts)
814
- else
815
- APIError.new(message, **opts)
816
- end
817
- end
818
-
819
- # Attempts to look at a response's error code and return an OAuth error if
820
- # one matches. Will return `nil` if the code isn't recognized.
821
- private def specific_oauth_error(resp, error_code, context)
822
- description = resp.data[:error_description] || error_code
823
-
824
- Util.log_error("Stripe OAuth error",
825
- status: resp.http_status,
826
- error_code: error_code,
827
- error_description: description,
828
- idempotency_key: context.idempotency_key,
829
- request_id: context.request_id,
830
- config: config)
831
-
832
- args = {
833
- http_status: resp.http_status, http_body: resp.http_body,
834
- json_body: resp.data, http_headers: resp.http_headers,
835
- }
836
-
837
- case error_code
838
- when "invalid_client"
839
- OAuth::InvalidClientError.new(error_code, description, **args)
840
- when "invalid_grant"
841
- OAuth::InvalidGrantError.new(error_code, description, **args)
842
- when "invalid_request"
843
- OAuth::InvalidRequestError.new(error_code, description, **args)
844
- when "invalid_scope"
845
- OAuth::InvalidScopeError.new(error_code, description, **args)
846
- when "unsupported_grant_type"
847
- OAuth::UnsupportedGrantTypeError.new(error_code, description, **args)
848
- when "unsupported_response_type"
849
- OAuth::UnsupportedResponseTypeError.new(error_code, description, **args)
850
- else
851
- # We'd prefer that all errors are typed, but we create a generic
852
- # OAuthError in case we run into a code that we don't recognize.
853
- OAuth::OAuthError.new(error_code, description, **args)
854
- end
855
- end
856
-
857
- private def handle_network_error(error, context, num_retries,
858
- api_base = nil)
859
- Util.log_error("Stripe network error",
860
- error_message: error.message,
861
- idempotency_key: context.idempotency_key,
862
- request_id: context.request_id,
863
- config: config)
864
-
865
- errors, message = NETWORK_ERROR_MESSAGES_MAP.detect do |(e, _)|
866
- error.is_a?(e)
867
- end
868
-
869
- if errors.nil?
870
- message = "Unexpected error #{error.class.name} communicating " \
871
- "with Stripe. Please let us know at support@stripe.com."
872
- end
873
-
874
- api_base ||= config.api_base
875
- message = message % api_base
876
-
877
- message += " Request was retried #{num_retries} times." if num_retries > 0
878
-
879
- raise APIConnectionError,
880
- message + "\n\n(Network error: #{error.message})"
881
- end
882
-
883
- private def request_headers(api_key, method, api_mode)
884
- user_agent = "Stripe/v1 RubyBindings/#{Stripe::VERSION}"
885
- user_agent += " " + format_app_info(Stripe.app_info) unless Stripe.app_info.nil?
886
-
887
- headers = {
888
- "User-Agent" => user_agent,
889
- "Authorization" => "Bearer #{api_key}",
890
- }
891
-
892
- if api_mode != :preview
893
- # TODO: (major) don't set Content-Type if method is not post
894
- headers["Content-Type"] = "application/x-www-form-urlencoded"
895
- end
896
-
897
- if config.enable_telemetry? && !@last_request_metrics.nil?
898
- headers["X-Stripe-Client-Telemetry"] = JSON.generate(
899
- last_request_metrics: @last_request_metrics.payload
900
- )
901
- end
902
-
903
- # It is only safe to retry network failures on post and delete
904
- # requests if we add an Idempotency-Key header
905
- if %i[post delete].include?(method) && config.max_network_retries > 0
906
- headers["Idempotency-Key"] ||= SecureRandom.uuid
907
- end
908
-
909
- if api_mode == :preview
910
- headers["Stripe-Version"] = ApiVersion::PREVIEW
911
- elsif config.api_version
912
- headers["Stripe-Version"] = config.api_version
913
- end
914
-
915
- headers["Stripe-Account"] = config.stripe_account if config.stripe_account
916
-
917
- user_agent = @system_profiler.user_agent
918
- begin
919
- headers.update(
920
- "X-Stripe-Client-User-Agent" => JSON.generate(user_agent)
921
- )
922
- rescue StandardError => e
923
- headers.update(
924
- "X-Stripe-Client-Raw-User-Agent" => user_agent.inspect,
925
- :error => "#{e} (#{e.class})"
926
- )
927
- end
928
-
929
- headers
51
+ def request(&block)
52
+ @requestor.request(&block)
930
53
  end
54
+ extend Gem::Deprecate
55
+ deprecate :request, :raw_request, 2024, 9
931
56
 
932
- private def log_request(context, num_retries)
933
- Util.log_info("Request to Stripe API",
934
- account: context.account,
935
- api_version: context.api_version,
936
- idempotency_key: context.idempotency_key,
937
- method: context.method,
938
- num_retries: num_retries,
939
- path: context.path,
940
- config: config)
941
- Util.log_debug("Request details",
942
- body: context.body,
943
- idempotency_key: context.idempotency_key,
944
- query: context.query,
945
- config: config,
946
- process_id: Process.pid,
947
- thread_object_id: Thread.current.object_id,
948
- log_timestamp: Util.monotonic_time)
949
- end
57
+ def parse_thin_event(payload, sig_header, secret, tolerance: Webhook::DEFAULT_TOLERANCE)
58
+ payload = payload.force_encoding("UTF-8") if payload.respond_to?(:force_encoding)
950
59
 
951
- private def log_response(context, request_start, status, body, resp)
952
- Util.log_info("Response from Stripe API",
953
- account: context.account,
954
- api_version: context.api_version,
955
- elapsed: Util.monotonic_time - request_start,
956
- idempotency_key: context.idempotency_key,
957
- method: context.method,
958
- path: context.path,
959
- request_id: context.request_id,
960
- status: status,
961
- config: config)
962
- Util.log_debug("Response details",
963
- body: body,
964
- idempotency_key: context.idempotency_key,
965
- request_id: context.request_id,
966
- config: config,
967
- process_id: Process.pid,
968
- thread_object_id: Thread.current.object_id,
969
- response_object_id: resp.object_id,
970
- log_timestamp: Util.monotonic_time)
60
+ # v2 events use the same signing mechanism as v1 events
61
+ Webhook::Signature.verify_header(payload, sig_header, secret, tolerance: tolerance)
971
62
 
972
- return unless context.request_id
63
+ parsed = JSON.parse(payload, symbolize_names: true)
973
64
 
974
- Util.log_debug("Dashboard link for request",
975
- idempotency_key: context.idempotency_key,
976
- request_id: context.request_id,
977
- url: Util.request_id_dashboard_url(context.request_id,
978
- context.api_key),
979
- config: config)
65
+ Stripe::ThinEvent.new(parsed)
980
66
  end
981
67
 
982
- private def log_response_error(context, request_start, error)
983
- elapsed = request_start ? Util.monotonic_time - request_start : nil
984
- Util.log_error("Request error",
985
- elapsed: elapsed,
986
- error_message: error.message,
987
- idempotency_key: context.idempotency_key,
988
- method: context.method,
989
- path: context.path,
990
- config: config)
991
- end
68
+ def raw_request(method, url, base_address: :api, params: {}, opts: {})
69
+ opts = Util.normalize_opts(opts)
70
+ req_opts = RequestOptions.extract_opts_from_hash(opts)
992
71
 
993
- # RequestLogContext stores information about a request that's begin made so
994
- # that we can log certain information. It's useful because it means that we
995
- # don't have to pass around as many parameters.
996
- class RequestLogContext
997
- attr_accessor :body, :account, :api_key, :authenticator, :api_version, :idempotency_key, :method, :path, :query,
998
- :request_id
72
+ resp, = @requestor.send(:execute_request_internal, method, url, base_address, params, req_opts, usage: ["raw_request"])
999
73
 
1000
- # The idea with this method is that we might want to update some of
1001
- # context information because a response that we've received from the API
1002
- # contains information that's more authoritative than what we started
1003
- # with for a request. For example, we should trust whatever came back in
1004
- # a `Stripe-Version` header beyond what configuration information that we
1005
- # might have had available.
1006
- def dup_from_response_headers(headers)
1007
- context = dup
1008
- context.account = headers["Stripe-Account"]
1009
- context.api_version = headers["Stripe-Version"]
1010
- context.idempotency_key = headers["Idempotency-Key"]
1011
- context.request_id = headers["Request-Id"]
1012
- context
1013
- end
74
+ @requestor.interpret_response(resp)
1014
75
  end
1015
76
 
1016
- # SystemProfiler extracts information about the system that we're running
1017
- # in so that we can generate a rich user agent header to help debug
1018
- # integrations.
1019
- class SystemProfiler
1020
- def self.uname
1021
- if ::File.exist?("/proc/version")
1022
- ::File.read("/proc/version").strip
1023
- else
1024
- case RbConfig::CONFIG["host_os"]
1025
- when /linux|darwin|bsd|sunos|solaris|cygwin/i
1026
- uname_from_system
1027
- when /mswin|mingw/i
1028
- uname_from_system_ver
1029
- else
1030
- "unknown platform"
1031
- end
1032
- end
1033
- end
1034
-
1035
- def self.uname_from_system
1036
- (`uname -a 2>/dev/null` || "").strip
1037
- rescue Errno::ENOENT
1038
- "uname executable not found"
1039
- rescue Errno::ENOMEM # couldn't create subprocess
1040
- "uname lookup failed"
1041
- end
1042
-
1043
- def self.uname_from_system_ver
1044
- (`ver` || "").strip
1045
- rescue Errno::ENOENT
1046
- "ver executable not found"
1047
- rescue Errno::ENOMEM # couldn't create subprocess
1048
- "uname lookup failed"
1049
- end
1050
-
1051
- def initialize
1052
- @uname = self.class.uname
1053
- end
1054
-
1055
- def user_agent
1056
- lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} " \
1057
- "(#{RUBY_RELEASE_DATE})"
1058
-
1059
- {
1060
- application: Stripe.app_info,
1061
- bindings_version: Stripe::VERSION,
1062
- lang: "ruby",
1063
- lang_version: lang_version,
1064
- platform: RUBY_PLATFORM,
1065
- engine: defined?(RUBY_ENGINE) ? RUBY_ENGINE : "",
1066
- publisher: "stripe",
1067
- uname: @uname,
1068
- hostname: Socket.gethostname,
1069
- }.delete_if { |_k, v| v.nil? }
1070
- end
1071
- end
1072
-
1073
- # StripeRequestMetrics tracks metadata to be reported to stripe for metrics
1074
- # collection
1075
- class StripeRequestMetrics
1076
- # The Stripe request ID of the response.
1077
- attr_accessor :request_id
1078
-
1079
- # Request duration in milliseconds
1080
- attr_accessor :request_duration_ms
1081
-
1082
- # list of names of tracked behaviors associated with this request
1083
- attr_accessor :usage
1084
-
1085
- def initialize(request_id, request_duration_ms, usage: [])
1086
- self.request_id = request_id
1087
- self.request_duration_ms = request_duration_ms
1088
- self.usage = usage
1089
- end
1090
-
1091
- def payload
1092
- ret = { request_id: request_id, request_duration_ms: request_duration_ms }
1093
- ret[:usage] = usage if !usage.nil? && !usage.empty?
1094
- ret
1095
- end
77
+ def deserialize(data, api_mode: :v1)
78
+ data = JSON.parse(data) if data.is_a?(String)
79
+ Util.convert_to_stripe_object(data, {}, api_mode: api_mode)
1096
80
  end
1097
81
  end
1098
82
  end