stripe 1.8.1 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  /stripe-*.gem
2
+ /Gemfile.lock
2
3
  .rvmrc
@@ -3,6 +3,7 @@ rvm:
3
3
  - 1.8.7
4
4
  - 1.9.2
5
5
  - 1.9.3
6
+ - 2.0.0
6
7
  gemfile:
7
8
  - gemfiles/default-with-activesupport.gemfile
8
9
  - gemfiles/json.gemfile
@@ -1,3 +1,17 @@
1
+ === 1.8.2 2013-05-01
2
+
3
+ * 3 minor enhancement:
4
+ * Use to_sym instead of type checking for minor performance
5
+ improvement (github issue #59)
6
+ * Handle low-memory situations without throwing an exception (github
7
+ issue #61)
8
+ * Add an Customer#upcoming_invoice convenience method (github issue
9
+ #65)
10
+
11
+ * 1 bugfix:
12
+ * Allow updating resources without first retrieving them (github
13
+ issue #60)
14
+
1
15
  === 1.8.1 2013-04-19
2
16
 
3
17
  * 1 minor enhancement:
@@ -23,6 +23,16 @@ The stripe gem is mirrored on Rubygems, so you should be able to
23
23
  install it via <tt>gem install stripe</tt> if desired. We recommend using
24
24
  the https://code.stripe.com mirror so all code is fetched over SSL.
25
25
 
26
+ Note that if you are installing via bundler, you should be sure to use the https
27
+ rubygems source in your Gemfile, as any gems fetched over http could potentially be
28
+ comprimised in transit and alter the code of gems fetched securely over https:
29
+
30
+ source 'https://code.stripe.com'
31
+ source 'https://rubygems.org'
32
+
33
+ gem 'rails'
34
+ gem 'stripe'
35
+
26
36
  == Development
27
37
 
28
38
  Test cases can be run with: `bundle exec rake test`
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.8.1
1
+ 1.8.2
@@ -43,163 +43,164 @@ require 'stripe/errors/invalid_request_error'
43
43
  require 'stripe/errors/authentication_error'
44
44
 
45
45
  module Stripe
46
- @@ssl_bundle_path = File.join(File.dirname(__FILE__), 'data/ca-certificates.crt')
47
- @@api_key = nil
48
- @@api_base = 'https://api.stripe.com'
49
- @@verify_ssl_certs = true
50
- @@api_version = nil
46
+ @api_base = 'https://api.stripe.com'
51
47
 
52
- def self.api_url(url='')
53
- @@api_base + url
54
- end
55
-
56
- def self.api_key=(api_key)
57
- @@api_key = api_key
58
- end
59
-
60
- def self.api_key
61
- @@api_key
62
- end
48
+ @ssl_bundle_path = File.dirname(__FILE__) + '/data/ca-certificates.crt'
49
+ @verify_ssl_certs = true
63
50
 
64
- def self.api_base=(api_base)
65
- @@api_base = api_base
51
+ class << self
52
+ attr_accessor :api_key, :api_base, :verify_ssl_certs, :api_version
66
53
  end
67
54
 
68
- def self.api_base
69
- @@api_base
70
- end
71
-
72
- def self.verify_ssl_certs=(verify)
73
- @@verify_ssl_certs = verify
74
- end
75
-
76
- def self.verify_ssl_certs
77
- @@verify_ssl_certs
55
+ def self.api_url(url='')
56
+ @api_base + url
78
57
  end
79
58
 
80
- def self.api_version=(version)
81
- @@api_version = version
82
- end
59
+ def self.request(method, url, api_key, params={}, headers={})
60
+ unless api_key ||= @api_key
61
+ raise AuthenticationError.new('No API key provided. ' +
62
+ 'Set your API key using "Stripe.api_key = <API-KEY>". ' +
63
+ 'You can generate API keys from the Stripe web interface. ' +
64
+ 'See https://stripe.com/api for details, or email support@stripe.com ' +
65
+ 'if you have any questions.')
66
+ end
83
67
 
84
- def self.api_version
85
- @@api_version
86
- end
68
+ if api_key =~ /\s/
69
+ raise AuthenticationError.new('Your API key is invalid, as it contains ' +
70
+ 'whitespace. (HINT: You can double-check your API key from the ' +
71
+ 'Stripe web interface. See https://stripe.com/api for details, or ' +
72
+ 'email support@stripe.com if you have any questions.)')
73
+ end
87
74
 
88
- def self.request(method, url, api_key, params={}, headers={})
89
- api_key ||= @@api_key
90
- raise AuthenticationError.new('No API key provided. (HINT: set your API key using "Stripe.api_key = <API-KEY>". You can generate API keys from the Stripe web interface. See https://stripe.com/api for details, or email support@stripe.com if you have any questions.)') unless api_key
75
+ request_opts = { :verify_ssl => false }
91
76
 
92
- if !verify_ssl_certs
93
- unless @no_verify
94
- $stderr.puts "WARNING: Running without SSL cert verification. Execute 'Stripe.verify_ssl_certs = true' to enable verification."
95
- @no_verify = true
96
- end
97
- ssl_opts = { :verify_ssl => false }
98
- elsif !Util.file_readable(@@ssl_bundle_path)
99
- unless @no_bundle
100
- $stderr.puts "WARNING: Running without SSL cert verification because #{@@ssl_bundle_path} isn't readable"
101
- @no_bundle = true
102
- end
103
- ssl_opts = { :verify_ssl => false }
104
- else
105
- ssl_opts = {
106
- :verify_ssl => OpenSSL::SSL::VERIFY_PEER,
107
- :ssl_ca_file => @@ssl_bundle_path
108
- }
77
+ if ssl_preflight_passed?
78
+ request_opts.update(:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
79
+ :ssl_ca_file => @ssl_bundle_path)
109
80
  end
110
- uname = (@@uname ||= RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil)
111
- lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
112
- ua = {
113
- :bindings_version => Stripe::VERSION,
114
- :lang => 'ruby',
115
- :lang_version => lang_version,
116
- :platform => RUBY_PLATFORM,
117
- :publisher => 'stripe',
118
- :uname => uname
119
- }
120
81
 
121
82
  params = Util.objects_to_ids(params)
122
- url = self.api_url(url)
83
+ url = api_url(url)
84
+
123
85
  case method.to_s.downcase.to_sym
124
86
  when :get, :head, :delete
125
87
  # Make params into GET parameters
126
- if params && params.count > 0
127
- query_string = Util.flatten_params(params).collect{|key, value| "#{key}=#{Util.url_encode(value)}"}.join('&')
128
- url += "#{URI.parse(url).query ? '&' : '?'}#{query_string}"
129
- end
88
+ url += "#{URI.parse(url).query ? '&' : '?'}#{uri_encode(params)}" if params && params.any?
130
89
  payload = nil
131
90
  else
132
- payload = Util.flatten_params(params).collect{|(key, value)| "#{key}=#{Util.url_encode(value)}"}.join('&')
91
+ payload = uri_encode(params)
133
92
  end
134
93
 
135
- begin
136
- headers = { :x_stripe_client_user_agent => Stripe::JSON.dump(ua) }.merge(headers)
137
- rescue => e
138
- headers = {
139
- :x_stripe_client_raw_user_agent => ua.inspect,
140
- :error => "#{e} (#{e.class})"
141
- }.merge(headers)
142
- end
143
-
144
- headers = {
145
- :user_agent => "Stripe/v1 RubyBindings/#{Stripe::VERSION}",
146
- :authorization => "Bearer #{api_key}",
147
- :content_type => 'application/x-www-form-urlencoded'
148
- }.merge(headers)
149
-
150
- if self.api_version
151
- headers[:stripe_version] = self.api_version
152
- end
153
-
154
- opts = {
155
- :method => method,
156
- :url => url,
157
- :headers => headers,
158
- :open_timeout => 30,
159
- :payload => payload,
160
- :timeout => 80
161
- }.merge(ssl_opts)
94
+ request_opts.update(:headers => request_headers.update(headers),
95
+ :method => method, :open_timeout => 30,
96
+ :payload => payload, :url => url, :timeout => 80)
162
97
 
163
98
  begin
164
- response = execute_request(opts)
99
+ response = execute_request(request_opts)
165
100
  rescue SocketError => e
166
- self.handle_restclient_error(e)
101
+ handle_restclient_error(e)
167
102
  rescue NoMethodError => e
168
103
  # Work around RestClient bug
169
104
  if e.message =~ /\WRequestFailed\W/
170
105
  e = APIConnectionError.new('Unexpected HTTP response code')
171
- self.handle_restclient_error(e)
106
+ handle_restclient_error(e)
172
107
  else
173
108
  raise
174
109
  end
175
110
  rescue RestClient::ExceptionWithResponse => e
176
111
  if rcode = e.http_code and rbody = e.http_body
177
- self.handle_api_error(rcode, rbody)
112
+ handle_api_error(rcode, rbody)
178
113
  else
179
- self.handle_restclient_error(e)
114
+ handle_restclient_error(e)
180
115
  end
181
116
  rescue RestClient::Exception, Errno::ECONNREFUSED => e
182
- self.handle_restclient_error(e)
117
+ handle_restclient_error(e)
183
118
  end
184
119
 
185
- rbody = response.body
186
- rcode = response.code
120
+ parse(response)
121
+ end
122
+
123
+ private
124
+
125
+ def self.ssl_preflight_passed?
126
+ if !verify_ssl_certs && !@no_verify
127
+ $stderr.puts "WARNING: Running without SSL cert verification. " +
128
+ "Execute 'Stripe.verify_ssl_certs = true' to enable verification."
129
+
130
+ @no_verify = true
131
+
132
+ elsif !Util.file_readable(@ssl_bundle_path) && !@no_bundle
133
+ $stderr.puts "WARNING: Running without SSL cert verification " +
134
+ "because #{@ssl_bundle_path} isn't readable"
135
+
136
+ @no_bundle = true
137
+ end
138
+
139
+ !(@no_verify || @no_nobundle)
140
+ end
141
+
142
+ def self.user_agent
143
+ @uname ||= get_uname
144
+ lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
145
+
146
+ {
147
+ :bindings_version => Stripe::VERSION,
148
+ :lang => 'ruby',
149
+ :lang_version => lang_version,
150
+ :platform => RUBY_PLATFORM,
151
+ :publisher => 'stripe',
152
+ :uname => @uname
153
+ }
154
+
155
+ end
156
+
157
+ def self.get_uname
158
+ `uname -a 2>/dev/null`.strip if RUBY_PLATFORM =~ /linux|darwin/i
159
+ rescue Errno::ENOMEM => ex # couldn't create subprocess
160
+ "uname lookup failed"
161
+ end
162
+
163
+ def self.uri_encode(params)
164
+ Util.flatten_params(params).
165
+ map { |k,v| "#{k}=#{Util.url_encode(v)}" }.join('&')
166
+ end
167
+
168
+ def self.request_headers
169
+ headers = {
170
+ :user_agent => "Stripe/v1 RubyBindings/#{Stripe::VERSION}",
171
+ :authorization => "Bearer #{api_key}",
172
+ :content_type => 'application/x-www-form-urlencoded'
173
+ }
174
+
175
+ headers[:stripe_version] = api_version if api_version
176
+
177
+ begin
178
+ headers.update(:x_stripe_client_user_agent => Stripe::JSON.dump(user_agent))
179
+ rescue => e
180
+ headers.update(:x_stripe_client_raw_user_agent => user_agent.inspect,
181
+ :error => "#{e} (#{e.class})")
182
+ end
183
+ end
184
+
185
+ def self.execute_request(opts)
186
+ RestClient::Request.execute(opts)
187
+ end
188
+
189
+ def self.parse(response)
187
190
  begin
188
191
  # Would use :symbolize_names => true, but apparently there is
189
192
  # some library out there that makes symbolize_names not work.
190
- resp = Stripe::JSON.load(rbody)
193
+ response = Stripe::JSON.load(response.body)
191
194
  rescue MultiJson::DecodeError
192
- raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})", rcode, rbody)
195
+ raise general_api_error(response.code, response.body)
193
196
  end
194
197
 
195
- resp = Util.symbolize_names(resp)
196
- [resp, api_key]
198
+ [Util.symbolize_names(response), api_key]
197
199
  end
198
200
 
199
- private
200
-
201
- def self.execute_request(opts)
202
- RestClient::Request.execute(opts)
201
+ def self.general_api_error(rcode, rbody)
202
+ APIError.new("Invalid response object from API: #{rbody.inspect} " +
203
+ "(HTTP response code was #{rcode})", rcode, rbody)
203
204
  end
204
205
 
205
206
  def self.handle_api_error(rcode, rbody)
@@ -207,24 +208,27 @@ module Stripe
207
208
  error_obj = Stripe::JSON.load(rbody)
208
209
  error_obj = Util.symbolize_names(error_obj)
209
210
  error = error_obj[:error] or raise StripeError.new # escape from parsing
211
+
210
212
  rescue MultiJson::DecodeError, StripeError
211
- raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})", rcode, rbody)
213
+ raise general_api_error(rcode, rbody)
212
214
  end
