stripe 1.8.1 → 1.8.2

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.
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: