stripe 4.24.0 → 5.36.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +169 -0
  3. data/CODE_OF_CONDUCT.md +77 -0
  4. data/Gemfile +10 -16
  5. data/README.md +111 -44
  6. data/Rakefile +8 -7
  7. data/VERSION +1 -1
  8. data/lib/stripe.rb +39 -130
  9. data/lib/stripe/api_operations/create.rb +1 -1
  10. data/lib/stripe/api_operations/delete.rb +7 -3
  11. data/lib/stripe/api_operations/list.rb +1 -12
  12. data/lib/stripe/api_operations/nested_resource.rb +29 -26
  13. data/lib/stripe/api_operations/request.rb +82 -6
  14. data/lib/stripe/api_operations/save.rb +7 -4
  15. data/lib/stripe/api_resource.rb +12 -3
  16. data/lib/stripe/connection_manager.rb +179 -0
  17. data/lib/stripe/error_object.rb +94 -0
  18. data/lib/stripe/errors.rb +24 -3
  19. data/lib/stripe/instrumentation.rb +82 -0
  20. data/lib/stripe/list_object.rb +34 -5
  21. data/lib/stripe/multipart_encoder.rb +131 -0
  22. data/lib/stripe/oauth.rb +8 -6
  23. data/lib/stripe/object_types.rb +12 -1
  24. data/lib/stripe/resources.rb +12 -1
  25. data/lib/stripe/resources/account.rb +6 -14
  26. data/lib/stripe/resources/account_link.rb +2 -1
  27. data/lib/stripe/resources/alipay_account.rb +1 -1
  28. data/lib/stripe/resources/apple_pay_domain.rb +2 -1
  29. data/lib/stripe/resources/application_fee.rb +2 -12
  30. data/lib/stripe/resources/application_fee_refund.rb +1 -1
  31. data/lib/stripe/resources/balance.rb +2 -1
  32. data/lib/stripe/resources/balance_transaction.rb +2 -1
  33. data/lib/stripe/resources/bank_account.rb +3 -2
  34. data/lib/stripe/resources/billing_portal/configuration.rb +14 -0
  35. data/lib/stripe/resources/billing_portal/session.rb +12 -0
  36. data/lib/stripe/resources/bitcoin_receiver.rb +2 -1
  37. data/lib/stripe/resources/bitcoin_transaction.rb +1 -1
  38. data/lib/stripe/resources/capability.rb +1 -1
  39. data/lib/stripe/resources/card.rb +2 -1
  40. data/lib/stripe/resources/charge.rb +8 -69
  41. data/lib/stripe/resources/checkout/session.rb +6 -1
  42. data/lib/stripe/resources/country_spec.rb +2 -1
  43. data/lib/stripe/resources/coupon.rb +2 -1
  44. data/lib/stripe/resources/credit_note.rb +12 -1
  45. data/lib/stripe/resources/credit_note_line_item.rb +7 -0
  46. data/lib/stripe/resources/customer.rb +9 -63
  47. data/lib/stripe/resources/customer_balance_transaction.rb +1 -1
  48. data/lib/stripe/resources/discount.rb +1 -1
  49. data/lib/stripe/resources/dispute.rb +2 -7
  50. data/lib/stripe/resources/ephemeral_key.rb +2 -1
  51. data/lib/stripe/resources/event.rb +2 -1
  52. data/lib/stripe/resources/exchange_rate.rb +2 -1
  53. data/lib/stripe/resources/file.rb +6 -14
  54. data/lib/stripe/resources/file_link.rb +2 -1
  55. data/lib/stripe/resources/identity/verification_report.rb +12 -0
  56. data/lib/stripe/resources/identity/verification_session.rb +35 -0
  57. data/lib/stripe/resources/invoice.rb +8 -2
  58. data/lib/stripe/resources/invoice_item.rb +2 -1
  59. data/lib/stripe/resources/invoice_line_item.rb +1 -1
  60. data/lib/stripe/resources/issuing/authorization.rb +2 -1
  61. data/lib/stripe/resources/issuing/card.rb +2 -1
  62. data/lib/stripe/resources/issuing/card_details.rb +1 -1
  63. data/lib/stripe/resources/issuing/cardholder.rb +2 -1
  64. data/lib/stripe/resources/issuing/dispute.rb +13 -1
  65. data/lib/stripe/resources/issuing/transaction.rb +2 -1
  66. data/lib/stripe/resources/line_item.rb +7 -0
  67. data/lib/stripe/resources/login_link.rb +1 -1
  68. data/lib/stripe/resources/mandate.rb +8 -0
  69. data/lib/stripe/resources/order.rb +2 -9
  70. data/lib/stripe/resources/order_return.rb +2 -1
  71. data/lib/stripe/resources/payment_intent.rb +2 -1
  72. data/lib/stripe/resources/payment_method.rb +2 -1
  73. data/lib/stripe/resources/payout.rb +10 -5
  74. data/lib/stripe/resources/person.rb +1 -1
  75. data/lib/stripe/resources/plan.rb +2 -1
  76. data/lib/stripe/resources/price.rb +12 -0
  77. data/lib/stripe/resources/product.rb +2 -1
  78. data/lib/stripe/resources/promotion_code.rb +12 -0
  79. data/lib/stripe/resources/quote.rb +95 -0
  80. data/lib/stripe/resources/radar/early_fraud_warning.rb +2 -1
  81. data/lib/stripe/resources/radar/value_list.rb +2 -1
  82. data/lib/stripe/resources/radar/value_list_item.rb +2 -1
  83. data/lib/stripe/resources/recipient.rb +2 -5
  84. data/lib/stripe/resources/recipient_transfer.rb +1 -1
  85. data/lib/stripe/resources/refund.rb +2 -1
  86. data/lib/stripe/resources/reporting/report_run.rb +2 -1
  87. data/lib/stripe/resources/reporting/report_type.rb +2 -1
  88. data/lib/stripe/resources/reversal.rb +1 -1
  89. data/lib/stripe/resources/review.rb +2 -1
  90. data/lib/stripe/resources/setup_attempt.rb +10 -0
  91. data/lib/stripe/resources/setup_intent.rb +2 -1
  92. data/lib/stripe/resources/sigma/scheduled_query_run.rb +2 -1
  93. data/lib/stripe/resources/sku.rb +2 -1
  94. data/lib/stripe/resources/source.rb +11 -10
  95. data/lib/stripe/resources/source_transaction.rb +1 -1
  96. data/lib/stripe/resources/subscription.rb +10 -9
  97. data/lib/stripe/resources/subscription_item.rb +8 -2
  98. data/lib/stripe/resources/subscription_schedule.rb +2 -1
  99. data/lib/stripe/resources/tax_code.rb +10 -0
  100. data/lib/stripe/resources/tax_id.rb +1 -1
  101. data/lib/stripe/resources/tax_rate.rb +2 -1
  102. data/lib/stripe/resources/terminal/connection_token.rb +2 -1
  103. data/lib/stripe/resources/terminal/location.rb +2 -1
  104. data/lib/stripe/resources/terminal/reader.rb +2 -1
  105. data/lib/stripe/resources/three_d_secure.rb +2 -1
  106. data/lib/stripe/resources/token.rb +2 -1
  107. data/lib/stripe/resources/topup.rb +2 -1
  108. data/lib/stripe/resources/transfer.rb +2 -6
  109. data/lib/stripe/resources/usage_record.rb +1 -17
  110. data/lib/stripe/resources/usage_record_summary.rb +1 -1
  111. data/lib/stripe/resources/webhook_endpoint.rb +2 -1
  112. data/lib/stripe/stripe_client.rb +608 -242
  113. data/lib/stripe/stripe_configuration.rb +194 -0
  114. data/lib/stripe/stripe_object.rb +30 -25
  115. data/lib/stripe/stripe_response.rb +87 -27
  116. data/lib/stripe/util.rb +37 -18
  117. data/lib/stripe/version.rb +1 -1
  118. data/lib/stripe/webhook.rb +39 -8
  119. data/stripe.gemspec +15 -11
  120. metadata +24 -216
  121. data/.editorconfig +0 -10
  122. data/.gitattributes +0 -4
  123. data/.github/ISSUE_TEMPLATE.md +0 -5
  124. data/.gitignore +0 -8
  125. data/.rubocop.yml +0 -43
  126. data/.rubocop_todo.yml +0 -38
  127. data/.travis.yml +0 -43
  128. data/.vscode/extensions.json +0 -7
  129. data/.vscode/settings.json +0 -8
  130. data/lib/stripe/resources/issuer_fraud_record.rb +0 -9
  131. data/test/api_stub_helpers.rb +0 -1
  132. data/test/openapi/README.md +0 -9
  133. data/test/stripe/account_link_test.rb +0 -18
  134. data/test/stripe/account_test.rb +0 -428
  135. data/test/stripe/alipay_account_test.rb +0 -37
  136. data/test/stripe/api_operations_test.rb +0 -80
  137. data/test/stripe/api_resource_test.rb +0 -621
  138. data/test/stripe/apple_pay_domain_test.rb +0 -46
  139. data/test/stripe/application_fee_refund_test.rb +0 -37
  140. data/test/stripe/application_fee_test.rb +0 -58
  141. data/test/stripe/balance_test.rb +0 -13
  142. data/test/stripe/balance_transaction_test.rb +0 -20
  143. data/test/stripe/bank_account_test.rb +0 -36
  144. data/test/stripe/capability_test.rb +0 -45
  145. data/test/stripe/charge_test.rb +0 -80
  146. data/test/stripe/checkout/session_test.rb +0 -41
  147. data/test/stripe/country_spec_test.rb +0 -20
  148. data/test/stripe/coupon_test.rb +0 -61
  149. data/test/stripe/credit_note_test.rb +0 -61
  150. data/test/stripe/customer_balance_transaction_test.rb +0 -37
  151. data/test/stripe/customer_card_test.rb +0 -42
  152. data/test/stripe/customer_test.rb +0 -269
  153. data/test/stripe/dispute_test.rb +0 -51
  154. data/test/stripe/ephemeral_key_test.rb +0 -93
  155. data/test/stripe/errors_test.rb +0 -20
  156. data/test/stripe/exchange_rate_test.rb +0 -20
  157. data/test/stripe/file_link_test.rb +0 -41
  158. data/test/stripe/file_test.rb +0 -97
  159. data/test/stripe/file_upload_test.rb +0 -79
  160. data/test/stripe/invoice_item_test.rb +0 -66
  161. data/test/stripe/invoice_line_item_test.rb +0 -8
  162. data/test/stripe/invoice_test.rb +0 -213
  163. data/test/stripe/issuer_fraud_record_test.rb +0 -20
  164. data/test/stripe/issuing/authorization_test.rb +0 -72
  165. data/test/stripe/issuing/card_test.rb +0 -62
  166. data/test/stripe/issuing/cardholder_test.rb +0 -53
  167. data/test/stripe/issuing/dispute_test.rb +0 -45
  168. data/test/stripe/issuing/transaction_test.rb +0 -48
  169. data/test/stripe/list_object_test.rb +0 -156
  170. data/test/stripe/login_link_test.rb +0 -37
  171. data/test/stripe/oauth_test.rb +0 -88
  172. data/test/stripe/order_return_test.rb +0 -21
  173. data/test/stripe/order_test.rb +0 -82
  174. data/test/stripe/payment_intent_test.rb +0 -107
  175. data/test/stripe/payment_method_test.rb +0 -84
  176. data/test/stripe/payout_test.rb +0 -57
  177. data/test/stripe/person_test.rb +0 -46
  178. data/test/stripe/plan_test.rb +0 -98
  179. data/test/stripe/product_test.rb +0 -59
  180. data/test/stripe/radar/early_fraud_warning_test.rb +0 -22
  181. data/test/stripe/radar/value_list_item_test.rb +0 -48
  182. data/test/stripe/radar/value_list_test.rb +0 -61
  183. data/test/stripe/recipient_test.rb +0 -62
  184. data/test/stripe/refund_test.rb +0 -39
  185. data/test/stripe/reporting/report_run_test.rb +0 -33
  186. data/test/stripe/reporting/report_type_test.rb +0 -22
  187. data/test/stripe/reversal_test.rb +0 -43
  188. data/test/stripe/review_test.rb +0 -27
  189. data/test/stripe/setup_intent_test.rb +0 -84
  190. data/test/stripe/sigma/scheduled_query_run_test.rb +0 -22
  191. data/test/stripe/sku_test.rb +0 -60
  192. data/test/stripe/source_test.rb +0 -99
  193. data/test/stripe/source_transaction_test.rb +0 -19
  194. data/test/stripe/stripe_client_test.rb +0 -842
  195. data/test/stripe/stripe_object_test.rb +0 -525
  196. data/test/stripe/stripe_response_test.rb +0 -49
  197. data/test/stripe/subscription_item_test.rb +0 -75
  198. data/test/stripe/subscription_schedule_test.rb +0 -82
  199. data/test/stripe/subscription_test.rb +0 -80
  200. data/test/stripe/tax_id_test.rb +0 -31
  201. data/test/stripe/tax_rate_test.rb +0 -43
  202. data/test/stripe/terminal/connection_token_test.rb +0 -16
  203. data/test/stripe/terminal/location_test.rb +0 -68
  204. data/test/stripe/terminal/reader_test.rb +0 -62
  205. data/test/stripe/three_d_secure_test.rb +0 -23
  206. data/test/stripe/topup_test.rb +0 -62
  207. data/test/stripe/transfer_test.rb +0 -88
  208. data/test/stripe/usage_record_summary_test.rb +0 -19
  209. data/test/stripe/usage_record_test.rb +0 -28
  210. data/test/stripe/util_test.rb +0 -402
  211. data/test/stripe/webhook_endpoint_test.rb +0 -59
  212. data/test/stripe/webhook_test.rb +0 -96
  213. data/test/stripe_mock.rb +0 -77
  214. data/test/stripe_test.rb +0 -63
  215. data/test/test_data.rb +0 -61
  216. data/test/test_helper.rb +0 -71
