stripe 4.20.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +17 -4
  3. data/.rubocop_todo.yml +10 -9
  4. data/.travis.yml +2 -6
  5. data/CHANGELOG.md +52 -1
  6. data/Gemfile +2 -12
  7. data/README.md +10 -10
  8. data/Rakefile +8 -7
  9. data/VERSION +1 -1
  10. data/lib/stripe/api_operations/list.rb +0 -6
  11. data/lib/stripe/api_resource.rb +16 -0
  12. data/lib/stripe/connection_manager.rb +131 -0
  13. data/lib/stripe/error_object.rb +94 -0
  14. data/lib/stripe/errors.rb +15 -2
  15. data/lib/stripe/list_object.rb +2 -1
  16. data/lib/stripe/multipart_encoder.rb +131 -0
  17. data/lib/stripe/object_types.rb +1 -4
  18. data/lib/stripe/resources/account.rb +7 -7
  19. data/lib/stripe/resources/account_link.rb +1 -1
  20. data/lib/stripe/resources/alipay_account.rb +1 -1
  21. data/lib/stripe/resources/apple_pay_domain.rb +1 -1
  22. data/lib/stripe/resources/application_fee.rb +1 -12
  23. data/lib/stripe/resources/application_fee_refund.rb +1 -1
  24. data/lib/stripe/resources/balance.rb +1 -1
  25. data/lib/stripe/resources/balance_transaction.rb +1 -5
  26. data/lib/stripe/resources/bank_account.rb +1 -1
  27. data/lib/stripe/resources/bitcoin_receiver.rb +1 -1
  28. data/lib/stripe/resources/bitcoin_transaction.rb +1 -1
  29. data/lib/stripe/resources/capability.rb +1 -1
  30. data/lib/stripe/resources/card.rb +1 -1
  31. data/lib/stripe/resources/charge.rb +7 -69
  32. data/lib/stripe/resources/checkout/session.rb +1 -1
  33. data/lib/stripe/resources/country_spec.rb +1 -1
  34. data/lib/stripe/resources/coupon.rb +1 -1
  35. data/lib/stripe/resources/credit_note.rb +7 -3
  36. data/lib/stripe/resources/customer.rb +5 -66
  37. data/lib/stripe/resources/customer_balance_transaction.rb +1 -1
  38. data/lib/stripe/resources/discount.rb +1 -1
  39. data/lib/stripe/resources/dispute.rb +7 -9
  40. data/lib/stripe/resources/ephemeral_key.rb +1 -1
  41. data/lib/stripe/resources/event.rb +1 -1
  42. data/lib/stripe/resources/exchange_rate.rb +1 -1
  43. data/lib/stripe/resources/file.rb +3 -13
  44. data/lib/stripe/resources/file_link.rb +1 -1
  45. data/lib/stripe/resources/invoice.rb +36 -11
  46. data/lib/stripe/resources/invoice_item.rb +1 -1
  47. data/lib/stripe/resources/invoice_line_item.rb +1 -1
  48. data/lib/stripe/resources/issuing/authorization.rb +13 -5
  49. data/lib/stripe/resources/issuing/card.rb +7 -3
  50. data/lib/stripe/resources/issuing/card_details.rb +1 -1
  51. data/lib/stripe/resources/issuing/cardholder.rb +1 -1
  52. data/lib/stripe/resources/issuing/dispute.rb +1 -1
  53. data/lib/stripe/resources/issuing/transaction.rb +1 -1
  54. data/lib/stripe/resources/login_link.rb +1 -1
  55. data/lib/stripe/resources/order.rb +13 -13
  56. data/lib/stripe/resources/order_return.rb +1 -1
  57. data/lib/stripe/resources/payment_intent.rb +19 -7
  58. data/lib/stripe/resources/payment_method.rb +13 -5
  59. data/lib/stripe/resources/payout.rb +7 -9
  60. data/lib/stripe/resources/person.rb +1 -1
  61. data/lib/stripe/resources/plan.rb +1 -1
  62. data/lib/stripe/resources/product.rb +1 -1
  63. data/lib/stripe/resources/radar/early_fraud_warning.rb +1 -1
  64. data/lib/stripe/resources/radar/value_list.rb +1 -1
  65. data/lib/stripe/resources/radar/value_list_item.rb +1 -1
  66. data/lib/stripe/resources/recipient.rb +1 -5
  67. data/lib/stripe/resources/recipient_transfer.rb +1 -1
  68. data/lib/stripe/resources/refund.rb +1 -1
  69. data/lib/stripe/resources/reporting/report_run.rb +1 -1
  70. data/lib/stripe/resources/reporting/report_type.rb +1 -1
  71. data/lib/stripe/resources/reversal.rb +1 -1
  72. data/lib/stripe/resources/review.rb +7 -3
  73. data/lib/stripe/resources/setup_intent.rb +32 -0
  74. data/lib/stripe/resources/sigma/scheduled_query_run.rb +1 -1
  75. data/lib/stripe/resources/sku.rb +1 -1
  76. data/lib/stripe/resources/source.rb +7 -9
  77. data/lib/stripe/resources/source_transaction.rb +1 -1
  78. data/lib/stripe/resources/subscription.rb +9 -9
  79. data/lib/stripe/resources/subscription_item.rb +4 -1
  80. data/lib/stripe/resources/subscription_schedule.rb +13 -13
  81. data/lib/stripe/resources/tax_id.rb +1 -1
  82. data/lib/stripe/resources/tax_rate.rb +1 -1
  83. data/lib/stripe/resources/terminal/connection_token.rb +1 -1
  84. data/lib/stripe/resources/terminal/location.rb +1 -1
  85. data/lib/stripe/resources/terminal/reader.rb +1 -1
  86. data/lib/stripe/resources/three_d_secure.rb +1 -1
  87. data/lib/stripe/resources/token.rb +1 -1
  88. data/lib/stripe/resources/topup.rb +7 -3
  89. data/lib/stripe/resources/transfer.rb +7 -8
  90. data/lib/stripe/resources/usage_record.rb +1 -17
  91. data/lib/stripe/resources/usage_record_summary.rb +1 -1
  92. data/lib/stripe/resources/webhook_endpoint.rb +1 -1
  93. data/lib/stripe/resources.rb +1 -2
  94. data/lib/stripe/stripe_client.rb +281 -183
  95. data/lib/stripe/stripe_object.rb +4 -23
  96. data/lib/stripe/stripe_response.rb +53 -21
  97. data/lib/stripe/util.rb +14 -11
  98. data/lib/stripe/version.rb +1 -1
  99. data/lib/stripe/webhook.rb +1 -1
  100. data/lib/stripe.rb +56 -15
  101. data/stripe.gemspec +10 -3
  102. data/test/stripe/account_test.rb +0 -16
  103. data/test/stripe/api_operations_test.rb +2 -2
  104. data/test/stripe/api_resource_test.rb +98 -8
  105. data/test/stripe/balance_transaction_test.rb +20 -0
  106. data/test/stripe/charge_test.rb +0 -16
  107. data/test/stripe/connection_manager_test.rb +138 -0
  108. data/test/stripe/customer_test.rb +1 -44
  109. data/test/stripe/errors_test.rb +29 -8
  110. data/test/stripe/file_test.rb +0 -10
  111. data/test/stripe/invoice_test.rb +17 -1
  112. data/test/stripe/list_object_test.rb +0 -16
  113. data/test/stripe/login_link_test.rb +1 -1
  114. data/test/stripe/multipart_encoder_test.rb +130 -0
  115. data/test/stripe/payment_intent_test.rb +2 -2
  116. data/test/stripe/setup_intent_test.rb +84 -0
  117. data/test/stripe/source_test.rb +0 -18
  118. data/test/stripe/stripe_client_test.rb +214 -29
  119. data/test/stripe/stripe_object_test.rb +7 -35
  120. data/test/stripe/stripe_response_test.rb +70 -24
  121. data/test/stripe/subscription_item_test.rb +12 -0
  122. data/test/stripe/subscription_schedule_test.rb +0 -34
  123. data/test/stripe/subscription_test.rb +2 -2
  124. data/test/stripe/webhook_test.rb +2 -2
  125. data/test/stripe_mock.rb +4 -3
  126. data/test/stripe_test.rb +0 -13
  127. data/test/test_helper.rb +10 -5
  128. metadata +23 -43
  129. data/lib/stripe/resources/issuer_fraud_record.rb +0 -9
  130. data/lib/stripe/resources/subscription_schedule_revision.rb +0 -34
  131. data/test/stripe/file_upload_test.rb +0 -79
  132. data/test/stripe/issuer_fraud_record_test.rb +0 -20
  133. data/test/stripe/subscription_schedule_revision_test.rb +0 -37
  134. data/test/stripe/usage_record_test.rb +0 -28
@@ -127,18 +127,6 @@ module Stripe
127
127
  JSON.pretty_generate(@values)
128
128
  end
129
129
 
130
- # Re-initializes the object based on a hash of values (usually one that's
131
- # come back from an API call). Adds or removes value accessors as necessary
132
- # and updates the state of internal data.
133
- #
134
- # Please don't use this method. If you're trying to do mass assignment, try
135
- # #initialize_from instead.
136
- def refresh_from(values, opts, partial = false)
137
- initialize_from(values, opts, partial)
138
- end
139
- extend Gem::Deprecate
140
- deprecate :refresh_from, "#update_attributes", 2016, 1
141
-
142
130
  # Mass assigns attributes on the model.
143
131
  #
144
132
  # This is a version of +update_attributes+ that takes some extra options
@@ -192,7 +180,9 @@ module Stripe
192
180
 
193
181
  def to_hash
194
182
  maybe_to_hash = lambda do |value|
195
- value && value.respond_to?(:to_hash) ? value.to_hash : value
183
+ return nil if value.nil?
184
+
185
+ value.respond_to?(:to_hash) ? value.to_hash : value
196
186
  end
197
187
 
198
188
  @values.each_with_object({}) do |(key, value), acc|
@@ -256,6 +246,7 @@ module Stripe
256
246
  #