213
215
 
214
216
  case rcode
215
- when 400, 404 then
216
- raise invalid_request_error(error, rcode, rbody, error_obj)
217
+ when 400, 404
218
+ raise invalid_request_error error, rcode, rbody, error_obj
217
219
  when 401
218
- raise authentication_error(error, rcode, rbody, error_obj)
220
+ raise authentication_error error, rcode, rbody, error_obj
219
221
  when 402
220
- raise card_error(error, rcode, rbody, error_obj)
222
+ raise card_error error, rcode, rbody, error_obj
221
223
  else
222
- raise api_error(error, rcode, rbody, error_obj)
224
+ raise api_error error, rcode, rbody, error_obj
223
225
  end
226
+
224
227
  end
225
228
 
226
229
  def self.invalid_request_error(error, rcode, rbody, error_obj)
227
- InvalidRequestError.new(error[:message], error[:param], rcode, rbody, error_obj)
230
+ InvalidRequestError.new(error[:message], error[:param], rcode,
231
+ rbody, error_obj)
228
232
  end
229
233
 
230
234
  def self.authentication_error(error, rcode, rbody, error_obj)
@@ -232,7 +236,8 @@ module Stripe
232
236
  end
233
237
 
234
238
  def self.card_error(error, rcode, rbody, error_obj)