@@ -15,7 +15,8 @@ module Stripe
15
15
  # * +opts+ - A Hash of additional options (separate from the params /
16
16
  # object values) to be added to the request. E.g. to allow for an
17
17
  # idempotency_key to be passed in the request headers, or for the
18
- # api_key to be overwritten. See {APIOperations::Request.request}.
18
+ # api_key to be overwritten. See
19
+ # {APIOperations::Request.execute_resource_request}.
19
20
  def update(id, params = {}, opts = {})
20
21
  params.each_key do |k|
21
22
  if protected_fields.include?(k)
@@ -23,7 +24,8 @@ module Stripe
23
24
  end
24
25
  end
25
26
 
26
- resp, opts = request(:post, "#{resource_url}/#{id}", params, opts)
27
+ resp, opts = execute_resource_request(:post, "#{resource_url}/#{id}",
28
+ params, opts)
27
29
  Util.convert_to_stripe_object(resp.data, opts)
28
30
  end
29
31
  end
@@ -43,7 +45,8 @@ module Stripe
43
45
  # * +opts+ - A Hash of additional options (separate from the params /
44
46
  # object values) to be added to the request. E.g. to allow for an
45
47
  # idempotency_key to be passed in the request headers, or for the
46
- # api_key to be overwritten. See {APIOperations::Request.request}.
48
+ # api_key to be overwritten. See
49
+ # {APIOperations::Request.execute_resource_request}.
47
50
  def save(params = {}, opts = {})