257
247
  unsaved = @unsaved_values.include?(k)
258
248
  next unless options[:force] || unsaved || v.is_a?(StripeObject)
249
+
259
250
  update_hash[k.to_sym] = serialize_params_value(
260
251
  @values[k], @original_values[k], unsaved, options[:force], key: k
261
252
  )
@@ -268,16 +259,6 @@ module Stripe
268
259
  update_hash
269
260
  end
270
261
 
271
- class << self
272
- # This class method has been deprecated in favor of the instance method
273
- # of the same name.
274
- def serialize_params(obj, options = {})
275
- obj.serialize_params(options)
276
- end
277
- extend Gem::Deprecate
278
- deprecate :serialize_params, "#serialize_params", 2016, 9
279
- end
280
-
281
262
  # A protected field is one that doesn't get an accessor assigned to it
282
263
  # (i.e. `obj.public = ...`) and one which is not allowed to be updated via
283
264
  # the class level `Model.update(id, { ... })`.
@@ -4,6 +4,53 @@ module Stripe
4
4
  # StripeResponse encapsulates some vitals of a response that came back from
5
5
  # the Stripe API.
6
6
  class StripeResponse
7
+ # Headers provides an access wrapper to an API response's header data. It
8
+ # mainly exists so that we don't need to expose the entire
9
+ # `Net::HTTPResponse` object while still getting some of its benefits like
10
+ # case-insensitive access to header names and flattening of header values.
11
+ class Headers
12
+ # Initializes a Headers object from a Net::HTTP::HTTPResponse object.
13
+ def self.from_net_http(resp)
14
+ new(resp.to_hash)
15
+ end
16
+
17
+ # `hash` is expected to be a hash mapping header names to arrays of
18
+ # header values. This is the default format generated by calling
19
+ # `#to_hash` on a `Net::HTTPResponse` object because headers can be
20
+ # repeated multiple times. Using `#[]` will collapse values down to just
21
+ # the first.
22
+ def initialize(hash)
23
+ if !hash.is_a?(Hash) ||
24
+ !hash.keys.all? { |n| n.is_a?(String) } ||
25
+ !hash.values.all? { |a| a.is_a?(Array) } ||
26
+ !hash.values.all? { |a| a.all? { |v| v.is_a?(String) } }
27
+ raise ArgumentError,
28
+ "expect hash to be a map of string header names to arrays of " \
29
+ "header values"
30
+ end
31
+
32
+ @hash = {}
33
+
34
+ # This shouldn't be strictly necessary because `Net::HTTPResponse` will
35
+ # produce a hash with all headers downcased, but do it anyway just in
36
+ # case an object of this class was constructed manually.
37
+ #
38
+ # Also has the effect of duplicating the hash, which is desirable for a
39
+ # little extra object safety.
40
+ hash.each do |k, v|
41
+ @hash[k.downcase] = v
42
+ end
43
+ end
44
+
45
+ def [](name)
46
+ values = @hash[name.downcase]
47
+ if values && values.count > 1
48
+ warn("Duplicate header values for `#{name}`; returning only first")
49
+ end
50
+ values ? values.first : nil
51
+ end
52
+ end
53
+
7
54
  # The data contained by the HTTP body of the response deserialized from