235
- CardError.new(error[:message], error[:param], error[:code], rcode, rbody, error_obj)
239
+ CardError.new(error[:message], error[:param], error[:code],
240
+ rcode, rbody, error_obj)
236
241
  end
237
242
 
238
243
  def self.api_error(error, rcode, rbody, error_obj)
@@ -242,15 +247,28 @@ module Stripe
242
247
  def self.handle_restclient_error(e)
243
248
  case e
244
249
  when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
245
- message = "Could not connect to Stripe (#{@@api_base}). Please check your internet connection and try again. If this problem persists, you should check Stripe's service status at https://twitter.com/stripestatus, or let us know at support@stripe.com."
250
+ message = "Could not connect to Stripe (#{@api_base}). " +
251
+ "Please check your internet connection and try again. " +
252
+ "If this problem persists, you should check Stripe's service status at " +
253
+ "https://twitter.com/stripestatus, or let us know at support@stripe.com."
254
+
246
255
  when RestClient::SSLCertificateNotVerified
247
- message = "Could not verify Stripe's SSL certificate. Please make sure that your network is not intercepting certificates. (Try going to https://api.stripe.com/v1 in your browser.) If this problem persists, let us know at support@stripe.com."
256
+ message = "Could not verify Stripe's SSL certificate. " +
257
+ "Please make sure that your network is not intercepting certificates. " +
258
+ "(Try going to https://api.stripe.com/v1 in your browser.) " +
259
+ "If this problem persists, let us know at support@stripe.com."
260
+
248
261
  when SocketError