48
51
  # We started unintentionally (sort of) allowing attributes sent to
49
52
  # +save+ to override values used during the update. So as not to break
@@ -59,7 +62,7 @@ module Stripe
59
62
  # generated a uri for this object with an identifier baked in
60
63
  values.delete(:id)
61
64
 
62
- resp, opts = request(:post, save_url, values, opts)
65
+ resp, opts = execute_resource_request(:post, save_url, values, opts)
63
66
  initialize_from(resp.data, opts)
64
67
  end
65
68
 
@@ -76,7 +76,7 @@ module Stripe
76
76
  end
77
77
 
78
78
  url = "#{resource_url}/#{CGI.escape(id)}/#{CGI.escape(http_path)}"
79
- resp, opts = request(http_verb, url, params, opts)
79
+ resp, opts = execute_resource_request(http_verb, url, params, opts)
80
80
  Util.convert_to_stripe_object(resp.data, opts)
81
81
  end
82
82
  end
@@ -93,7 +93,8 @@ module Stripe
93
93
  end
94
94
 
95
95
  def refresh
96
- resp, opts = request(:get, resource_url, @retrieve_params)
96
+ resp, opts = execute_resource_request(:get, resource_url,
97
+ @retrieve_params)
97
98
  initialize_from(resp.data, opts)