8
55
  # JSON.
9
56
  attr_accessor :data
@@ -20,30 +67,15 @@ module Stripe
20
67
  # The Stripe request ID of the response.
21
68
  attr_accessor :request_id
22
69
 
23
- # Initializes a StripeResponse object from a Hash like the kind returned as
24
- # part of a Faraday exception.
25
- #
26
- # This may throw JSON::ParserError if the response body is not valid JSON.
27
- def self.from_faraday_hash(http_resp)
28
- resp = StripeResponse.new
29
- resp.data = JSON.parse(http_resp[:body], symbolize_names: true)
30
- resp.http_body = http_resp[:body]
31
- resp.http_headers = http_resp[:headers]
32
- resp.http_status = http_resp[:status]
33
- resp.request_id = http_resp[:headers]["Request-Id"]
34
- resp
35
- end
36
-
37
- # Initializes a StripeResponse object from a Faraday HTTP response object.
38
- #
39
- # This may throw JSON::ParserError if the response body is not valid JSON.
40
- def self.from_faraday_response(http_resp)
70
+ # Initializes a StripeResponse object from a Net::HTTP::HTTPResponse
71
+ # object.
72
+ def self.from_net_http(http_resp)
41
73
  resp = StripeResponse.new
42
74
  resp.data = JSON.parse(http_resp.body, symbolize_names: true)
43
75
  resp.http_body = http_resp.body
44
- resp.http_headers = http_resp.headers
45
- resp.http_status = http_resp.status
46
- resp.request_id = http_resp.headers["Request-Id"]
76
+ resp.http_headers = Headers.from_net_http(http_resp)
77
+ resp.http_status = http_resp.code.to_i
78
+ resp.request_id = http_resp["request-id"]
47
79
  resp
48
80
  end
49
81
  end
data/lib/stripe/util.rb CHANGED
@@ -43,6 +43,10 @@ module Stripe
43
43
  @object_classes ||= Stripe::ObjectTypes.object_names_to_classes
44
44
  end
45
45
 
46
+ def self.object_name_matches_class?(object_name, klass)
47
+ Util.object_classes[object_name] == klass
48
+ end
49
+
46
50
  # Converts a hash of fields or an array of hashes into a +StripeObject+ or
47
51
  # array of +StripeObject+s. These new objects will be created as a concrete
48
52
  # type as dictated by their `object` field (e.g. an `object` value of
@@ -194,11 +198,13 @@ module Stripe
194
198
 
195
199
  def self.check_string_argument!(key)
196
200
  raise TypeError, "argument must be a string" unless key.is_a?(String)
201
+
197
202
  key
198
203
  end
199
204
 
200
205
  def self.check_api_key!(key)
201
206
  raise TypeError, "api_key must be a string" unless key.is_a?(String)
207
+
202
208
  key
203
209
  end
204
210
 
@@ -241,14 +247,14 @@ module Stripe
241
247
  #
242
248
 
243
249
  COLOR_CODES = {
244
- black: 0, light_black: 60,
245
- red: 1, light_red: 61,
246
- green: 2, light_green: 62,
247
- yellow: 3, light_yellow: 63,
248
- blue: 4, light_blue: 64,
250
+ black: 0, light_black: 60,
251
+ red: 1, light_red: 61,
252
+ green: 2, light_green: 62,
253
+ yellow: 3, light_yellow: 63,
254
+ blue: 4, light_blue: 64,
249
255
  magenta: 5, light_magenta: 65,
250
- cyan: 6, light_cyan: 66,
251
- white: 7, light_white: 67,
256
+ cyan: 6, light_cyan: 66,
257
+ white: 7, light_white: 67,
252
258
  default: 9,
253
259
  }.freeze
