stripe 1.7.0 → 1.7.1

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 (48) hide show
  1. data/.gitignore +2 -0
  2. data/.travis.yml +8 -0
  3. data/CONTRIBUTORS +3 -0
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +34 -0
  6. data/History.txt +68 -0
  7. data/LICENSE +21 -0
  8. data/README.rdoc +28 -0
  9. data/Rakefile +9 -0
  10. data/VERSION +1 -0
  11. data/gemfiles/default-with-activesupport.gemfile +3 -0
  12. data/gemfiles/default-with-activesupport.gemfile.lock +39 -0
  13. data/gemfiles/json.gemfile +4 -0
  14. data/gemfiles/json.gemfile.lock +41 -0
  15. data/gemfiles/yajl.gemfile +4 -0
  16. data/gemfiles/yajl.gemfile.lock +41 -0
  17. data/lib/stripe.rb +62 -500
  18. data/lib/stripe/account.rb +4 -0
  19. data/lib/stripe/api_operations/create.rb +16 -0
  20. data/lib/stripe/api_operations/delete.rb +11 -0
  21. data/lib/stripe/api_operations/list.rb +16 -0
  22. data/lib/stripe/api_operations/update.rb +15 -0
  23. data/lib/stripe/api_resource.rb +33 -0
  24. data/lib/stripe/charge.rb +29 -0
  25. data/lib/stripe/coupon.rb +7 -0
  26. data/lib/stripe/customer.rb +51 -0
  27. data/lib/stripe/errors/api_connection_error.rb +4 -0
  28. data/lib/stripe/errors/api_error.rb +4 -0
  29. data/lib/stripe/errors/authentication_error.rb +4 -0
  30. data/lib/stripe/errors/card_error.rb +11 -0
  31. data/lib/stripe/errors/invalid_request_error.rb +10 -0
  32. data/lib/stripe/errors/stripe_error.rb +20 -0
  33. data/lib/stripe/event.rb +5 -0
  34. data/lib/stripe/invoice.rb +16 -0
  35. data/lib/stripe/invoice_item.rb +8 -0
  36. data/lib/stripe/json.rb +21 -0
  37. data/lib/stripe/plan.rb +8 -0
  38. data/lib/stripe/singleton_api_resource.rb +20 -0
  39. data/lib/stripe/stripe_object.rb +150 -0
  40. data/lib/stripe/token.rb +5 -0
  41. data/lib/stripe/transfer.rb +16 -0
  42. data/lib/stripe/util.rb +103 -0
  43. data/lib/stripe/version.rb +1 -1
  44. data/stripe.gemspec +28 -0
  45. data/test/test_helper.rb +175 -0
  46. data/test/test_stripe.rb +472 -0
  47. data/test/test_stripe_with_active_support.rb +3 -0
  48. metadata +54 -7
