stripe 3.0.3 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +23 -0
  3. data/Gemfile +1 -2
  4. data/History.txt +4 -0
  5. data/README.md +28 -3
  6. data/Rakefile +0 -12
  7. data/VERSION +1 -1
  8. data/lib/stripe.rb +24 -0
  9. data/lib/stripe/stripe_client.rb +166 -19
  10. data/lib/stripe/util.rb +127 -8
  11. data/lib/stripe/version.rb +1 -1
  12. data/test/api_stub_helpers.rb +0 -125
  13. data/test/stripe/account_test.rb +11 -13
  14. data/test/stripe/alipay_account_test.rb +2 -4
  15. data/test/stripe/api_resource_test.rb +112 -76
  16. data/test/stripe/apple_pay_domain_test.rb +4 -6
  17. data/test/stripe/application_fee_refund_test.rb +2 -5
  18. data/test/stripe/application_fee_test.rb +0 -2
  19. data/test/stripe/bank_account_test.rb +8 -13
  20. data/test/stripe/bitcoin_receiver_test.rb +13 -16
  21. data/test/stripe/bitcoin_transaction_test.rb +2 -4
  22. data/test/stripe/charge_test.rb +9 -11
  23. data/test/stripe/country_spec_test.rb +2 -4
  24. data/test/stripe/coupon_test.rb +6 -8
  25. data/test/stripe/customer_card_test.rb +13 -9
  26. data/test/stripe/customer_test.rb +16 -18
  27. data/test/stripe/dispute_test.rb +8 -10
  28. data/test/stripe/file_upload_test.rb +3 -3
  29. data/test/stripe/invoice_item_test.rb +9 -11
  30. data/test/stripe/invoice_line_item_test.rb +0 -1
  31. data/test/stripe/invoice_test.rb +39 -21
  32. data/test/stripe/list_object_test.rb +1 -1
  33. data/test/stripe/login_link_test.rb +6 -7
  34. data/test/stripe/order_return_test.rb +2 -4
  35. data/test/stripe/order_test.rb +10 -12
  36. data/test/stripe/payout_test.rb +7 -9
  37. data/test/stripe/plan_test.rb +8 -10
  38. data/test/stripe/product_test.rb +8 -10
  39. data/test/stripe/recipient_card_test.rb +13 -9
  40. data/test/stripe/recipient_test.rb +8 -10
  41. data/test/stripe/refund_test.rb +7 -9
  42. data/test/stripe/reversal_test.rb +5 -7
  43. data/test/stripe/sku_test.rb +9 -11
  44. data/test/stripe/source_test.rb +16 -15
  45. data/test/stripe/stripe_client_test.rb +190 -26
  46. data/test/stripe/subscription_item_test.rb +12 -14
  47. data/test/stripe/subscription_test.rb +10 -12
  48. data/test/stripe/three_d_secure_test.rb +3 -5
  49. data/test/stripe/transfer_test.rb +7 -9
  50. data/test/stripe/util_test.rb +164 -0
  51. data/test/test_helper.rb +33 -18
  52. metadata +2 -8
  53. data/openapi/fixtures.json +0 -1896
  54. data/openapi/fixtures.yaml +0 -1505
  55. data/openapi/spec2.json +0 -24601
  56. data/openapi/spec2.yaml +0 -18801
  57. data/test/api_fixtures.rb +0 -29
@@ -89,6 +89,20 @@ module Stripe
89
89
  end
90
90
  end
91
91
 
92
+ def self.log_info(message, data = {})
93
+ if Stripe.log_level == Stripe::LEVEL_DEBUG ||Stripe.log_level == Stripe::LEVEL_INFO
94
+ log_internal(message, data, color: :cyan,
95
+ level: Stripe::LEVEL_INFO, out: $stdout)
96
+ end
97
+ end
98
+
99
+ def self.log_debug(message, data = {})
100
+ if Stripe.log_level == Stripe::LEVEL_DEBUG
101
+ log_internal(message, data, color: :blue,
102
+ level: Stripe::LEVEL_DEBUG, out: $stdout)
103
+ end
104
+ end
105
+
92
106
  def self.file_readable(file)
93
107
  # This is nominally equivalent to File.readable?, but that can
94
108
  # report incorrect results on some more oddball filesystems
@@ -220,8 +234,59 @@ module Stripe
220
234
  key
221
235
  end
222
236
 