254
260
  private_constant :COLOR_CODES
@@ -277,10 +283,7 @@ module Stripe
277
283
  end
278
284
  private_class_method :level_name
279
285
 
280
- # TODO: Make these named required arguments when we drop support for Ruby
281
- # 2.0.
282
- def self.log_internal(message, data = {}, color: nil, level: nil,
283
- logger: nil, out: nil)
286
+ def self.log_internal(message, data = {}, color:, level:, logger:, out:)
284
287
  data_str = data.reject { |_k, v| v.nil? }
285
288
  .map do |(k, v)|
286
289
  format("%<key>s=%<value>s",
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stripe
4
- VERSION = "4.20.0".freeze
4
+ VERSION = "5.0.0"
5
5
  end
@@ -22,7 +22,7 @@ module Stripe
22
22
  end
23
23
 
24
24
  module Signature
25
- EXPECTED_SCHEME = "v1".freeze
25
+ EXPECTED_SCHEME = "v1"
26
26
 
27
27
  def self.compute_signature(payload, secret)
28
28
  OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), secret, payload)
data/lib/stripe.rb CHANGED
@@ -3,9 +3,9 @@
3
3
  # Stripe Ruby bindings
4
4
  # API spec at https://stripe.com/docs/api
5
5
  require "cgi"
6
- require "faraday"
7
6
  require "json"
8
7
  require "logger"
8
+ require "net/http"
9
9
  require "openssl"
10
10
  require "rbconfig"
11
11
  require "securerandom"
@@ -28,10 +28,13 @@ require "stripe/api_operations/save"
28
28
  require "stripe/errors"
29
29
  require "stripe/object_types"
30
30
  require "stripe/util"
31
+ require "stripe/connection_manager"
32
+ require "stripe/multipart_encoder"
31
33
  require "stripe/stripe_client"
32
34
  require "stripe/stripe_object"
33
35
  require "stripe/stripe_response"
34
36
  require "stripe/list_object"
37
+ require "stripe/error_object"
35
38
  require "stripe/api_resource"
36
39
  require "stripe/singleton_api_resource"
37
40
  require "stripe/webhook"
@@ -70,9 +73,20 @@ module Stripe
70
73
  @enable_telemetry = true
71
74
 
72
75
  class << self
73
- attr_accessor :stripe_account, :api_key, :api_base, :verify_ssl_certs,
74
- :api_version, :client_id, :connect_base, :uploads_base,
75
- :open_timeout, :read_timeout, :proxy
76
+ attr_accessor :api_key
77
+ attr_accessor :api_version
78
+ attr_accessor :client_id
79
+ attr_accessor :stripe_account
80
+
81
+ # These all get manual attribute writers so that we can reset connections
82
+ # if they change.
83
+ attr_reader :api_base
84
+ attr_reader :connect_base
85
+ attr_reader :open_timeout
86
+ attr_reader :proxy
87
+ attr_reader :read_timeout
88
+ attr_reader :uploads_base
89
+ attr_reader :verify_ssl_certs
76
90
 
77
91
  attr_reader :max_network_retry_delay, :initial_network_retry_delay
78
92
  end
@@ -87,6 +101,11 @@ module Stripe
87
101
  @app_info = info
88
102
  end
89
103
 
104
+ def self.api_base=(api_base)
105
+ @api_base = api_base
106
+ StripeClient.clear_all_connection_managers
107
+ end
108
+
90
109
  # The location of a file containing a bundle of CA certificates. By default
91
110
  # the library will use an included bundle that can successfully validate
92
111
  # Stripe certificates.
@@ -99,6 +118,8 @@ module Stripe
99
118
 
100
119
  # empty this field so a new store is initialized
101
120
  @ca_store = nil
121
+
122
+ StripeClient.clear_all_connection_managers
102
123
  end
103
124
 
104
125
  # A certificate store initialized from the the bundle in #ca_bundle_path and
@@ -118,6 +139,19 @@ module Stripe
118
139
  end
119
140
  end
120
141
 
