stripe 3.0.3 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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