237
+ # Normalizes header keys so that they're all lower case and each
238
+ # hyphen-delimited section starts with a single capitalized letter. For
239
+ # example, `request-id` becomes `Request-Id`. This is useful for extracting
240
+ # certain key values when the user could have set them with a variety of
241
+ # diffent naming schemes.
242
+ def self.normalize_headers(headers)
243
+ headers.inject({}) do |new_headers, (k, v)|
244
+ if k.is_a?(Symbol)
245
+ k = titlecase_parts(k.to_s.gsub("_", "-"))
246
+ elsif k.is_a?(String)
247
+ k = titlecase_parts(k)
248
+ end
249
+
250
+ new_headers[k] = v
251
+ new_headers
252
+ end
253
+ end
254
+
255
+ # Generates a Dashboard link to inspect a request ID based off of a request
256
+ # ID value and an API key, which is used to attempt to extract whether the
257
+ # environment is livemode or testmode.
258
+ def self.request_id_dashboard_url(request_id, api_key)
259
+ env = !api_key.nil? && api_key.start_with?("sk_live") ? "live" : "test"
260
+ "https://dashboard.stripe.com/#{env}/logs/#{request_id}"
261
+ end
262
+
263
+ # Constant time string comparison to prevent timing attacks
264
+ # Code borrowed from ActiveSupport
265
+ def self.secure_compare(a, b)
266
+ return false unless a.bytesize == b.bytesize
267
+
268
+ l = a.unpack "C#{a.bytesize}"
269
+
270
+ res = 0
271
+ b.each_byte { |byte| res |= byte ^ l.shift }
272
+ res == 0
273
+ end
274
+
223
275
  private
224
276
 
277
+ COLOR_CODES = {
278
+ :black => 0, :light_black => 60,
279
+ :red => 1, :light_red => 61,
280
+ :green => 2, :light_green => 62,
281
+ :yellow => 3, :light_yellow => 63,
282
+ :blue => 4, :light_blue => 64,
283
+ :magenta => 5, :light_magenta => 65,
284
+ :cyan => 6, :light_cyan => 66,
285
+ :white => 7, :light_white => 67,
286
+ :default => 9
287
+ }
288
+ private_constant :COLOR_CODES
289
+
225
290
  # We use a pretty janky version of form encoding (Rack's) that supports
226
291
  # more complex data structures like maps and arrays through the use of
227
292
  # specialized syntax. To encode an array of maps like:
@@ -258,17 +323,71 @@ module Stripe
258
323
  end
259
324
  end
260
325
  end
326
+ private_class_method :check_array_of_maps_start_keys!
261
327
 
262
- # Constant time string comparison to prevent timing attacks
263
- # Code borrowed from ActiveSupport
264
- def self.secure_compare(a, b)
265
- return false unless a.bytesize == b.bytesize
328
+ # Uses an ANSI escape code to colorize text if it's going to be sent to a
329
+ # TTY.
330
+ def self.colorize(val, color, isatty)
331
+ return val unless isatty
266
332
 
267
- l = a.unpack "C#{a.bytesize}"
333
+ mode = 0 # default
334
+ foreground = 30 + COLOR_CODES.fetch(color)
335
+ background = 40 + COLOR_CODES.fetch(:default)
268
336
 
269
- res = 0
270
- b.each_byte { |byte| res |= byte ^ l.shift }
271
- res == 0
337
+ "\033[#{mode};#{foreground};#{background}m#{val}\033[0m"
338
+ end
339
+ private_class_method :colorize
340
+
341
+ # TODO: Make these named required arguments when we drop support for Ruby
342
+ # 2.0.
343
+ def self.log_internal(message, data = {}, color: nil, level: nil, out: nil)
344
+ data_str = data.select { |k,v| !v.nil? }.
345
+ map { |(k,v)|
346
+ "%s=%s" % [
347
+ colorize(k, color, out.isatty),
348
+ wrap_logfmt_value(v)
349
+ ]
350
+ }.join(" ")
351
+
352
+ if out.isatty
353
+ out.puts "%s %s %s" %
354
+ [colorize(level[0, 4].upcase, color, out.isatty), message, data_str]
355
+ else
356
+ out.puts "message=%s level=%s %s" %
357
+ [wrap_logfmt_value(message), level, data_str]
358
+ end
359
+ end
360
+ private_class_method :log_internal
361
+
362
+ def self.titlecase_parts(s)
363
+ s.split("-").
364
+ select { |p| p != "" }.
365
+ map { |p| p[0].upcase + p[1..-1].downcase }.
366
+ join("-")
367
+ end
368
+ private_class_method :titlecase_parts
369
+
370
+ # Wraps a value in double quotes if it looks sufficiently complex so that
371
+ # it can be read by logfmt parsers.
372
+ def self.wrap_logfmt_value(val)
373
+ # If value is any kind of number, just allow it to be formatted directly
374
+ # to a string (this will handle integers or floats).
375
+ return val if val.is_a?(Numeric)
376
+
377
+ # Hopefully val is a string, but protect in case it's not.
378
+ val = val.to_s
379
+
380
+ if %r{[^\w\-/]} =~ val
381
+ # If the string contains any special characters, escape any double
382
+ # quotes it has, remove newlines, and wrap the whole thing in quotes.
383
+ %{"%s"} % val.gsub('"', '\"').gsub("\n", "")
384
+ else
385
+ # Otherwise use the basic value if it looks like a standard set of
386
+ # characters (and allow a few special characters like hyphens, and
387
+ # slashes)
388
+ val
389
+ end
272
390
  end