142
+ def self.connection_base=(connection_base)
143
+ @connection_base = connection_base
144
+ StripeClient.clear_all_connection_managers
145
+ end
146
+
147
+ def self.enable_telemetry?
148
+ @enable_telemetry
149
+ end
150
+
151
+ def self.enable_telemetry=(val)
152
+ @enable_telemetry = val
153
+ end
154
+
121
155
  # map to the same values as the standard library's logger
122
156
  LEVEL_DEBUG = Logger::DEBUG
123
157
  LEVEL_ERROR = Logger::ERROR
@@ -172,12 +206,19 @@ module Stripe
172
206
  @max_network_retries = val.to_i
173
207
  end
174
208
 
175
- def self.enable_telemetry?
176
- @enable_telemetry
209
+ def self.open_timeout=(open_timeout)
210
+ @open_timeout = open_timeout
211
+ StripeClient.clear_all_connection_managers
177
212
  end
178
213
 
179
- def self.enable_telemetry=(val)
180
- @enable_telemetry = val
214
+ def self.proxy=(proxy)
215
+ @proxy = proxy
216
+ StripeClient.clear_all_connection_managers
217
+ end
218
+
219
+ def self.read_timeout=(read_timeout)
220
+ @read_timeout = read_timeout
221
+ StripeClient.clear_all_connection_managers
181
222
  end
182
223
 
183
224
  # Sets some basic information about the running application that's sent along
@@ -194,14 +235,14 @@ module Stripe
194
235
  }
195
236
  end
196
237
 
197
- # DEPRECATED. Use `Util#encode_parameters` instead.
198
- def self.uri_encode(params)
199
- Util.encode_parameters(params)
238
+ def self.uploads_base=(uploads_base)
239
+ @uploads_base = uploads_base
240
+ StripeClient.clear_all_connection_managers
200
241
  end
201
- private_class_method :uri_encode
202
- class << self
203
- extend Gem::Deprecate
204
- deprecate :uri_encode, "Stripe::Util#encode_parameters", 2016, 1
242
+
243
+ def self.verify_ssl_certs=(verify_ssl_certs)
244
+ @verify_ssl_certs = verify_ssl_certs
245
+ StripeClient.clear_all_connection_managers
205
246
  end
206
247
  end
207
248
 
data/stripe.gemspec CHANGED
@@ -7,7 +7,7 @@ require "stripe/version"
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "stripe"
9
9
  s.version = Stripe::VERSION
10
- s.required_ruby_version = ">= 2.1.0"
10
+ s.required_ruby_version = ">= 2.3.0"
11
11
  s.summary = "Ruby bindings for the Stripe API"
12
12
  s.description = "Stripe is the easiest way to accept payments online. " \
13
13
  "See https://stripe.com for details."
@@ -16,8 +16,15 @@ Gem::Specification.new do |s|
16
16
  s.homepage = "https://stripe.com/docs/api/ruby"
17
17
  s.license = "MIT"
18
18
 
19
- s.add_dependency("faraday", "~> 0.13")
20
- s.add_dependency("net-http-persistent", "~> 3.0")
19
+ s.metadata = {
20
+ "bug_tracker_uri" => "https://github.com/stripe/stripe-ruby/issues",
21
+ "changelog_uri" =>
22
+ "https://github.com/stripe/stripe-ruby/blob/master/CHANGELOG.md",
23
+ "documentation_uri" => "https://stripe.com/docs/api/ruby",
24
+ "github_repo" => "ssh://github.com/stripe/stripe-ruby",
25
+ "homepage_uri" => "https://stripe.com/docs/api/ruby",
26
+ "source_code_uri" => "https://github.com/stripe/stripe-ruby",
27
+ }
21
28
 
22
29
  s.files = `git ls-files`.split("\n")
23
30
  s.test_files = `git ls-files -- test/*`.split("\n")
@@ -82,22 +82,6 @@ module Stripe
82
82
  assert persons.data[0].is_a?(Stripe::Person)
83
83
  end
84
84
 
85
- context "#bank_account=" do
86
- should "warn that #bank_account= is deprecated" do
87
- old_stderr = $stderr
88
- $stderr = StringIO.new
89
- begin
90
- account = Stripe::Account.retrieve("acct_123")
91
- account.bank_account = "tok_123"
92
- message = "NOTE: Stripe::Account#bank_account= is " \
93
- "deprecated; use #external_account= instead"
94
- assert_match Regexp.new(message), $stderr.string
95
- ensure
96
- $stderr = old_stderr
97
- end
98
- end
99
- end
100
-
101
85
  context "#deauthorize" do