@@ -0,0 +1,5 @@
1
+ module Stripe
2
+ class Token < APIResource
3
+ include Stripe::APIOperations::Create
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ module Stripe
2
+ class Transfer < APIResource
3
+ include Stripe::APIOperations::List
4
+
5
+ def transactions(params={})
6
+ response, api_key = Stripe.request(:get, transactions_url, @api_key, params)
7
+ Util.convert_to_stripe_object(response, api_key)
8
+ end
9
+
10
+ private
11
+
12
+ def transactions_url
13
+ url + '/transactions'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,103 @@
1
+ module Stripe
2
+ module Util
3
+ def self.objects_to_ids(h)
4
+ case h
5
+ when APIResource
6
+ h.id
7
+ when Hash
8
+ res = {}
9
+ h.each { |k, v| res[k] = objects_to_ids(v) unless v.nil? }
10
+ res
11
+ when Array
12
+ h.map { |v| objects_to_ids(v) }
13
+ else
14
+ h
15
+ end
16
+ end
17
+
18
+ def self.convert_to_stripe_object(resp, api_key)
19
+ types = {
20
+ 'charge' => Charge,
21
+ 'customer' => Customer,
22
+ 'invoiceitem' => InvoiceItem,
23
+ 'invoice' => Invoice,
24
+ 'plan' => Plan,
25
+ 'coupon' => Coupon,
26
+ 'event' => Event,
27
+ 'transfer' => Transfer
28
+ }
29
+ case resp
30
+ when Array
31
+ resp.map { |i| convert_to_stripe_object(i, api_key) }
32
+ when Hash
33
+ # Try converting to a known object class. If none available, fall back to generic APIResource
34
+ if klass_name = resp[:object]
35
+ klass = types[klass_name]
36
+ end
37
+ klass ||= StripeObject
38
+ klass.construct_from(resp, api_key)
39
+ else
40
+ resp
41
+ end
42
+ end
43
+
44
+ def self.file_readable(file)
45
+ begin
46
+ File.open(file) { |f| }
47
+ rescue
48
+ false
49
+ else
50
+ true
51
+ end
52
+ end
53
+
54
+ def self.symbolize_names(object)
55
+ case object
56
+ when Hash
57
+ new = {}
58
+ object.each do |key, value|
59
+ key = (key.to_sym rescue key) || key
60
+ new[key] = symbolize_names(value)
61
+ end
62
+ new
63
+ when Array
64
+ object.map { |value| symbolize_names(value) }
65
+ else
66
+ object
67
+ end
68
+ end
69
+
70
+ def self.encode_key(key)
71
+ URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
72
+ end
73
+
74
+ def self.flatten_params(params, parent_key=nil)
75
+ result = []
76
+ params.each do |key, value|
77
+ calculated_key = parent_key ? "#{parent_key}[#{encode_key(key)}]" : encode_key(key)
78
+ if value.is_a?(Hash)
79
+ result += flatten_params(value, calculated_key)
80
+ elsif value.is_a?(Array)
81
+ result += flatten_params_array(value, calculated_key)
82
+ else
83
+ result << [calculated_key, value]
84
+ end
85
+ end
86
+ result
87
+ end
88
+
89
+ def self.flatten_params_array(value, calculated_key)
90
+ result = []
91
+ value.each do |elem|
92
+ if elem.is_a?(Hash)
93
+ result += flatten_params(elem, calculated_key)
94
+ elsif elem.is_a?(Array)
95
+ result += flatten_params_array(elem, calculated_key)
96
+ else
97
+ result << ["#{calculated_key}[]", elem]
98
+ end
99
+ end
100
+ result
101
+ end
102
+ end
103
+ end
@@ -1,3 +1,3 @@
1
1
  module Stripe
2
- VERSION = '1.7.0'
2
+ VERSION = '1.7.1'
3
3
  end