249
- message = "Unexpected error communicating when trying to connect to Stripe. HINT: You may be seeing this message because your DNS is not working. To check, try running 'host stripe.com' from the command line."
262
+ message = "Unexpected error communicating when trying to connect to Stripe. " +
263
+ "You may be seeing this message because your DNS is not working. " +
264
+ "To check, try running 'host stripe.com' from the command line."
265
+
250
266
  else
251
- message = "Unexpected error communicating with Stripe. If this problem persists, let us know at support@stripe.com."
267
+ message = "Unexpected error communicating with Stripe. " +
268
+ "If this problem persists, let us know at support@stripe.com."
269
+
252
270
  end
253
- message += "\n\n(Network error: #{e.message})"
254
- raise APIConnectionError.new(message)
271
+
272
+ raise APIConnectionError.new(message + "\n\n(Network error: #{e.message})")
255
273
  end
256
274
  end
@@ -5,6 +5,7 @@ module Stripe
5
5
  if @unsaved_values.length > 0
6
6
  values = {}
7
7
  @unsaved_values.each { |k| values[k] = @values[k] }
8
+ values.delete(:id)
8
9
  response, api_key = Stripe.request(:post, url, @api_key, values)
9
10
  refresh_from(response, api_key)
10
11
  end
@@ -17,6 +17,10 @@ module Stripe
17
17
  InvoiceItem.all({ :customer => id }, @api_key)