102
86
  should "deauthorize an account" do
103
87
  account = Stripe::Account.retrieve("acct_123")
@@ -7,7 +7,7 @@ module Stripe
7
7
  class UpdateableResource < APIResource
8
8
  include Stripe::APIOperations::Save
9
9
 
10
- OBJECT_NAME = "updateableresource".freeze
10
+ OBJECT_NAME = "updateableresource"
11
11
 
12
12
  def self.protected_fields
13
13
  [:protected]
@@ -34,7 +34,7 @@ module Stripe
34
34
  context ".nested_resource_class_methods" do
35
35
  class MainResource < APIResource
36
36
  extend Stripe::APIOperations::NestedResource
37
- OBJECT_NAME = "mainresource".freeze
37
+ OBJECT_NAME = "mainresource"
38
38
  nested_resource_class_methods :nested,
39
39
  operations: %i[create retrieve update delete list]
40
40
  end
@@ -4,10 +4,31 @@ require ::File.expand_path("../test_helper", __dir__)
4
4
 
5
5
  module Stripe
6
6
  class ApiResourceTest < Test::Unit::TestCase
7
+ class CustomMethodAPIResource < APIResource
8
+ OBJECT_NAME = "custom_method"
9
+ custom_method :my_method, http_verb: :post
10
+ end
11
+
7
12
  class NestedTestAPIResource < APIResource
8
13
  save_nested_resource :external_account
9
14
  end
10
15
 
16
+ context ".custom_method" do
17
+ should "call to an RPC-style method" do
18
+ stub_request(:post, "#{Stripe.api_base}/v1/custom_methods/ch_123/my_method")
19
+ .to_return(body: JSON.generate({}))
20
+ CustomMethodAPIResource.my_method("ch_123")
21
+ end
22
+
23
+ should "raise an error if a non-ID is passed" do
24
+ e = assert_raises ArgumentError do
25
+ CustomMethodAPIResource.my_method(id: "ch_123")
26
+ end
27
+ assert_equal "id should be a string representing the ID of an API resource",
28
+ e.message
29
+ end
30
+ end
31
+
11
32
  context ".save_nested_resource" do
12
33
  should "can have a scalar set" do
13
34
  r = NestedTestAPIResource.new("test_resource")
@@ -250,14 +271,6 @@ module Stripe
250
271
  assert_equal c.created, 12_345
251
272
  end
252
273
 
253
- should "accessing a property other than id or parent on an unfetched object should fetch it" do
254
- stub_request(:get, "#{Stripe.api_base}/v1/charges")
255
- .with(query: { customer: "cus_123" })
256
- .to_return(body: JSON.generate(customer_fixture))
257
- c = Stripe::Customer.new("cus_123")
258
- c.charges
259
- end
260
-
261
274
  should "updating an object should issue a POST request with only the changed properties" do
262
275
  stub_request(:post, "#{Stripe.api_base}/v1/customers/cus_123")
263
276
  .with(body: { "description" => "another_mn" })
@@ -490,6 +503,83 @@ module Stripe
490
503
  end
491
504
  end
492
505
 
506
+ context "#request_stripe_object" do
507
+ class HelloTestAPIResource < APIResource
508
+ OBJECT_NAME = "hello"
509
+ def say_hello(params = {}, opts = {})
510
+ request_stripe_object(
511
+ method: :post,
512
+ path: resource_url + "/say",
513
+ params: params,
514
+ opts: opts
515
+ )
516
+ end
517
+ end
518
+
519
+ setup do
520
+ Util.instance_variable_set(
521
+ :@object_classes,
522
+ Stripe::ObjectTypes.object_names_to_classes.merge(
523
+ "hello" => HelloTestAPIResource
524
+ )
525
+ )
526
+ end
527
+ teardown do
528
+ Util.class.instance_variable_set(:@object_classes, Stripe::ObjectTypes.object_names_to_classes)
529
+ end
530
+
531
+ should "make requests appropriately" do
532
+ stub_request(:post, "#{Stripe.api_base}/v1/hellos/hi_123/say")
533
+ .with(body: { foo: "bar" }, headers: { "Stripe-Account" => "acct_hi" })
534
+ .to_return(body: JSON.generate("object" => "hello"))
535
+
536
+ hello = HelloTestAPIResource.new(id: "hi_123")
537
+ hello.say_hello({ foo: "bar" }, stripe_account: "acct_hi")
538
+ end
539
+
540
+ should "update attributes in-place when it returns the same thing" do
541
+ stub_request(:post, "#{Stripe.api_base}/v1/hellos/hi_123/say")
542
+ .to_return(body: JSON.generate("object" => "hello", "additional" => "attribute"))
543
+
544
+ hello = HelloTestAPIResource.new(id: "hi_123")
545
+ hello.unsaved = "a value"
546
+ new_hello = hello.say_hello
547
+
548
+ # Doesn't matter if you use the return variable or the instance.
549
+ assert_equal(hello, new_hello)
550
+
551
+ # It updates new attributes in-place.
552
+ assert_equal("attribute", hello.additional)
553
+
554
+ # It removes unsaved attributes, but at least lets you know about them.
555
+ e = assert_raises(NoMethodError) { hello.unsaved }
556
+ assert_match("The 'unsaved' attribute was set in the past", e.message)
557
+ end
558
+
559
+ should "instantiate a new object of the appropriate class when it is different than the host class" do
560
+ stub_request(:post, "#{Stripe.api_base}/v1/hellos/hi_123/say")
561
+ .to_return(body: JSON.generate("object" => "goodbye", "additional" => "attribute"))
562
+
563
+ hello = HelloTestAPIResource.new(id: "hi_123")
564
+ hello.unsaved = "a value"
565
+ new_goodbye = hello.say_hello
566
+
567
+ # The returned value and the instance are different objects.
568
+ refute_equal(new_goodbye, hello)
569
+
570
+ # The returned value has stuff from the server.
571
+ assert_equal("attribute", new_goodbye.additional)
572
+ assert_equal("goodbye", new_goodbye.object)
573
+
574
+ # You instance doesn't have stuff from the server.
575
+ e = assert_raises(NoMethodError) { hello.additional }
576
+ refute_match(/was set in the past/, e.message)
577
+
578
+ # The instance preserves unset attributes on the original instance (not sure this is good behavior?)
579
+ assert_equal("a value", hello.unsaved)
580
+ end
581
+ end
582
+
493
583
  @@fixtures = {} # rubocop:disable Style/ClassVars
494
584
  setup do
495
585
  if @@fixtures.empty?
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require ::File.expand_path("../test_helper", __dir__)
4
+
5
+ module Stripe
6
+ class BalanceTransactionTest < Test::Unit::TestCase
7
+ should "be listable" do
8
+ balance_transactions = Stripe::BalanceTransaction.list
9
+ assert_requested :get, "#{Stripe.api_base}/v1/balance_transactions"
10
+ assert balance_transactions.data.is_a?(Array)
11
+ assert balance_transactions.first.is_a?(Stripe::BalanceTransaction)
12
+ end
13
+
14
+ should "be retrievable" do
15
+ balance_transaction = Stripe::BalanceTransaction.retrieve("txn_123")
16
+ assert_requested :get, "#{Stripe.api_base}/v1/balance_transactions/txn_123"
17
+ assert balance_transaction.is_a?(Stripe::BalanceTransaction)
18
+ end
19
+ end
20
+ end
@@ -60,21 +60,5 @@ module Stripe
60
60
  assert charge.is_a?(Stripe::Charge)
61
61
  end
62
62
  end
63
-
64
- context "#mark_as_fraudulent" do
65
- should "charges should be able to be marked as fraudulent" do
66
- charge = Stripe::Charge.retrieve("ch_123")
67
- charge = charge.mark_as_fraudulent
68
- assert charge.is_a?(Stripe::Charge)
69
- end
70
- end
71
-
72
- context "#mark_as_safe" do
73
- should "charges should be able to be marked as safe" do
74
- charge = Stripe::Charge.retrieve("ch_123")
75
- charge = charge.mark_as_safe
76
- assert charge.is_a?(Stripe::Charge)
77
- end
78
- end
79
63
  end
80
64
  end