391
+ private_class_method :wrap_logfmt_value
273
392
  end
274
393
  end
@@ -1,3 +1,3 @@
1
1
  module Stripe
2
- VERSION = '3.0.3'
2
+ VERSION = '3.1.0'
3
3
  end
@@ -1,125 +0,0 @@
1
- require "json"
2
-
3
- # Provides a set of helpers for a test suite that help to mock out the Stripe
4
- # API.
5
- module APIStubHelpers
6
- protected
7
-
8
- # Uses Webmock to stub out the Stripe API for testing purposes. The stub will
9
- # by default respond on any routes that are defined in the bundled OpenAPI
10
- # spec with generated response data.
11
- #
12
- # An `override_app` can be specified to get finer grain control over how a
13
- # stubbed endpoint responds. It can be used to modify generated responses,
14
- # mock expectations, or even to override the default stub completely.
15
- def stub_api
16
- stub_request(:any, /^#{Stripe.api_base}/).to_rack(new_api_stub)
17
- end
18
-
19
- def stub_connect
20
- stub_request(:any, /^#{Stripe.connect_base}/).to_return(:body => "{}")
21
- end
22
-
23
- private
24
-
25
- # APIStubMiddleware intercepts a response generated by Committee's stubbing
26
- # middleware, and tries to replace it with a better version from a set of
27
- # sample fixtures data generated from Stripe's core API service.
28
- class APIStubMiddleware
29
- API_FIXTURES = APIFixtures.new
30
-
31
- def initialize(app)
32
- @app = app
33
- end
34
-
35
- def call(env)
36
- # We use a vendor specific prefix (`x-resourceId`) embedded in the schema
37
- # of any resource in our spec to identify it (e.g. "charge"). This allows
38
- # us to cross-reference that response with some data that we might find
39
- # in our fixtures file so that we can respond with a higher fidelity
40
- # response.
41
- schema = env["committee.response_schema"]
42
- resource_id = schema.data["x-resourceId"] || ""
43
-
44
- if data = API_FIXTURES[resource_id.to_sym]
45
- # standard top-level API resource
46
- data = fixturize_lists_recursively(schema, data)
47
- env["committee.response"] = data
48
- elsif schema.properties["object"].enum == ["list"]
49
- # top level list (like from a list endpoint)
50
- data = fixturize_list(schema, env["committee.response"])
51
- env["committee.response"] = data
52
- else
53
- raise "no fixture for: #{resource_id}"
54
- end
55
- @app.call(env)
56
- end
57
-
58
- private
59
-
60
- # If schema looks like a Stripe list object, then we look up the resource
61
- # that the list is supposed to include and inject it into `data` as a
62
- # fixture. Also calls into that other schema recursively so that sublists
63
- # within it will also be assigned a fixture.
64
- def fixturize_list(schema, data)
65
- object_schema = schema.properties["object"]
66
- if object_schema && object_schema.enum == ["list"]
67
- subschema = schema.properties["data"].items
68
- resource_id = subschema.data["x-resourceId"] || ""
69
- if subdata = API_FIXTURES[resource_id.to_sym]
70
- subdata = fixturize_lists_recursively(subschema, subdata)
71
-
72
- data = data ? data.dup : {}
73
- data[:data] = [subdata]
74
- end
75
- end
76
- data
77
- end
78
-
79
- # Examines each of the given schema's properties and calls #fixturize_list
80
- # on them so that any sublists will be populated with sample fixture data.
81
- def fixturize_lists_recursively(schema, data)
82
- data = data.dup
83
- schema.properties.each do |key, subschema|
84
- data[key.to_sym] = fixturize_list(subschema, data[key.to_sym])
85
- end
86
- data
87
- end
88
- end
89
-
90
- # A descendant of the standard `Sinatra::Base` that we can use to enrich
91
- # certain types of responses.
92
- class APIStubApp < Sinatra::Base
93
- not_found do
94
- "endpoint not found in API stub: #{request.request_method} #{request.path_info}"
95
- end
96
- end
97
-
98
- # Finds the latest OpenAPI specification in ROOT/openapi/ and parses it for
99
- # use with Committee.
100
- def self.initialize_spec
101
- spec_data = ::JSON.parse(File.read("#{PROJECT_ROOT}/openapi/spec2.json"))
102
-
103
- driver = Committee::Drivers::OpenAPI2.new
104
- driver.parse(spec_data)
105
- end
106
-
107
- # Creates a new Rack app with Committee middleware it.
108
- def new_api_stub
109
- Rack::Builder.new {
110
- use Committee::Middleware::RequestValidation, schema: @@spec,
111
- params_response: true, strict: true
112
- use Committee::Middleware::Stub, schema: @@spec,
113
- call: true
114
- use APIStubMiddleware
115
- run APIStubApp.new
116
- }
117
- end
118
-
119
- # Parse and initialize the OpenAPI spec only once for the entire test suite.
120
- @@spec = initialize_spec
121
-
122
- # The default override app. Doesn't respond on any route so generated
123
- # responses will always take precedence.
124
- @@default_override_app = Sinatra.new
125
- end
@@ -2,8 +2,6 @@ require File.expand_path('../../test_helper', __FILE__)
2
2
 
3
3
  module Stripe
4
4
  class AccountTest < Test::Unit::TestCase
5
- FIXTURE = API_FIXTURES.fetch(:account)
6
-
7
5
  should "be listable" do
8
6
  accounts = Stripe::Account.list
9
7
  assert_requested :get, "#{Stripe.api_base}/v1/accounts"
@@ -18,8 +16,8 @@ module Stripe
18
16
  end
19
17
 
20
18
  should "be retrievable using plural endpoint" do
21
- account = Stripe::Account.retrieve(FIXTURE[:id])
22
- assert_requested :get, "#{Stripe.api_base}/v1/accounts/#{FIXTURE[:id]}"
19
+ account = Stripe::Account.retrieve("acct_123")
20
+ assert_requested :get, "#{Stripe.api_base}/v1/accounts/acct_123"
23
21
  assert account.kind_of?(Stripe::Account)
24
22
  end
25
23
 
@@ -42,22 +40,22 @@ module Stripe
42
40
  end
43
41
 
44
42
  should "be saveable" do
45
- account = Stripe::Account.retrieve(FIXTURE[:id])
43
+ account = Stripe::Account.retrieve("acct_123")
46
44
  account.metadata['key'] = 'value'
47
45
  account.save
48
- assert_requested :post, "#{Stripe.api_base}/v1/accounts/#{FIXTURE[:id]}"
46
+ assert_requested :post, "#{Stripe.api_base}/v1/accounts/#{account.id}"
49
47
  end
50
48
 
51
49
  should "be updateable" do
52
- account = Stripe::Account.update(FIXTURE[:id], metadata: {foo: 'bar'})
53
- assert_requested :post, "#{Stripe.api_base}/v1/accounts/#{FIXTURE[:id]}"
50
+ account = Stripe::Account.update("acct_123", metadata: {foo: 'bar'})
51
+ assert_requested :post, "#{Stripe.api_base}/v1/accounts/acct_123"
54
52
  assert account.kind_of?(Stripe::Account)
55
53
  end
56
54
 
57
55
  should "be deletable" do
58
- account = Stripe::Account.retrieve(FIXTURE[:id])
56
+ account = Stripe::Account.retrieve("acct_123")
59
57
  account = account.delete
60
- assert_requested :delete, "#{Stripe.api_base}/v1/accounts/#{FIXTURE[:id]}"
58
+ assert_requested :delete, "#{Stripe.api_base}/v1/accounts/#{account.id}"
61
59
  assert account.kind_of?(Stripe::Account)
62
60
  end
63
61
 
@@ -66,7 +64,7 @@ module Stripe
66
64
  old_stderr = $stderr
67
65
  $stderr = StringIO.new
68
66
  begin
69
- account = Stripe::Account.retrieve(FIXTURE[:id])
67
+ account = Stripe::Account.retrieve("acct_123")
70
68
  account.bank_account = "tok_123"
71
69
  message = "NOTE: Stripe::Account#bank_account= is " +
72
70
  "deprecated; use #external_account= instead"
@@ -79,7 +77,7 @@ module Stripe
79
77
 
80
78
  context "#deauthorize" do
81
79
  should "deauthorize an account" do
82
- account = Stripe::Account.retrieve(FIXTURE[:id])
80
+ account = Stripe::Account.retrieve("acct_123")
83
81
 
84
82
  # Unfortunately, the OpenAPI spec doesn't yet cover anything under the
85
83
  # Connect endpoints, so for just stub this out with Webmock.
@@ -92,7 +90,7 @@ module Stripe
92
90
 
93
91
  context "#legal_entity=" do
94
92
  should 'disallow direct overrides' do
95
- account = Stripe::Account.retrieve(FIXTURE[:id])
93
+ account = Stripe::Account.retrieve("acct_123")
96
94
 
97
95
  assert_raise NoMethodError do
98
96
  account.legal_entity = {:first_name => 'Blah'}
@@ -2,17 +2,15 @@ require File.expand_path('../../test_helper', __FILE__)
2
2
 
3
3
  module Stripe
4
4
  class AlipayAccountTest < Test::Unit::TestCase
5
- FIXTURE = API_FIXTURES.fetch(:alipay_account)
6
-
7
5
  should "raise on #retrieve" do
8
6
  assert_raises NotImplementedError do
9
- Stripe::AlipayAccount.retrieve FIXTURE[:id]
7
+ Stripe::AlipayAccount.retrieve("aliacc_123")
10
8
  end
11
9
  end
12
10
 
13
11
  should "raise on #update" do
14
12
  assert_raises NotImplementedError do
15
- Stripe::AlipayAccount.update FIXTURE[:id], {}
13
+ Stripe::AlipayAccount.update("aliacc_123", {})
16
14
  end
17
15
  end
18
16
  end
@@ -38,14 +38,14 @@ module Stripe
38
38
  end
39
39
 
40
40
  should "setting an attribute should not cause a network request" do
41
- c = Stripe::Customer.new("test_customer");
41
+ c = Stripe::Customer.new("cus_123");
42
42
  c.card = {:id => "somecard", :object => "card"}
43
43
  assert_not_requested :get, %r{#{Stripe.api_base}/.*}
44
44
  assert_not_requested :post, %r{#{Stripe.api_base}/.*}
45
45
  end
46
46
 
47
47
  should "accessing id should not issue a fetch" do
48
- c = Stripe::Customer.new("test_customer")
48
+ c = Stripe::Customer.new("cus_123")
49
49
  c.id
50
50
  assert_not_requested :get, %r{#{Stripe.api_base}/.*}
51
51
  end
@@ -53,7 +53,7 @@ module Stripe
53
53
  should "not specifying api credentials should raise an exception" do
54
54
  Stripe.api_key = nil
55
55
  assert_raises Stripe::AuthenticationError do
56
- Stripe::Customer.new("test_customer").refresh
56
+ Stripe::Customer.new("cus_123").refresh
57
57
  end
58
58
  end
59
59
 
@@ -69,36 +69,36 @@ module Stripe
69
69
  should "specifying api credentials containing whitespace should raise an exception" do
70
70
  Stripe.api_key = "key "
71
71
  assert_raises Stripe::AuthenticationError do
72
- Stripe::Customer.new("test_customer").refresh
72
+ Stripe::Customer.new("cus_123").refresh
73
73
  end
74
74
  end
75
75
 
76
76
  should "send expand on fetch properly" do
77
- stub_request(:get, "#{Stripe.api_base}/v1/charges/ch_test_charge").
77
+ stub_request(:get, "#{Stripe.api_base}/v1/charges/ch_123").
78
78
  with(query: { "expand" => ["customer"] }).
79
- to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
79
+ to_return(body: JSON.generate(charge_fixture))
80
80
 
81
- Stripe::Charge.retrieve({:id => 'ch_test_charge', :expand => [:customer]})
81
+ Stripe::Charge.retrieve({:id => 'ch_123', :expand => [:customer]})
82
82
  end
83
83
 
84
84
  should "preserve expand across refreshes" do
85
- stub_request(:get, "#{Stripe.api_base}/v1/charges/ch_test_charge").
85
+ stub_request(:get, "#{Stripe.api_base}/v1/charges/ch_123").
86
86
  with(query: { "expand" => ["customer"] }).
87
- to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
87
+ to_return(body: JSON.generate(charge_fixture))
88
88
 
89
- ch = Stripe::Charge.retrieve({:id => 'ch_test_charge', :expand => :customer})
89
+ ch = Stripe::Charge.retrieve({:id => 'ch_123', :expand => :customer})
90
90
  ch.refresh
91
91
  end
92
92
 
93
93
  should "send expand when fetching through ListObject" do
94
- stub_request(:get, "#{Stripe.api_base}/v1/customers/c_test_customer").
95
- to_return(body: JSON.generate(API_FIXTURES.fetch(:customer)))
94
+ stub_request(:get, "#{Stripe.api_base}/v1/customers/cus_123").
95
+ to_return(body: JSON.generate(customer_fixture))
96
96
 
97
- stub_request(:get, "#{Stripe.api_base}/v1/customers/c_test_customer/sources/cc_test_card").
97
+ stub_request(:get, "#{Stripe.api_base}/v1/customers/cus_123/sources/cc_test_card").
98
98
  with(query: { "expand" => ["customer"] }).
99
- to_return(body: JSON.generate(API_FIXTURES.fetch(:customer)))
99
+ to_return(body: JSON.generate(customer_fixture))
100
100
 
101
- customer = Stripe::Customer.retrieve('c_test_customer')
101
+ customer = Stripe::Customer.retrieve('cus_123')
102
102
  customer.sources.retrieve({:id => 'cc_test_card', :expand => :customer})
103
103
  end
104
104
 
@@ -107,7 +107,7 @@ module Stripe
107
107
  should "use the per-object credential when creating" do
108
108
  stub_request(:post, "#{Stripe.api_base}/v1/charges").
109
109
  with(headers: {"Authorization" => "Bearer sk_test_local"}).
110
- to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
110
+ to_return(body: JSON.generate(charge_fixture))
111
111
 
112
112
  Stripe::Charge.create({:source => 'tok_visa'},
113
113
  'sk_test_local')
@@ -125,22 +125,22 @@ module Stripe
125
125
 
126
126
  should "use the per-object credential when creating" do
127
127
  stub_request(:post, "#{Stripe.api_base}/v1/charges").
128
- with(headers: {"Authorization" => "Bearer local"}).
129
- to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
128
+ with(headers: {"Authorization" => "Bearer sk_test_local"}).
129
+ to_return(body: JSON.generate(charge_fixture))
130
130
 
131
131
  Stripe::Charge.create({:source => 'tok_visa'},
132
- 'local')
132
+ 'sk_test_local')
133
133
  end
134
134
 
135
135
  should "use the per-object credential when retrieving and making other calls" do
136
- stub_request(:get, "#{Stripe.api_base}/v1/charges/ch_test_charge").
137
- with(headers: {"Authorization" => "Bearer local"}).
138
- to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
139
- stub_request(:post, "#{Stripe.api_base}/v1/charges/ch_test_charge/refunds").
140
- with(headers: {"Authorization" => "Bearer local"}).
141
- to_return(body: JSON.generate(API_FIXTURES.fetch(:refund)))
142
-
143
- ch = Stripe::Charge.retrieve('ch_test_charge', 'local')
136
+ stub_request(:get, "#{Stripe.api_base}/v1/charges/ch_123").
137
+ with(headers: {"Authorization" => "Bearer sk_test_local"}).
138
+ to_return(body: JSON.generate(charge_fixture))
139
+ stub_request(:post, "#{Stripe.api_base}/v1/charges/ch_123/refunds").
140
+ with(headers: {"Authorization" => "Bearer sk_test_local"}).
141
+ to_return(body: "{}")
142
+
143
+ ch = Stripe::Charge.retrieve('ch_123', 'sk_test_local')
144
144
  ch.refunds.create
145
145
  end
146
146
  end
@@ -151,45 +151,45 @@ module Stripe
151
151
  stub_request(:get, "#{Stripe.api_base}/v1/charges").
152
152
  with(query: { customer: "test customer" }).
153
153
  to_return(body: JSON.generate({
154
- data: [API_FIXTURES.fetch(:charge)]
154
+ data: [charge_fixture]
155
155
  }))
156
156
  charges = Stripe::Charge.list(:customer => 'test customer').data
157
157
  assert charges.kind_of? Array
158
158
  end
159
159
 
160
160
  should "construct URL properly with base query parameters" do
161
- stub_request(:get, "#{Stripe.api_base}/v1/invoices").
162
- with(query: { customer: "test_customer" }).
161
+ stub_request(:get, "#{Stripe.api_base}/v1/charges").
162
+ with(query: { customer: "cus_123" }).
163
163
  to_return(body: JSON.generate({
164
- data: [API_FIXTURES.fetch(:invoice)],
165
- url: "/v1/invoices"
164
+ data: [charge_fixture],
165
+ url: "/v1/charges"
166
166
  }))
167
- invoices = Stripe::Invoice.list(:customer => 'test_customer')
167
+ charges = Stripe::Invoice.list(:customer => 'cus_123')
168
168
 
169
- stub_request(:get, "#{Stripe.api_base}/v1/invoices").
170
- with(query: { customer: "test_customer", paid: "true" }).
169
+ stub_request(:get, "#{Stripe.api_base}/v1/charges").
170
+ with(query: { customer: "cus_123", created: "123" }).
171
171
  to_return(body: JSON.generate({
172
- data: [API_FIXTURES.fetch(:invoice)],
173
- url: "/v1/invoices"
172
+ data: [charge_fixture],
173
+ url: "/v1/charges"
174
174
  }))
175
- invoices.list(:paid => true)
175
+ charges.list(:created => 123)
176
176
  end
177
177
 
178
178
  should "setting a nil value for a param should exclude that param from the request" do
179
179
  stub_request(:get, "#{Stripe.api_base}/v1/charges").
180
180
  with(query: { offset: 5, sad: false }).
181
- to_return(body: JSON.generate({ :count => 1, :data => [API_FIXTURES.fetch(:charge)] }))
181
+ to_return(body: JSON.generate({ :count => 1, :data => [charge_fixture] }))
182
182
  Stripe::Charge.list(:count => nil, :offset => 5, :sad => false)
183
183
 
184
184
  stub_request(:post, "#{Stripe.api_base}/v1/charges").
185
185
  with(body: { 'amount' => '50', 'currency' => 'usd' }).
186
- to_return(body: JSON.generate({ :count => 1, :data => [API_FIXTURES.fetch(:charge)] }))
186
+ to_return(body: JSON.generate({ :count => 1, :data => [charge_fixture] }))
187
187
  Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
188
188
  end
189
189
 
190
190
  should "not trigger a warning if a known opt, such as idempotency_key, is in opts" do
191
191
  stub_request(:post, "#{Stripe.api_base}/v1/charges").
192
- to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
192
+ to_return(body: JSON.generate(charge_fixture))
193
193
  old_stderr = $stderr
194
194
  $stderr = StringIO.new
195
195
  begin
@@ -202,7 +202,7 @@ module Stripe
202
202
 
203
203
  should "trigger a warning if a known opt, such as idempotency_key, is in params" do
204
204
  stub_request(:post, "#{Stripe.api_base}/v1/charges").
205
- to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
205
+ to_return(body: JSON.generate(charge_fixture))
206
206
  old_stderr = $stderr
207
207
  $stderr = StringIO.new
208
208
  begin
@@ -228,28 +228,28 @@ module Stripe
228
228
  should "making a GET request with parameters should have a query string and no body" do
229
229
  stub_request(:get, "#{Stripe.api_base}/v1/charges").
230
230
  with(query: { limit: 1 }).
231
- to_return(body: JSON.generate({ :data => [API_FIXTURES.fetch(:charge)] }))
231
+ to_return(body: JSON.generate({ :data => [charge_fixture] }))
232
232
  Stripe::Charge.list({ :limit => 1 })
233
233
  end
234
234
 
235
235
  should "making a POST request with parameters should have a body and no query string" do
236
236
  stub_request(:post, "#{Stripe.api_base}/v1/charges").
237
237
  with(body: {'amount' => '100', 'currency' => 'usd', 'card' => 'sc_token'}).
238
- to_return(body: JSON.generate(API_FIXTURES.fetch(:charge)))
238
+ to_return(body: JSON.generate(charge_fixture))
239
239
  Stripe::Charge.create({ :amount => 100, :currency => 'usd', :card => 'sc_token' })
240
240
  end
241
241
 
242
242
  should "loading an object should issue a GET request" do
243
- stub_request(:get, "#{Stripe.api_base}/v1/customers/test_customer").
244
- to_return(body: JSON.generate(API_FIXTURES.fetch(:customer)))
245
- c = Stripe::Customer.new("test_customer")
243
+ stub_request(:get, "#{Stripe.api_base}/v1/charges/ch_123").
244
+ to_return(body: JSON.generate(charge_fixture))
245
+ c = Stripe::Charge.new("ch_123")
246
246
  c.refresh
247
247
  end
248
248
 
249
249
  should "using array accessors should be the same as the method interface" do
250
- stub_request(:get, "#{Stripe.api_base}/v1/customers/test_customer").
251
- to_return(body: JSON.generate(API_FIXTURES.fetch(:customer)))
252
- c = Stripe::Customer.new("test_customer")
250
+ stub_request(:get, "#{Stripe.api_base}/v1/charges/ch_123").
251
+ to_return(body: JSON.generate(charge_fixture))
252
+ c = Stripe::Charge.new("cus_123")
253
253
  c.refresh
254
254
  assert_equal c.created, c[:created]
255
255
  assert_equal c.created, c['created']
@@ -259,26 +259,26 @@ module Stripe
259
259
 
260
260
  should "accessing a property other than id or parent on an unfetched object should fetch it" do
261
261
  stub_request(:get, "#{Stripe.api_base}/v1/charges").
262
- with(query: { customer: "test_customer" }).
263
- to_return(body: JSON.generate(API_FIXTURES.fetch(:customer)))
264
- c = Stripe::Customer.new("test_customer")
262
+ with(query: { customer: "cus_123" }).
263
+ to_return(body: JSON.generate(customer_fixture))
264
+ c = Stripe::Customer.new("cus_123")
265
265
  c.charges
266
266
  end
267
267
 
268
268
  should "updating an object should issue a POST request with only the changed properties" do
269
- stub_request(:post, "#{Stripe.api_base}/v1/customers/c_test_customer").
269
+ stub_request(:post, "#{Stripe.api_base}/v1/customers/cus_123").
270
270
  with(body: { 'description' => 'another_mn' }).
271
- to_return(body: JSON.generate(API_FIXTURES.fetch(:customer)))
272
- c = Stripe::Customer.construct_from(API_FIXTURES.fetch(:customer))
271
+ to_return(body: JSON.generate(customer_fixture))
272
+ c = Stripe::Customer.construct_from(customer_fixture)
273
273
  c.description = "another_mn"
274
274
  c.save
275
275
  end
276
276
 
277
277
  should "updating should merge in returned properties" do
278
- stub_request(:post, "#{Stripe.api_base}/v1/customers/c_test_customer").
278
+ stub_request(:post, "#{Stripe.api_base}/v1/customers/cus_123").
279
279
  with(body: { 'description' => 'another_mn' }).
280
- to_return(body: JSON.generate(API_FIXTURES.fetch(:customer)))
281
- c = Stripe::Customer.new("c_test_customer")
280
+ to_return(body: JSON.generate(customer_fixture))
281
+ c = Stripe::Customer.new("cus_123")
282
282
  c.description = "another_mn"
283
283
  c.save
284
284
  assert_equal false, c.livemode
@@ -294,23 +294,23 @@ module Stripe
294
294
  should "updating should use the supplied api_key" do
295
295
  stub_request(:post, "#{Stripe.api_base}/v1/customers").
296
296
  with(headers: {"Authorization" => "Bearer sk_test_local"}).
297
- to_return(body: JSON.generate(API_FIXTURES.fetch(:customer)))
297
+ to_return(body: JSON.generate(customer_fixture))
298
298
  c = Stripe::Customer.new
299
299
  c.save({}, { :api_key => 'sk_test_local' })
300
300
  assert_equal false, c.livemode
301
301
  end
302
302
 
303
303
  should "deleting should send no props and result in an object that has no props other deleted" do
304
- stub_request(:delete, "#{Stripe.api_base}/v1/customers/c_test_customer").
305
- to_return(body: JSON.generate({ "id" => "test_customer", "deleted" => true }))
306
- c = Stripe::Customer.construct_from(API_FIXTURES.fetch(:customer))
304
+ stub_request(:delete, "#{Stripe.api_base}/v1/customers/cus_123").
305
+ to_return(body: JSON.generate({ "id" => "cus_123", "deleted" => true }))
306
+ c = Stripe::Customer.construct_from(customer_fixture)
307
307
  c.delete
308
308
  end
309
309
 
310
310
  should "loading all of an APIResource should return an array of recursively instantiated objects" do
311
311
  stub_request(:get, "#{Stripe.api_base}/v1/charges").
312
312
  to_return(body: JSON.generate({
313
- data: [API_FIXTURES.fetch(:charge)]
313
+ data: [charge_fixture]
314
314
  }))
315
315
  charges = Stripe::Charge.list.data
316
316
  assert charges.kind_of? Array
@@ -319,21 +319,21 @@ module Stripe
319
319
  end
320
320
 
321
321
  should "passing in a stripe_account header should pass it through on call" do
322
- stub_request(:get, "#{Stripe.api_base}/v1/customers/c_test_customer").
323
- with(headers: {"Stripe-Account" => "acct_abc"}).
324
- to_return(body: JSON.generate(API_FIXTURES.fetch(:customer)))
325
- Stripe::Customer.retrieve("c_test_customer", {:stripe_account => 'acct_abc'})
322
+ stub_request(:get, "#{Stripe.api_base}/v1/customers/cus_123").
323
+ with(headers: {"Stripe-Account" => "acct_123"}).
324
+ to_return(body: JSON.generate(customer_fixture))
325
+ Stripe::Customer.retrieve("cus_123", {:stripe_account => 'acct_123'})
326
326
  end
327
327
 
328
328
  should "passing in a stripe_account header should pass it through on save" do
329
- stub_request(:get, "#{Stripe.api_base}/v1/customers/c_test_customer").
330
- with(headers: {"Stripe-Account" => "acct_abc"}).
331
- to_return(body: JSON.generate(API_FIXTURES.fetch(:customer)))
332
- c = Stripe::Customer.retrieve("c_test_customer", {:stripe_account => 'acct_abc'})
333
-
334
- stub_request(:post, "#{Stripe.api_base}/v1/customers/c_test_customer").
335
- with(headers: {"Stripe-Account" => "acct_abc"}).
336
- to_return(body: JSON.generate(API_FIXTURES.fetch(:customer)))
329
+ stub_request(:get, "#{Stripe.api_base}/v1/customers/cus_123").
330
+ with(headers: {"Stripe-Account" => "acct_123"}).
331
+ to_return(body: JSON.generate(customer_fixture))
332
+ c = Stripe::Customer.retrieve("cus_123", {:stripe_account => 'acct_123'})
333
+
334
+ stub_request(:post, "#{Stripe.api_base}/v1/customers/cus_123").
335
+ with(headers: {"Stripe-Account" => "acct_123"}).
336
+ to_return(body: JSON.generate(customer_fixture))
337
337
  c.description = 'FOO'
338
338
  c.save
339
339
  end
@@ -518,5 +518,41 @@ module Stripe
518
518
  account.save(:display_name => 'stripe', :metadata => {:key => 'value' })
519
519
  end
520
520
  end
521
+
522
+ @@fixtures = {}
523
+ setup do
524
+ if @@fixtures.empty?
525
+ set_fixture(:charge) do
526
+ Charge.retrieve("ch_123")
527
+ end
528
+ set_fixture(:customer) do
529
+ Customer.retrieve("cus_123")
530
+ end
531
+ end
532
+ end
533
+
534
+ private
535
+
536
+ def charge_fixture
537
+ @@fixtures[:charge]
538
+ end
539
+
540
+ def customer_fixture
541
+ @@fixtures[:customer]
542
+ end
543
+
544
+ def get_fixture(key)
545
+ @@fixtures.fetch(key)
546
+ end
547
+
548
+ # Expects to retrieve a fixture from stripe-mock (an API call should be
549
+ # included in the block to yield to) and does very simple memoization.
550
+ def set_fixture(key)
551
+ return @@fixtures[key] if @@fixtures.key?(key)
552
+
553
+ obj = yield
554
+ @@fixtures[key] = obj.instance_variable_get(:@values).freeze
555
+ @@fixtures[key]
556
+ end
521
557
  end
522
558
  end