18
18
  end
19
19
 
20
+ def upcoming_invoice
21
+ Invoice.upcoming({ :customer => id }, @api_key)
22
+ end
23
+
20
24
  def charges
21
25
  Charge.all({ :customer => id }, @api_key)
22
26
  end
@@ -70,8 +70,7 @@ module Stripe
70
70
  end
71
71
 
72
72
  def [](k)
73
- k = k.to_sym if k.kind_of?(String)
74
- @values[k]
73
+ @values[k.to_sym]
75
74
  end
76
75
 
77
76
  def []=(k, v)
@@ -1,3 +1,3 @@
1
1
  module Stripe
2
- VERSION = '1.8.1'
2
+ VERSION = '1.8.2'
3
3
  end
@@ -81,6 +81,13 @@ class TestStripeRuby < Test::Unit::TestCase
81
81
  end
82
82
  end
83
83
 
84
+ should "specifying api credentials containing whitespace should raise an exception" do
85
+ Stripe.api_key = "key "
86
+ assert_raises Stripe::AuthenticationError do
87
+ Stripe::Customer.new("test_customer").refresh
88
+ end
89
+ end
90
+
84
91
  should "specifying invalid api credentials should raise an exception" do
85
92
  Stripe.api_key = "invalid"
86
93
  response = test_response(test_invalid_api_key_error, 401)
@@ -431,7 +438,7 @@ class TestStripeRuby < Test::Unit::TestCase
431
438
  c = Stripe::Customer.retrieve("test_customer")
432
439
 
433
440
  # Not an accurate response, but whatever
434
-
441
+
435
442
  @mock.expects(:delete).once.with("#{Stripe.api_base}/v1/customers/c_test_customer/subscription?at_period_end=true", nil, nil).returns(test_response(test_subscription('silver')))
436
443
  s = c.cancel_subscription({:at_period_end => 'true'})
437
444
 
@@ -447,6 +454,15 @@ class TestStripeRuby < Test::Unit::TestCase
447
454
  s = c.delete_discount
448
455
  assert_equal nil, c.discount
449
456
  end
457
+
458
+ should "be able to update a customer without refreshing it first" do
459
+ @mock.expects(:post).once.with("#{Stripe.api_base}/v1/customers/test_customer", nil, 'mnemonic=bar').returns(test_response(test_customer({:mnemonic => "bar"})))
460
+ c = Stripe::Customer.new("test_customer")
461
+ c.mnemonic = "bar"
462
+ c.save
463
+ assert_equal c.mnemonic, "bar"
464
+ end
465
+
450
466
  end
451
467
 
452
468
  context "card tests" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stripe
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.1
4
+ version: 1.8.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-04-19 00:00:00.000000000 Z
13
+ date: 2013-05-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rest-client
@@ -199,3 +199,4 @@ test_files:
199
199
  - test/test_helper.rb
200
200
  - test/test_stripe.rb
201
201
  - test/test_stripe_with_active_support.rb
202
+ has_rdoc: