stripe 1.8.7 → 1.8.8

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  /stripe-*.gem
2
2
  /Gemfile.lock
3
3
  .rvmrc
4
+ Gemfile.lock
@@ -1,3 +1,7 @@
1
+ === 1.8.8 2013-10-3
2
+
3
+ * Add support for metadata API
4
+
1
5
  === 1.8.7 2013-08-18
2
6
 
3
7
  * Add support for closing disputes.
data/Rakefile CHANGED
@@ -1,9 +1,15 @@
1
- task :default => [:test]
1
+ task :default => [:all]
2
2
 
3
3
  task :test do
4
4
  ret = true
5
- Dir["test/**/*.rb"].each do |f|
5
+ Dir["test/**/*_test.rb"].each do |f|
6
6
  ret = ret && ruby(f, '')
7
7
  end
8
- exit(ret)
9
8
  end
9
+
10
+ task :all do
11
+ Rake::Task["test"].invoke
12
+ require 'active_support/all'
13
+ Rake::Task["test"].reenable
14
+ Rake::Task["test"].invoke
15
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.8.7
1
+ 1.8.8
@@ -2,16 +2,56 @@ module Stripe
2
2
  module APIOperations
3
3
  module Update
4
4
  def save
5
- if @unsaved_values.length > 0
6
- values = @unsaved_values.reduce({}) do |h, k|
7
- h.update(k => @values[k].nil? ? '' : @values[k])
8
- end
5
+ values = serialize_params(self)
6
+
7
+ if @values[:metadata]
8
+ values[:metadata] = serialize_metadata
9
+ end
10
+
11
+ if values.length > 0
9
12
  values.delete(:id)
13
+
10
14
  response, api_key = Stripe.request(:post, url, @api_key, values)
11
15
  refresh_from(response, api_key)
12
16
  end
13
17
  self
14
18
  end
19
+
20
+ def serialize_metadata
21
+ if @unsaved_values.include?(:metadata)
22
+ # the metadata object has been reassigned
23
+ # i.e. as object.metadata = {key => val}
24
+ metadata_update = @values[:metadata] # new hash
25
+ new_keys = metadata_update.keys.map(&:to_sym)
26
+ # remove keys at the server, but not known locally
27
+ keys_to_unset = @previous_metadata.keys - new_keys
28
+ keys_to_unset.each {|key| metadata_update[key] = ''}
29
+
30
+ metadata_update
31
+ else
32
+ # metadata is a StripeObject, and can be serialized normally
33
+ serialize_params(@values[:metadata])
34
+ end
35
+ end
36
+
37
+ def serialize_params(obj)
38
+ case obj
39
+ when nil
40
+ ''
41
+ when StripeObject
42
+ unsaved_keys = obj.instance_variable_get(:@unsaved_values)
43
+ obj_values = obj.instance_variable_get(:@values)
44
+ update_hash = {}
45
+
46
+ unsaved_keys.each do |k|
47
+ update_hash[k] = serialize_params(obj_values[k])
48
+ end
49
+
50
+ update_hash
51
+ else
52
+ obj
53
+ end
54
+ end
15
55
  end
16
56
  end
17
57
  end
@@ -47,6 +47,7 @@ module Stripe
47
47
  def refresh_from(values, api_key, partial=false)
48
48
  @api_key = api_key
49
49
 
50
+ @previous_metadata = values[:metadata]
50
51
  removed = partial ? Set.new : Set.new(@values.keys - values.keys)
51
52
  added = Set.new(values.keys - @values.keys)
52
53
  # Wipe old state before setting new. This is useful for e.g. updating a
@@ -2,5 +2,6 @@ module Stripe
2
2
  class Transfer < APIResource
3
3
  include Stripe::APIOperations::List
4
4
  include Stripe::APIOperations::Create
5
+ include Stripe::APIOperations::Update
5
6
  end
6
7
  end
@@ -1,3 +1,3 @@
1
1
  module Stripe
2
- VERSION = '1.8.7'
2
+ VERSION = '1.8.8'
3
3
  end
@@ -0,0 +1,14 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Stripe
4
+ class AccountTest < Test::Unit::TestCase
5
+ should "account should be retrievable" do
6
+ resp = {:email => "test+bindings@stripe.com", :charge_enabled => false, :details_submitted => false}
7
+ @mock.expects(:get).once.returns(test_response(resp))
8
+ a = Stripe::Account.retrieve
9
+ assert_equal "test+bindings@stripe.com", a.email
10
+ assert !a.charge_enabled
11
+ assert !a.details_submitted
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,345 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path('../../test_helper', __FILE__)
3
+
4
+ module Stripe
5
+ class ApiResourceTest < Test::Unit::TestCase
6
+ should "creating a new APIResource should not fetch over the network" do
7
+ @mock.expects(:get).never
8
+ c = Stripe::Customer.new("someid")
9
+ end
10
+
11
+ should "creating a new APIResource from a hash should not fetch over the network" do
12
+ @mock.expects(:get).never
13
+ c = Stripe::Customer.construct_from({
14
+ :id => "somecustomer",
15
+ :card => {:id => "somecard", :object => "card"},
16
+ :object => "customer"
17
+ })
18
+ end
19
+
20
+ should "setting an attribute should not cause a network request" do
21
+ @mock.expects(:get).never
22
+ @mock.expects(:post).never
23
+ c = Stripe::Customer.new("test_customer");
24
+ c.card = {:id => "somecard", :object => "card"}
25
+ end
26
+
27
+ should "accessing id should not issue a fetch" do
28
+ @mock.expects(:get).never
29
+ c = Stripe::Customer.new("test_customer");
30
+ c.id
31
+ end
32
+
33
+ should "not specifying api credentials should raise an exception" do
34
+ Stripe.api_key = nil
35
+ assert_raises Stripe::AuthenticationError do
36
+ Stripe::Customer.new("test_customer").refresh
37
+ end
38
+ end
39
+
40
+ should "specifying api credentials containing whitespace should raise an exception" do
41
+ Stripe.api_key = "key "
42
+ assert_raises Stripe::AuthenticationError do
43
+ Stripe::Customer.new("test_customer").refresh
44
+ end
45
+ end
46
+
47
+ should "specifying invalid api credentials should raise an exception" do
48
+ Stripe.api_key = "invalid"
49
+ response = test_response(test_invalid_api_key_error, 401)
50
+ assert_raises Stripe::AuthenticationError do
51
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
52
+ Stripe::Customer.retrieve("failing_customer")
53
+ end
54
+ end
55
+
56
+ should "AuthenticationErrors should have an http status, http body, and JSON body" do
57
+ Stripe.api_key = "invalid"
58
+ response = test_response(test_invalid_api_key_error, 401)
59
+ begin
60
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
61
+ Stripe::Customer.retrieve("failing_customer")
62
+ rescue Stripe::AuthenticationError => e
63
+ assert_equal(401, e.http_status)
64
+ assert_equal(true, !!e.http_body)
65
+ assert_equal(true, !!e.json_body[:error][:message])
66
+ assert_equal(test_invalid_api_key_error['error']['message'], e.json_body[:error][:message])
67
+ end
68
+ end
69
+
70
+ context "when specifying per-object credentials" do
71
+ context "with no global API key set" do
72
+ should "use the per-object credential when creating" do
73
+ Stripe.expects(:execute_request).with do |opts|
74
+ opts[:headers][:authorization] == 'Bearer sk_test_local'
75
+ end.returns(test_response(test_charge))
76
+
77
+ Stripe::Charge.create({:card => {:number => '4242424242424242'}},
78
+ 'sk_test_local')
79
+ end
80
+ end
81
+
82
+ context "with a global API key set" do
83
+ setup do
84
+ Stripe.api_key = "global"
85
+ end
86
+
87
+ teardown do
88
+ Stripe.api_key = nil
89
+ end
90
+
91
+ should "use the per-object credential when creating" do
92
+ Stripe.expects(:execute_request).with do |opts|
93
+ opts[:headers][:authorization] == 'Bearer local'
94
+ end.returns(test_response(test_charge))
95
+
96
+ Stripe::Charge.create({:card => {:number => '4242424242424242'}},
97
+ 'local')
98
+ end
99
+
100
+ should "use the per-object credential when retrieving and making other calls" do
101
+ Stripe.expects(:execute_request).with do |opts|
102
+ opts[:url] == "#{Stripe.api_base}/v1/charges/ch_test_charge" &&
103
+ opts[:headers][:authorization] == 'Bearer local'
104
+ end.returns(test_response(test_charge))
105
+ Stripe.expects(:execute_request).with do |opts|
106
+ opts[:url] == "#{Stripe.api_base}/v1/charges/ch_test_charge/refund" &&
107
+ opts[:headers][:authorization] == 'Bearer local'
108
+ end.returns(test_response(test_charge))
109
+
110
+ ch = Stripe::Charge.retrieve('ch_test_charge', 'local')
111
+ ch.refund
112
+ end
113
+ end
114
+ end
115
+
116
+ context "with valid credentials" do
117
+ should "urlencode values in GET params" do
118
+ response = test_response(test_charge_array)
119
+ @mock.expects(:get).with("#{Stripe.api_base}/v1/charges?customer=test%20customer", nil, nil).returns(response)
120
+ charges = Stripe::Charge.all(:customer => 'test customer').data
121
+ assert charges.kind_of? Array
122
+ end
123
+
124
+ should "construct URL properly with base query parameters" do
125
+ response = test_response(test_invoice_customer_array)
126
+ @mock.expects(:get).with("#{Stripe.api_base}/v1/invoices?customer=test_customer", nil, nil).returns(response)
127
+ invoices = Stripe::Invoice.all(:customer => 'test_customer')
128
+
129
+ @mock.expects(:get).with("#{Stripe.api_base}/v1/invoices?customer=test_customer&paid=true", nil, nil).returns(response)
130
+ invoices.all(:paid => true)
131
+ end
132
+
133
+ should "a 400 should give an InvalidRequestError with http status, body, and JSON body" do
134
+ response = test_response(test_missing_id_error, 400)
135
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
136
+ begin
137
+ Stripe::Customer.retrieve("foo")
138
+ rescue Stripe::InvalidRequestError => e
139
+ assert_equal(400, e.http_status)
140
+ assert_equal(true, !!e.http_body)
141
+ assert_equal(true, e.json_body.kind_of?(Hash))
142
+ end
143
+ end
144
+
145
+ should "a 401 should give an AuthenticationError with http status, body, and JSON body" do
146
+ response = test_response(test_missing_id_error, 401)
147
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
148
+ begin
149
+ Stripe::Customer.retrieve("foo")
150
+ rescue Stripe::AuthenticationError => e
151
+ assert_equal(401, e.http_status)
152
+ assert_equal(true, !!e.http_body)
153
+ assert_equal(true, e.json_body.kind_of?(Hash))
154
+ end
155
+ end
156
+
157
+ should "a 402 should give a CardError with http status, body, and JSON body" do
158
+ response = test_response(test_missing_id_error, 402)
159
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
160
+ begin
161
+ Stripe::Customer.retrieve("foo")
162
+ rescue Stripe::CardError => e
163
+ assert_equal(402, e.http_status)
164
+ assert_equal(true, !!e.http_body)
165
+ assert_equal(true, e.json_body.kind_of?(Hash))
166
+ end
167
+ end
168
+
169
+ should "a 404 should give an InvalidRequestError with http status, body, and JSON body" do
170
+ response = test_response(test_missing_id_error, 404)
171
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
172
+ begin
173
+ Stripe::Customer.retrieve("foo")
174
+ rescue Stripe::InvalidRequestError => e
175
+ assert_equal(404, e.http_status)
176
+ assert_equal(true, !!e.http_body)
177
+ assert_equal(true, e.json_body.kind_of?(Hash))
178
+ end
179
+ end
180
+
181
+ should "setting a nil value for a param should exclude that param from the request" do
182
+ @mock.expects(:get).with do |url, api_key, params|
183
+ uri = URI(url)
184
+ query = CGI.parse(uri.query)
185
+ (url =~ %r{^#{Stripe.api_base}/v1/charges?} &&
186
+ query.keys.sort == ['offset', 'sad'])
187
+ end.returns(test_response({ :count => 1, :data => [test_charge] }))
188
+ c = Stripe::Charge.all(:count => nil, :offset => 5, :sad => false)
189
+
190
+ @mock.expects(:post).with do |url, api_key, params|
191
+ url == "#{Stripe.api_base}/v1/charges" &&
192
+ api_key.nil? &&
193
+ CGI.parse(params) == { 'amount' => ['50'], 'currency' => ['usd'] }
194
+ end.returns(test_response({ :count => 1, :data => [test_charge] }))
195
+ c = Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
196
+ end
197
+
198
+ should "requesting with a unicode ID should result in a request" do
199
+ response = test_response(test_missing_id_error, 404)
200
+ @mock.expects(:get).once.with("#{Stripe.api_base}/v1/customers/%E2%98%83", nil, nil).raises(RestClient::ExceptionWithResponse.new(response, 404))
201
+ c = Stripe::Customer.new("☃")
202
+ assert_raises(Stripe::InvalidRequestError) { c.refresh }
203
+ end
204
+
205
+ should "requesting with no ID should result in an InvalidRequestError with no request" do
206
+ c = Stripe::Customer.new
207
+ assert_raises(Stripe::InvalidRequestError) { c.refresh }
208
+ end
209
+
210
+ should "making a GET request with parameters should have a query string and no body" do
211
+ params = { :limit => 1 }
212
+ @mock.expects(:get).once.with("#{Stripe.api_base}/v1/charges?limit=1", nil, nil).returns(test_response([test_charge]))
213
+ c = Stripe::Charge.all(params)
214
+ end
215
+
216
+ should "making a POST request with parameters should have a body and no query string" do
217
+ params = { :amount => 100, :currency => 'usd', :card => 'sc_token' }
218
+ @mock.expects(:post).once.with do |url, get, post|
219
+ get.nil? && CGI.parse(post) == {'amount' => ['100'], 'currency' => ['usd'], 'card' => ['sc_token']}
220
+ end.returns(test_response(test_charge))
221
+ c = Stripe::Charge.create(params)
222
+ end
223
+
224
+ should "loading an object should issue a GET request" do
225
+ @mock.expects(:get).once.returns(test_response(test_customer))
226
+ c = Stripe::Customer.new("test_customer")
227
+ c.refresh
228
+ end
229
+
230
+ should "using array accessors should be the same as the method interface" do
231
+ @mock.expects(:get).once.returns(test_response(test_customer))
232
+ c = Stripe::Customer.new("test_customer")
233
+ c.refresh
234
+ assert_equal c.created, c[:created]
235
+ assert_equal c.created, c['created']
236
+ c['created'] = 12345
237
+ assert_equal c.created, 12345
238
+ end
239
+
240
+ should "accessing a property other than id or parent on an unfetched object should fetch it" do
241
+ @mock.expects(:get).once.returns(test_response(test_customer))
242
+ c = Stripe::Customer.new("test_customer")
243
+ c.charges
244
+ end
245
+
246
+ should "updating an object should issue a POST request with only the changed properties" do
247
+ @mock.expects(:post).with do |url, api_key, params|
248
+ url == "#{Stripe.api_base}/v1/customers/c_test_customer" && api_key.nil? && CGI.parse(params) == {'description' => ['another_mn']}
249
+ end.once.returns(test_response(test_customer))
250
+ c = Stripe::Customer.construct_from(test_customer)
251
+ c.description = "another_mn"
252
+ c.save
253
+ end
254
+
255
+ should "updating should merge in returned properties" do
256
+ @mock.expects(:post).once.returns(test_response(test_customer))
257
+ c = Stripe::Customer.new("c_test_customer")
258
+ c.description = "another_mn"
259
+ c.save
260
+ assert_equal false, c.livemode
261
+ end
262
+
263
+ should "deleting should send no props and result in an object that has no props other deleted" do
264
+ @mock.expects(:get).never
265
+ @mock.expects(:post).never
266
+ @mock.expects(:delete).with("#{Stripe.api_base}/v1/customers/c_test_customer", nil, nil).once.returns(test_response({ "id" => "test_customer", "deleted" => true }))
267
+
268
+ c = Stripe::Customer.construct_from(test_customer)
269
+ c.delete
270
+ assert_equal true, c.deleted
271
+
272
+ assert_raises NoMethodError do
273
+ c.livemode
274
+ end
275
+ end
276
+
277
+ should "loading an object with properties that have specific types should instantiate those classes" do
278
+ @mock.expects(:get).once.returns(test_response(test_charge))
279
+ c = Stripe::Charge.retrieve("test_charge")
280
+ assert c.card.kind_of?(Stripe::StripeObject) && c.card.object == 'card'
281
+ end
282
+
283
+ should "loading all of an APIResource should return an array of recursively instantiated objects" do
284
+ @mock.expects(:get).once.returns(test_response(test_charge_array))
285
+ c = Stripe::Charge.all.data
286
+ assert c.kind_of? Array
287
+ assert c[0].kind_of? Stripe::Charge
288
+ assert c[0].card.kind_of?(Stripe::StripeObject) && c[0].card.object == 'card'
289
+ end
290
+
291
+ context "error checking" do
292
+
293
+ should "404s should raise an InvalidRequestError" do
294
+ response = test_response(test_missing_id_error, 404)
295
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
296
+
297
+ begin
298
+ Stripe::Customer.new("test_customer").refresh
299
+ assert false #shouldn't get here either
300
+ rescue Stripe::InvalidRequestError => e # we don't use assert_raises because we want to examine e
301
+ assert e.kind_of? Stripe::InvalidRequestError
302
+ assert_equal "id", e.param
303
+ assert_equal "Missing id", e.message
304
+ return
305
+ end
306
+
307
+ assert false #shouldn't get here
308
+ end
309
+
310
+ should "5XXs should raise an APIError" do
311
+ response = test_response(test_api_error, 500)
312
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 500))
313
+
314
+ begin
315
+ Stripe::Customer.new("test_customer").refresh
316
+ assert false #shouldn't get here either
317
+ rescue Stripe::APIError => e # we don't use assert_raises because we want to examine e
318
+ assert e.kind_of? Stripe::APIError
319
+ return
320
+ end
321
+
322
+ assert false #shouldn't get here
323
+ end
324
+
325
+ should "402s should raise a CardError" do
326
+ response = test_response(test_invalid_exp_year_error, 402)
327
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 402))
328
+
329
+ begin
330
+ Stripe::Customer.new("test_customer").refresh
331
+ assert false #shouldn't get here either
332
+ rescue Stripe::CardError => e # we don't use assert_raises because we want to examine e
333
+ assert e.kind_of? Stripe::CardError
334
+ assert_equal "invalid_expiry_year", e.code
335
+ assert_equal "exp_year", e.param
336
+ assert_equal "Your card's expiration year is invalid", e.message
337
+ return
338
+ end
339
+
340
+ assert false #shouldn't get here
341
+ end
342
+ end
343
+ end
344
+ end
345
+ end