data/stripe.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
+
3
+ require 'stripe/version'
4
+
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = 'stripe'
7
+ s.version = Stripe::VERSION
8
+ s.summary = 'Ruby bindings for the Stripe API'
9
+ s.description = 'Stripe is the easiest way to accept payments online. See https://stripe.com for details.'
10
+ s.authors = ['Ross Boucher', 'Greg Brockman']
11
+ s.email = ['boucher@stripe.com', 'gdb@stripe.com']
12
+ s.homepage = 'https://stripe.com/api'
13
+ s.executables = 'stripe-console'
14
+ s.require_paths = %w{lib}
15
+
16
+ s.add_dependency('rest-client', '~> 1.4')
17
+ s.add_dependency('multi_json', '~> 1.1')
18
+
19
+ s.add_development_dependency('mocha')
20
+ s.add_development_dependency('shoulda')
21
+ s.add_development_dependency('test-unit')
22
+ s.add_development_dependency('rake')
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- test/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ['lib']
28
+ end
@@ -0,0 +1,175 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require 'stripe'
4
+ require 'mocha'
5
+ include Mocha
6
+
7
+ #monkeypatch request methods
8
+ module Stripe
9
+ @mock_rest_client = nil
10
+
11
+ def self.mock_rest_client=(mock_client)
12
+ @mock_rest_client = mock_client
13
+ end
14
+
15
+ def self.execute_request(opts)
16
+ get_params = (opts[:headers] || {})[:params]
17
+ post_params = opts[:payload]
18
+ case opts[:method]
19
+ when :get then @mock_rest_client.get opts[:url], get_params, post_params
20
+ when :post then @mock_rest_client.post opts[:url], get_params, post_params
21
+ when :delete then @mock_rest_client.delete opts[:url], get_params, post_params
22
+ end
23
+ end
24
+ end
25
+
26
+ def test_response(body, code=200)
27
+ # When an exception is raised, restclient clobbers method_missing. Hence we
28
+ # can't just use the stubs interface.
29
+ body = MultiJson.dump(body) if !(body.kind_of? String)
30
+ m = mock
31
+ m.instance_variable_set('@stripe_values', { :body => body, :code => code })
32
+ def m.body; @stripe_values[:body]; end
33
+ def m.code; @stripe_values[:code]; end
34
+ m
35
+ end
36
+
37
+ def test_customer(params={})
38
+ {
39
+ :subscription_history => [],
40
+ :bills => [],
41
+ :charges => [],
42
+ :livemode => false,
43
+ :object => "customer",
44
+ :id => "c_test_customer",
45
+ :active_card => {
46
+ :type => "Visa",
47
+ :last4 => "4242",
48
+ :exp_month => 11,
49
+ :country => "US",
50
+ :exp_year => 2012,
51
+ :id => "cc_test_card",
52
+ :object => "card"
53
+ },
54
+ :created => 1304114758
55
+ }.merge(params)
56
+ end
57
+
58
+ def test_customer_array
59
+ {:data => [test_customer, test_customer, test_customer]}
60
+ end
61
+
62
+ def test_charge(params={})
63
+ {
64
+ :refunded => false,
65
+ :paid => true,
66
+ :amount => 100,
67
+ :card => {
68
+ :type => "Visa",
69
+ :last4 => "4242",
70
+ :exp_month => 11,
71
+ :country => "US",
72
+ :exp_year => 2012,
73
+ :id => "cc_test_card",
74
+ :object => "card"
75
+ },
76
+ :id => "ch_test_charge",
77
+ :reason => "execute_charge",
78
+ :livemode => false,
79
+ :currency => "usd",
80
+ :object => "charge",
81
+ :created => 1304114826
82
+ }.merge(params)
83
+ end
84
+
85
+ def test_charge_array
86
+ {:data => [test_charge, test_charge, test_charge]}
87
+ end
88
+
89
+ def test_card(params={})
90
+ {
91
+ :type => "Visa",
92
+ :last4 => "4242",
93
+ :exp_month => 11,
94
+ :country => "US",
95
+ :exp_year => 2012,
96
+ :id => "cc_test_card",
97
+ :object => "card"
98
+ }.merge(params)
99
+ end
100
+
101
+ def test_coupon(params={})
102
+ {
103
+ :duration => 'repeating',
104
+ :duration_in_months => 3,
105
+ :percent_off => 25,
106
+ :id => "co_test_coupon",
107
+ :object => "coupon"
108
+ }.merge(params)
109
+ end
110
+
111
+ #FIXME nested overrides would be better than hardcoding plan_id
112
+ def test_subscription(plan_id="gold")
113
+ {
114
+ :current_period_end => 1308681468,
115
+ :status => "trialing",
116
+ :plan => {
117
+ :interval => "month",
118
+ :amount => 7500,
119
+ :trial_period_days => 30,
120
+ :object => "plan",
121
+ :identifier => plan_id
122
+ },
123
+ :current_period_start => 1308595038,
124
+ :start => 1308595038,
125
+ :object => "subscription",
126
+ :trial_start => 1308595038,
127
+ :trial_end => 1308681468,
128
+ :customer => "c_test_customer"
129
+ }
130
+ end
131
+
132
+ def test_invalid_api_key_error
133
+ {
134
+ "error" => {
135
+ "type" => "invalid_request_error",
136
+ "message" => "Invalid API Key provided: invalid"
137
+ }
138
+ }
139
+ end
140
+
141
+ def test_invalid_exp_year_error
142
+ {
143
+ "error" => {
144
+ "code" => "invalid_expiry_year",
145
+ "param" => "exp_year",
146
+ "type" => "card_error",
147
+ "message" => "Your card's expiration year is invalid"
148
+ }
149
+ }
150
+ end
151
+
152
+ def test_missing_id_error
153
+ {
154
+ :error => {
155
+ :param => "id",
156
+ :type => "invalid_request_error",
157
+ :message => "Missing id"
158
+ }
159
+ }
160
+ end
161
+
162
+ def test_api_error
163
+ {
164
+ :error => {
165
+ :type => "api_error"
166
+ }
167
+ }
168
+ end
169
+
170
+ def test_delete_discount_response
171
+ {
172
+ :deleted => true,
173
+ :id => "di_test_coupon"
174
+ }
175
+ end
@@ -0,0 +1,472 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path('../test_helper', __FILE__)
3
+ require 'test/unit'
4
+ require 'shoulda'
5
+ require 'mocha'
6
+ require 'pp'
7
+ require 'rest-client'
8
+ require 'cgi'
9
+ require 'uri'
10
+
11
+ class TestStripeRuby < Test::Unit::TestCase
12
+ include Mocha
13
+
14
+ context "Util" do
15
+ should "symbolize_names should convert names to symbols" do
16
+ start = {
17
+ 'foo' => 'bar',
18
+ 'array' => [{ 'foo' => 'bar' }],
19
+ 'nested' => {
20
+ 1 => 2,
21
+ :symbol => 9,
22
+ 'string' => nil
23
+ }
24
+ }
25
+ finish = {
26
+ :foo => 'bar',
27
+ :array => [{ :foo => 'bar' }],
28
+ :nested => {
29
+ 1 => 2,
30
+ :symbol => 9,
31
+ :string => nil
32
+ }
33
+ }
34
+
35
+ symbolized = Stripe::Util.symbolize_names(start)
36
+ assert_equal(finish, symbolized)
37
+ end
38
+ end
39
+
40
+ context "API Bindings" do
41
+ setup do
42
+ @mock = mock
43
+ Stripe.mock_rest_client = @mock
44
+ end
45
+
46
+ teardown do
47
+ Stripe.mock_rest_client = nil
48
+ end
49
+
50
+ should "creating a new APIResource should not fetch over the network" do
51
+ @mock.expects(:get).never
52
+ c = Stripe::Customer.new("someid")
53
+ end
54
+
55
+ should "creating a new APIResource from a hash should not fetch over the network" do
56
+ @mock.expects(:get).never
57
+ c = Stripe::Customer.construct_from({
58
+ :id => "somecustomer",
59
+ :card => {:id => "somecard", :object => "card"},
60
+ :object => "customer"
61
+ })
62
+ end
63
+
64
+ should "setting an attribute should not cause a network request" do
65
+ @mock.expects(:get).never
66
+ @mock.expects(:post).never
67
+ c = Stripe::Customer.new("test_customer");
68
+ c.card = {:id => "somecard", :object => "card"}
69
+ end
70
+
71
+ should "accessing id should not issue a fetch" do
72
+ @mock.expects(:get).never
73
+ c = Stripe::Customer.new("test_customer");
74
+ c.id
75
+ end
76
+
77
+ should "not specifying api credentials should raise an exception" do
78
+ Stripe.api_key = nil
79
+ assert_raises Stripe::AuthenticationError do
80
+ Stripe::Customer.new("test_customer").refresh
81
+ end
82
+ end
83
+
84
+ should "specifying invalid api credentials should raise an exception" do
85
+ Stripe.api_key = "invalid"
86
+ response = test_response(test_invalid_api_key_error, 401)
87
+ assert_raises Stripe::AuthenticationError do
88
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
89
+ Stripe::Customer.retrieve("failing_customer")
90
+ end
91
+ end
92
+
93
+ should "AuthenticationErrors should have an http status, http body, and JSON body" do
94
+ Stripe.api_key = "invalid"
95
+ response = test_response(test_invalid_api_key_error, 401)
96
+ begin
97
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
98
+ Stripe::Customer.retrieve("failing_customer")
99
+ rescue Stripe::AuthenticationError => e
100
+ assert_equal(401, e.http_status)
101
+ assert_equal(true, !!e.http_body)
102
+ assert_equal(true, !!e.json_body[:error][:message])
103
+ assert_equal(test_invalid_api_key_error['error']['message'], e.json_body[:error][:message])
104
+ end
105
+ end
106
+
107
+ context "with valid credentials" do
108
+ setup do
109
+ Stripe.api_key="foo"
110
+ end
111
+
112
+ teardown do
113
+ Stripe.api_key=nil
114
+ end
115
+
116
+ should "a 400 should give an InvalidRequestError with http status, body, and JSON body" do
117
+ response = test_response(test_missing_id_error, 400)
118
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
119
+ begin
120
+ Stripe::Customer.retrieve("foo")
121
+ rescue Stripe::InvalidRequestError => e
122
+ assert_equal(400, e.http_status)
123
+ assert_equal(true, !!e.http_body)
124
+ assert_equal(true, e.json_body.kind_of?(Hash))
125
+ end
126
+ end
127
+
128
+ should "a 401 should give an AuthenticationError with http status, body, and JSON body" do
129
+ response = test_response(test_missing_id_error, 401)
130
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
131
+ begin
132
+ Stripe::Customer.retrieve("foo")
133
+ rescue Stripe::AuthenticationError => e
134
+ assert_equal(401, e.http_status)
135
+ assert_equal(true, !!e.http_body)
136
+ assert_equal(true, e.json_body.kind_of?(Hash))
137
+ end
138
+ end
139
+
140
+ should "a 402 should give a CardError with http status, body, and JSON body" do
141
+ response = test_response(test_missing_id_error, 402)
142
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
143
+ begin
144
+ Stripe::Customer.retrieve("foo")
145
+ rescue Stripe::CardError => e
146
+ assert_equal(402, e.http_status)
147
+ assert_equal(true, !!e.http_body)
148
+ assert_equal(true, e.json_body.kind_of?(Hash))
149
+ end
150
+ end
151
+
152
+ should "a 404 should give an InvalidRequestError with http status, body, and JSON body" do
153
+ response = test_response(test_missing_id_error, 404)
154
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
155
+ begin
156
+ Stripe::Customer.retrieve("foo")
157
+ rescue Stripe::InvalidRequestError => e
158
+ assert_equal(404, e.http_status)
159
+ assert_equal(true, !!e.http_body)
160
+ assert_equal(true, e.json_body.kind_of?(Hash))
161
+ end
162
+ end
163
+
164
+ should "setting a nil value for a param should exclude that param from the request" do
165
+ @mock.expects(:get).with do |url, api_key, params|
166
+ uri = URI(url)
167
+ query = CGI.parse(uri.query)
168
+ (url =~ %r{^https://api.stripe.com/v1/charges?} &&
169
+ query.keys.sort == ['offset', 'sad'])
170
+ end.returns(test_response({ :count => 1, :data => [test_charge] }))
171
+ c = Stripe::Charge.all(:count => nil, :offset => 5, :sad => false)
172
+
173
+ @mock.expects(:post).with('https://api.stripe.com/v1/charges', nil, { :amount => 50, :currency => 'usd', :card => {} }).returns(test_response({ :count => 1, :data => [test_charge] }))
174
+ c = Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
175
+ end
176
+
177
+ should "requesting with a unicode ID should result in a request" do
178
+ response = test_response(test_missing_id_error, 404)
179
+ @mock.expects(:get).once.with("https://api.stripe.com/v1/customers/%E2%98%83", nil, nil).raises(RestClient::ExceptionWithResponse.new(response, 404))
180
+ c = Stripe::Customer.new("☃")
181
+ assert_raises(Stripe::InvalidRequestError) { c.refresh }
182
+ end
183
+
184
+ should "requesting with no ID should result in an InvalidRequestError with no request" do
185
+ c = Stripe::Customer.new
186
+ assert_raises(Stripe::InvalidRequestError) { c.refresh }
187
+ end
188
+
189
+ should "making a GET request with parameters should have a query string and no body" do
190
+ params = { :limit => 1 }
191
+ @mock.expects(:get).once.with("https://api.stripe.com/v1/charges?limit=1", nil, nil).returns(test_response([test_charge]))
192
+ c = Stripe::Charge.all(params)
193
+ end
194
+
195
+ should "making a POST request with parameters should have a body and no query string" do
196
+ params = { :amount => 100, :currency => 'usd', :card => 'sc_token' }
197
+ @mock.expects(:post).once.with { |url, get, post| get.nil? and post == params }.returns(test_response(test_charge))
198
+ c = Stripe::Charge.create(params)
199
+ end
200
+
201
+ should "loading an object should issue a GET request" do
202
+ @mock.expects(:get).once.returns(test_response(test_customer))
203
+ c = Stripe::Customer.new("test_customer")
204
+ c.refresh
205
+ end
206
+
207
+ should "using array accessors should be the same as the method interface" do
208
+ @mock.expects(:get).once.returns(test_response(test_customer))
209
+ c = Stripe::Customer.new("test_customer")
210
+ c.refresh
211
+ assert_equal c.created, c[:created]
212
+ assert_equal c.created, c['created']
213
+ c['created'] = 12345
214
+ assert_equal c.created, 12345
215
+ end
216
+
217
+ should "accessing a property other than id or parent on an unfetched object should fetch it" do
218
+ @mock.expects(:get).once.returns(test_response(test_customer))
219
+ c = Stripe::Customer.new("test_customer")
220
+ c.charges
221
+ end
222
+
223
+ should "updating an object should issue a POST request with only the changed properties" do
224
+ @mock.expects(:post).with("https://api.stripe.com/v1/customers/c_test_customer", nil, {:mnemonic => 'another_mn'}).once.returns(test_response(test_customer))
225
+ c = Stripe::Customer.construct_from(test_customer)
226
+ c.mnemonic = "another_mn"
227
+ c.save
228
+ end
229
+
230
+ should "updating should merge in returned properties" do
231
+ @mock.expects(:post).once.returns(test_response(test_customer))
232
+ c = Stripe::Customer.new("c_test_customer")
233
+ c.mnemonic = "another_mn"
234
+ c.save
235
+ assert_equal false, c.livemode
236
+ end
237
+
238
+ should "deleting should send no props and result in an object that has no props other deleted" do
239
+ @mock.expects(:get).never
240
+ @mock.expects(:post).never
241
+ @mock.expects(:delete).with("https://api.stripe.com/v1/customers/c_test_customer", nil, nil).once.returns(test_response({ "id" => "test_customer", "deleted" => true }))
242
+
243
+ c = Stripe::Customer.construct_from(test_customer)
244
+ c.delete
245
+ assert_equal true, c.deleted
246
+
247
+ assert_raises NoMethodError do
248
+ c.livemode
249
+ end
250
+ end
251
+
252
+ should "loading an object with properties that have specific types should instantiate those classes" do
253
+ @mock.expects(:get).once.returns(test_response(test_charge))
254
+ c = Stripe::Charge.retrieve("test_charge")
255
+ assert c.card.kind_of?(Stripe::StripeObject) && c.card.object == 'card'
256
+ end
257
+
258
+ should "loading all of an APIResource should return an array of recursively instantiated objects" do
259
+ @mock.expects(:get).once.returns(test_response(test_charge_array))
260
+ c = Stripe::Charge.all.data
261
+ assert c.kind_of? Array
262
+ assert c[0].kind_of? Stripe::Charge
263
+ assert c[0].card.kind_of?(Stripe::StripeObject) && c[0].card.object == 'card'
264
+ end
265
+
266
+ context "account tests" do
267
+ should "account should be retrievable" do
268
+ resp = {:email => "test+bindings@stripe.com", :charge_enabled => false, :details_submitted => false}
269
+ @mock.expects(:get).once.returns(test_response(resp))
270
+ a = Stripe::Account.retrieve
271
+ assert_equal "test+bindings@stripe.com", a.email
272
+ assert !a.charge_enabled
273
+ assert !a.details_submitted
274
+ end
275
+ end
276
+
277
+ context "charge tests" do
278
+
279
+ should "charges should be listable" do
280
+ @mock.expects(:get).once.returns(test_response(test_charge_array))
281
+ c = Stripe::Charge.all.data
282
+ assert c.kind_of? Array
283
+ end
284
+
285
+ should "charges should be refundable" do
286
+ @mock.expects(:get).never
287
+ @mock.expects(:post).once.returns(test_response({:id => "ch_test_charge", :refunded => true}))
288
+ c = Stripe::Charge.new("test_charge")
289
+ c.refund
290
+ assert c.refunded
291
+ end
292
+
293
+ should "charges should not be deletable" do
294
+ assert_raises NoMethodError do
295
+ @mock.expects(:get).once.returns(test_response(test_charge))
296
+ c = Stripe::Charge.retrieve("test_charge")
297
+ c.delete
298
+ end
299
+ end
300
+
301
+ should "charges should be updateable" do
302
+ @mock.expects(:get).once.returns(test_response(test_charge))
303
+ @mock.expects(:post).once.returns(test_response(test_charge))
304
+ c = Stripe::Charge.new("test_charge")
305
+ c.refresh
306
+ c.mnemonic = "New charge description"
307
+ c.save
308
+ end
309
+
310
+ should "charges should have Card objects associated with their Card property" do
311
+ @mock.expects(:get).once.returns(test_response(test_charge))
312
+ c = Stripe::Charge.retrieve("test_charge")
313
+ assert c.card.kind_of?(Stripe::StripeObject) && c.card.object == 'card'
314
+ end
315
+
316
+ should "execute should return a new, fully executed charge when passed correct parameters" do
317
+ @mock.expects(:post).with('https://api.stripe.com/v1/charges', nil, {
318
+ :currency => 'usd', :amount => 100,
319
+ :card => {:exp_year => 2012, :number => '4242424242424242', :exp_month => 11}
320
+ }).once.returns(test_response(test_charge))
321
+
322
+ c = Stripe::Charge.create({
323
+ :amount => 100,
324
+ :card => {
325
+ :number => "4242424242424242",
326
+ :exp_month => 11,
327
+ :exp_year => 2012,
328
+ },
329
+ :currency => "usd"
330
+ })
331
+ assert c.paid
332
+ end
333
+
334
+ end
335
+
336
+ context "customer tests" do
337
+
338
+ should "customers should be listable" do
339
+ @mock.expects(:get).once.returns(test_response(test_customer_array))
340
+ c = Stripe::Customer.all.data
341
+ assert c.kind_of? Array
342
+ assert c[0].kind_of? Stripe::Customer
343
+ end
344
+
345
+ should "customers should be deletable" do
346
+ @mock.expects(:delete).once.returns(test_response(test_customer({:deleted => true})))
347
+ c = Stripe::Customer.new("test_customer")
348
+ c.delete
349
+ assert c.deleted
350
+ end
351
+
352
+ should "customers should be updateable" do
353
+ @mock.expects(:get).once.returns(test_response(test_customer({:mnemonic => "foo"})))
354
+ @mock.expects(:post).once.returns(test_response(test_customer({:mnemonic => "bar"})))
355
+ c = Stripe::Customer.new("test_customer").refresh
356
+ assert_equal c.mnemonic, "foo"
357
+ c.mnemonic = "bar"
358
+ c.save
359
+ assert_equal c.mnemonic, "bar"
360
+ end
361
+
362
+ should "customers should have Card objects associated with their active_ard property" do
363
+ @mock.expects(:get).once.returns(test_response(test_customer))
364
+ c = Stripe::Customer.retrieve("test_customer")
365
+ assert c.active_card.kind_of?(Stripe::StripeObject) && c.active_card.object == 'card'
366
+ end
367
+
368
+ should "create should return a new customer" do
369
+ @mock.expects(:post).once.returns(test_response(test_customer))
370
+ c = Stripe::Customer.create
371
+ assert_equal "c_test_customer", c.id
372
+ end
373
+
374
+ should "be able to update a customer's subscription" do
375
+ @mock.expects(:get).once.returns(test_response(test_customer))
376
+ c = Stripe::Customer.retrieve("test_customer")
377
+
378
+ @mock.expects(:post).once.with("https://api.stripe.com/v1/customers/c_test_customer/subscription", nil, {:plan => 'silver'}).returns(test_response(test_subscription('silver')))
379
+ s = c.update_subscription({:plan => 'silver'})
380
+
381
+ assert_equal 'subscription', s.object
382
+ assert_equal 'silver', s.plan.identifier
383
+ end
384
+
385
+ should "be able to cancel a customer's subscription" do
386
+ @mock.expects(:get).once.returns(test_response(test_customer))
387
+ c = Stripe::Customer.retrieve("test_customer")
388
+
389
+ # Not an accurate response, but whatever
390
+
391
+ @mock.expects(:delete).once.with("https://api.stripe.com/v1/customers/c_test_customer/subscription?at_period_end=true", nil, nil).returns(test_response(test_subscription('silver')))
392
+ s = c.cancel_subscription({:at_period_end => 'true'})
393
+
394
+ @mock.expects(:delete).once.with("https://api.stripe.com/v1/customers/c_test_customer/subscription?", nil, nil).returns(test_response(test_subscription('silver')))
395
+ s = c.cancel_subscription
396
+ end
397
+
398
+ should "be able to delete a customer's discount" do
399
+ @mock.expects(:get).once.returns(test_response(test_customer))
400
+ c = Stripe::Customer.retrieve("test_customer")
401
+
402
+ @mock.expects(:delete).once.with("https://api.stripe.com/v1/customers/c_test_customer/discount", nil, nil).returns(test_response(test_delete_discount_response))
403
+ s = c.delete_discount
404
+ assert_equal nil, c.discount
405
+ end
406
+ end
407
+
408
+ context "card tests" do
409
+ end
410
+
411
+ context "coupon tests" do
412
+ should "create should return a new coupon" do
413
+ @mock.expects(:post).once.returns(test_response(test_coupon))
414
+ c = Stripe::Coupon.create
415
+ assert_equal "co_test_coupon", c.id
416
+ end
417
+ end
418
+ context "error checking" do
419
+
420
+ should "404s should raise an InvalidRequestError" do
421
+ response = test_response(test_missing_id_error, 404)
422
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
423
+
424
+ begin
425
+ Stripe::Customer.new("test_customer").refresh
426
+ assert false #shouldn't get here either
427
+ rescue Stripe::InvalidRequestError => e # we don't use assert_raises because we want to examine e
428
+ assert e.kind_of? Stripe::InvalidRequestError
429
+ assert_equal "id", e.param
430
+ assert_equal "Missing id", e.message
431
+ return
432
+ end
433
+
434
+ assert false #shouldn't get here
435
+ end
436
+
437
+ should "5XXs should raise an APIError" do
438
+ response = test_response(test_api_error, 500)
439
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 500))
440
+
441
+ begin
442
+ Stripe::Customer.new("test_customer").refresh
443
+ assert false #shouldn't get here either
444
+ rescue Stripe::APIError => e # we don't use assert_raises because we want to examine e
445
+ assert e.kind_of? Stripe::APIError
446
+ return
447
+ end
448
+
449
+ assert false #shouldn't get here
450
+ end
451
+
452
+ should "402s should raise a CardError" do
453
+ response = test_response(test_invalid_exp_year_error, 402)
454
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 402))
455
+
456
+ begin
457
+ Stripe::Customer.new("test_customer").refresh
458
+ assert false #shouldn't get here either
459
+ rescue Stripe::CardError => e # we don't use assert_raises because we want to examine e
460
+ assert e.kind_of? Stripe::CardError
461
+ assert_equal "invalid_expiry_year", e.code
462
+ assert_equal "exp_year", e.param
463
+ assert_equal "Your card's expiration year is invalid", e.message
464
+ return
465
+ end
466
+
467
+ assert false #shouldn't get here
468
+ end
469
+ end
470
+ end
471
+ end
472
+ end