98
99
  end
99
100
 
@@ -105,7 +106,7 @@ module Stripe
105
106
  end
106
107
 
107
108
  protected def request_stripe_object(method:, path:, params:, opts: {})
108
- resp, opts = request(method, path, params, opts)
109
+ resp, opts = execute_resource_request(method, path, params, opts)
109
110
 
110
111
  # If we're getting back this thing, update; otherwise, instantiate.
111
112
  if Util.object_name_matches_class?(resp.data[:object], self.class)
@@ -114,5 +115,13 @@ module Stripe
114
115
  Util.convert_to_stripe_object(resp.data, opts)
115
116
  end
116
117
  end
118
+
119
+ protected def request_stream(method:, path:, params:, opts: {},
120
+ &read_body_chunk_block)
121
+ resp, = execute_resource_request_stream(
122
+ method, path, params, opts, &read_body_chunk_block
123
+ )
124
+ resp
125
+ end
117
126
  end
118
127
  end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stripe
4
+ # Manages connections across multiple hosts which is useful because the
5
+ # library may connect to multiple hosts during a typical session (main API,
6
+ # Connect, Uploads). Ruby doesn't provide an easy way to make this happen
7
+ # easily, so this class is designed to track what we're connected to and
8
+ # manage the lifecycle of those connections.
9
+ #
10
+ # Note that this class in itself is *not* thread safe. We expect it to be
11
+ # instantiated once per thread.
12
+ class ConnectionManager
13
+ # Timestamp (in seconds procured from the system's monotonic clock)
14
+ # indicating when the connection manager last made a request. This is used
15
+ # by `StripeClient` to determine whether a connection manager should be
16
+ # garbage collected or not.
17
+ attr_reader :last_used
18
+ attr_reader :config
19
+
20
+ def initialize(config = Stripe.config)
21
+ @config = config
22
+ @active_connections = {}
23
+ @last_used = Util.monotonic_time
24
+
25
+ # A connection manager may be accessed across threads as one thread makes
26
+ # requests on it while another is trying to clear it (either because it's
27
+ # trying to garbage collect the manager or trying to clear it because a
28
+ # configuration setting has changed). That's probably thread-safe already
29
+ # because of Ruby's GIL, but just in case the library's running on JRuby
30
+ # or the like, use a mutex to synchronize access in this connection
31
+ # manager.
32
+ @mutex = Mutex.new
33
+ end
34
+
35
+ # Finishes any active connections by closing their TCP connection and
36
+ # clears them from internal tracking.
37
+ def clear
38
+ @mutex.synchronize do
39
+ @active_connections.each do |_, connection|
40
+ connection.finish
41
+ end
42
+ @active_connections = {}
43
+ end
44
+ end
45
+
46
+ # Gets a connection for a given URI. This is for internal use only as it's
47
+ # subject to change (we've moved between HTTP client schemes in the past
48
+ # and may do it again).
49
+ #
50
+ # `uri` is expected to be a string.
51
+ def connection_for(uri)
52
+ @mutex.synchronize do
53
+ u = URI.parse(uri)
54
+ connection = @active_connections[[u.host, u.port]]
55
+
56
+ if connection.nil?
57
+ connection = create_connection(u)
58
+ connection.start
59
+
60
+ @active_connections[[u.host, u.port]] = connection
61
+ end
62
+
63
+ connection
64
+ end
65
+ end
66
+
67
+ # Executes an HTTP request to the given URI with the given method. Also
68
+ # allows a request body, headers, and query string to be specified.
69
+ def execute_request(method, uri, body: nil, headers: nil, query: nil,
70
+ &block)
71
+ # Perform some basic argument validation because it's easy to get
72
+ # confused between strings and hashes for things like body and query
73
+ # parameters.
74
+ raise ArgumentError, "method should be a symbol" \
75
+ unless method.is_a?(Symbol)
76
+ raise ArgumentError, "uri should be a string" \
77
+ unless uri.is_a?(String)
78
+ raise ArgumentError, "body should be a string" \
79
+ if body && !body.is_a?(String)
80
+ raise ArgumentError, "headers should be a hash" \
81
+ if headers && !headers.is_a?(Hash)
82
+ raise ArgumentError, "query should be a string" \
83
+ if query && !query.is_a?(String)
84
+
85
+ @last_used = Util.monotonic_time
86
+
87
+ connection = connection_for(uri)
88
+
89
+ u = URI.parse(uri)
90
+ path = if query
91
+ u.path + "?" + query
92
+ else
93
+ u.path
94
+ end
95
+
96
+ method_name = method.to_s.upcase
97
+ has_response_body = method_name != "HEAD"
98
+ request = Net::HTTPGenericRequest.new(
99
+ method_name,
100
+ (body ? true : false),
101
+ has_response_body,
102
+ path,
103
+ headers
104
+ )
105
+
106
+ @mutex.synchronize do
107
+ # The block parameter is special here. If a block is provided, the block
108
+ # is invoked with the Net::HTTPResponse. However, the body will not have
109
+ # been read yet in the block, and can be streamed by calling
110
+ # HTTPResponse#read_body.
111
+ connection.request(request, body, &block)
112
+ end
113
+ end
114
+
115
+ #
116
+ # private
117
+ #
118
+
119
+ # `uri` should be a parsed `URI` object.
120
+ private def create_connection(uri)
121
+ # These all come back as `nil` if no proxy is configured.
122
+ proxy_host, proxy_port, proxy_user, proxy_pass = proxy_parts
123
+
124
+ connection = Net::HTTP.new(uri.host, uri.port,
125
+ proxy_host, proxy_port,
126
+ proxy_user, proxy_pass)
127
+
128
+ # Time in seconds within which Net::HTTP will try to reuse an already
129
+ # open connection when issuing a new operation. Outside this window, Ruby
130
+ # will transparently close and re-open the connection without trying to
131
+ # reuse it.
132
+ #
133
+ # Ruby's default of 2 seconds is almost certainly too short. Here I've
134
+ # reused Go's default for `DefaultTransport`.
135
+ connection.keep_alive_timeout = 30
136
+
137
+ connection.open_timeout = config.open_timeout
138
+ connection.read_timeout = config.read_timeout
139
+ if connection.respond_to?(:write_timeout=)
140
+ connection.write_timeout = config.write_timeout
141
+ end
142
+
143
+ connection.use_ssl = uri.scheme == "https"
144
+
145
+ if config.verify_ssl_certs
146
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
147
+ connection.cert_store = config.ca_store
148
+ else
149
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
150
+ warn_ssl_verify_none
151
+ end
152
+
153
+ connection
154
+ end
155
+
156
+ # `Net::HTTP` somewhat awkwardly requires each component of a proxy URI
157
+ # (host, port, etc.) rather than the URI itself. This method simply parses
158
+ # out those pieces to make passing them into a new connection a little less
159
+ # ugly.
160
+ private def proxy_parts
161
+ if config.proxy.nil?
162
+ [nil, nil, nil, nil]
163
+ else
164
+ u = URI.parse(config.proxy)
165
+ [u.host, u.port, u.user, u.password]
166
+ end
167
+ end
168
+
169
+ private def warn_ssl_verify_none
170
+ return if @verify_ssl_warned
171
+
172
+ @verify_ssl_warned = true
173
+ warn("WARNING: Running without SSL cert verification. " \
174
+ "You should never do this in production. " \
175
+ "Execute `Stripe.verify_ssl_certs = true` to enable " \
176
+ "verification.")
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stripe
4
+ # Represents an error object as returned by the API.
5
+ #
6
+ # @see https://stripe.com/docs/api/errors
7
+ class ErrorObject < StripeObject
8
+ # Unlike other objects, we explicitly declare getter methods here. This
9
+ # is because the API doesn't return `null` values for fields on this
10
+ # object, rather the fields are omitted entirely. Not declaring the getter
11
+ # methods would cause users to run into `NoMethodError` exceptions and
12
+ # get in the way of generic error handling.
13
+
14
+ # For card errors, the ID of the failed charge.
15
+ def charge
16
+ @values[:charge]
17
+ end
18
+
19
+ # For some errors that could be handled programmatically, a short string
20
+ # indicating the error code reported.
21
+ def code
22
+ @values[:code]
23
+ end
24
+
25
+ # For card errors resulting from a card issuer decline, a short string
26
+ # indicating the card issuer's reason for the decline if they provide one.
27
+ def decline_code
28
+ @values[:decline_code]
29
+ end
30
+
31
+ # A URL to more information about the error code reported.
32
+ def doc_url
33
+ @values[:doc_url]
34
+ end
35
+
36
+ # A human-readable message providing more details about the error. For card
37
+ # errors, these messages can be shown to your users.
38
+ def message
39
+ @values[:message]
40
+ end
41
+
42
+ # If the error is parameter-specific, the parameter related to the error.
43
+ # For example, you can use this to display a message near the correct form
44
+ # field.
45
+ def param
46
+ @values[:param]
47
+ end
48
+
49
+ # The PaymentIntent object for errors returned on a request involving a
50
+ # PaymentIntent.
51
+ def payment_intent
52
+ @values[:payment_intent]
53
+ end
54
+
55
+ # The PaymentMethod object for errors returned on a request involving a
56
+ # PaymentMethod.
57
+ def payment_method
58
+ @values[:payment_method]
59
+ end
60
+
61
+ # The SetupIntent object for errors returned on a request involving a
62
+ # SetupIntent.
63
+ def setup_intent
64
+ @values[:setup_intent]
65
+ end
66
+
67
+ # The source object for errors returned on a request involving a source.
68
+ def source
69
+ @values[:source]
70
+ end
71
+
72
+ # The type of error returned. One of `api_connection_error`, `api_error`,
73
+ # `authentication_error`, `card_error`, `idempotency_error`,
74
+ # `invalid_request_error`, or `rate_limit_error`.
75
+ def type
76
+ @values[:type]
77
+ end
78
+ end
79
+
80
+ # Represents on OAuth error returned by the OAuth API.
81
+ #
82
+ # @see https://stripe.com/docs/connect/oauth-reference#post-token-errors
83
+ class OAuthErrorObject < StripeObject
84
+ # A unique error code per error type.
85
+ def error
86
+ @values[:error]
87
+ end
88
+
89
+ # A human readable description of the error.
90
+ def error_description
91
+ @values[:error_description]
92
+ end
93
+ end
94
+ end
data/lib/stripe/errors.rb CHANGED
@@ -11,6 +11,7 @@ module Stripe
11
11
  attr_accessor :response
12
12
 
13
13
  attr_reader :code
14
+ attr_reader :error
14
15
  attr_reader :http_body
15
16
  attr_reader :http_headers
16
17
  attr_reader :http_status
@@ -24,9 +25,24 @@ module Stripe
24
25
  @http_status = http_status
25
26
  @http_body = http_body
26
27
  @http_headers = http_headers || {}
28
+ @idempotent_replayed = @http_headers["idempotent-replayed"] == "true"
27
29
  @json_body = json_body
28
30
  @code = code
29
- @request_id = @http_headers[:request_id]
31
+ @request_id = @http_headers["request-id"]
32
+ @error = construct_error_object
33
+ end
34
+
35
+ def construct_error_object
36
+ return nil if @json_body.nil? || !@json_body.key?(:error)
37
+
38
+ ErrorObject.construct_from(@json_body[:error])
39
+ end
40
+
41
+ # Whether the error was the result of an idempotent replay, meaning that it
42
+ # originally occurred on a previous request and is being replayed back
43
+ # because the user sent the same idempotency key for this one.
44
+ def idempotent_replayed?
45
+ @idempotent_replayed
30
46
  end
31
47
 
32
48
  def to_s
@@ -59,8 +75,7 @@ module Stripe
59
75
  class CardError < StripeError
60
76
  attr_reader :param
61
77
 
62
- # TODO: make code a keyword arg in next major release
63
- def initialize(message, param, code, http_status: nil, http_body: nil,
78
+ def initialize(message, param, code: nil, http_status: nil, http_body: nil,
64
79
  json_body: nil, http_headers: nil)
65
80
  super(message, http_status: http_status, http_body: http_body,
66
81
  json_body: json_body, http_headers: http_headers,
@@ -119,6 +134,12 @@ module Stripe
119
134
  json_body: json_body, http_headers: http_headers,
120
135
  code: code)
121
136
  end
137
+
138
+ def construct_error_object
139
+ return nil if @json_body.nil?
140
+
141
+ OAuthErrorObject.construct_from(@json_body)
142
+ end
122
143
  end
123
144
 
124
145
  # InvalidClientError is raised when the client doesn't belong to you, or
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stripe
4
+ class Instrumentation
5
+ # Event emitted on `request_begin` callback.
6
+ class RequestBeginEvent
7
+ attr_reader :method
8
+ attr_reader :path
9
+
10
+ # Arbitrary user-provided data in the form of a Ruby hash that's passed
11
+ # from subscribers on `request_begin` to subscribers on `request_end`.
12
+ # `request_begin` subscribers can set keys which will then be available
13
+ # in `request_end`.
14
+ #
15
+ # Note that all subscribers of `request_begin` share the same object, so
16
+ # they must be careful to set unique keys so as to not conflict with data
17
+ # set by other subscribers.
18
+ attr_reader :user_data
19
+
20
+ def initialize(method:, path:, user_data:)
21
+ @method = method
22
+ @path = path
23
+ @user_data = user_data
24
+ freeze
25
+ end
26
+ end
27
+
28
+ # Event emitted on `request_end` callback.
29
+ class RequestEndEvent
30
+ attr_reader :duration
31
+ attr_reader :http_status
32
+ attr_reader :method
33
+ attr_reader :num_retries
34
+ attr_reader :path
35
+
36
+ # Arbitrary user-provided data in the form of a Ruby hash that's passed
37
+ # from subscribers on `request_begin` to subscribers on `request_end`.
38
+ # `request_begin` subscribers can set keys which will then be available
39
+ # in `request_end`.
40
+ attr_reader :user_data
41
+
42
+ def initialize(duration:, http_status:, method:, num_retries:, path:,
43
+ user_data: nil)
44
+ @duration = duration
45
+ @http_status = http_status
46
+ @method = method
47
+ @num_retries = num_retries
48
+ @path = path
49
+ @user_data = user_data
50
+ freeze
51
+ end
52
+ end
53
+
54
+ # This class was renamed for consistency. This alias is here for backwards
55
+ # compatibility.
56
+ RequestEvent = RequestEndEvent
57
+
58
+ # Returns true if there are a non-zero number of subscribers on the given
59
+ # topic, and false otherwise.
60
+ def self.any_subscribers?(topic)
61
+ !subscribers[topic].empty?
62
+ end
63
+
64
+ def self.subscribe(topic, name = rand, &block)
65
+ subscribers[topic][name] = block
66
+ name
67
+ end
68
+
69
+ def self.unsubscribe(topic, name)
70
+ subscribers[topic].delete(name)
71
+ end
72
+
73
+ def self.notify(topic, event)
74
+ subscribers[topic].each_value { |subscriber| subscriber.call(event) }
75
+ end
76
+
77
+ def self.subscribers
78
+ @subscribers ||= Hash.new { |hash, key| hash[key] = {} }
79
+ end
80
+ private_class_method :subscribers
81
+ end
82
+ end