paid 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -1
  3. data/.travis.yml +16 -0
  4. data/History.txt +4 -0
  5. data/README.md +58 -0
  6. data/Rakefile +9 -29
  7. data/VERSION +1 -0
  8. data/bin/paid-console +7 -0
  9. data/gemfiles/default-with-activesupport.gemfile +10 -0
  10. data/gemfiles/json.gemfile +12 -0
  11. data/gemfiles/yajl.gemfile +12 -0
  12. data/lib/paid.rb +129 -177
  13. data/lib/paid/account.rb +14 -1
  14. data/lib/paid/api_class.rb +336 -0
  15. data/lib/paid/api_list.rb +47 -0
  16. data/lib/paid/api_resource.rb +8 -25
  17. data/lib/paid/api_singleton.rb +5 -0
  18. data/lib/paid/customer.rb +36 -21
  19. data/lib/paid/errors/api_error.rb +6 -0
  20. data/lib/paid/event.rb +22 -1
  21. data/lib/paid/invoice.rb +16 -21
  22. data/lib/paid/plan.rb +18 -2
  23. data/lib/paid/subscription.rb +17 -11
  24. data/lib/paid/transaction.rb +19 -12
  25. data/lib/paid/util.rb +53 -106
  26. data/lib/paid/version.rb +1 -1
  27. data/paid.gemspec +10 -11
  28. data/tasks/api_test.rb +187 -0
  29. data/test/mock_resource.rb +69 -0
  30. data/test/paid/account_test.rb +41 -4
  31. data/test/paid/api_class_test.rb +412 -0
  32. data/test/paid/api_list_test.rb +17 -0
  33. data/test/paid/api_resource_test.rb +13 -343
  34. data/test/paid/api_singleton_test.rb +12 -0
  35. data/test/paid/authentication_test.rb +50 -0
  36. data/test/paid/customer_test.rb +189 -29
  37. data/test/paid/event_test.rb +74 -0
  38. data/test/paid/invoice_test.rb +101 -20
  39. data/test/paid/plan_test.rb +84 -8
  40. data/test/paid/status_codes_test.rb +63 -0
  41. data/test/paid/subscription_test.rb +100 -20
  42. data/test/paid/transaction_test.rb +110 -37
  43. data/test/paid/util_test.rb +15 -24
  44. data/test/test_data.rb +144 -93
  45. data/test/test_helper.rb +6 -4
  46. metadata +32 -26
  47. data/Gemfile.lock +0 -54
  48. data/README.rdoc +0 -35
  49. data/lib/data/ca-certificates.crt +0 -0
  50. data/lib/paid/alias.rb +0 -16
  51. data/lib/paid/api_operations/create.rb +0 -17
  52. data/lib/paid/api_operations/delete.rb +0 -11
  53. data/lib/paid/api_operations/list.rb +0 -17
  54. data/lib/paid/api_operations/update.rb +0 -57
  55. data/lib/paid/certificate_blacklist.rb +0 -55
  56. data/lib/paid/list_object.rb +0 -37
  57. data/lib/paid/paid_object.rb +0 -187
  58. data/lib/paid/singleton_api_resource.rb +0 -20
  59. data/lib/tasks/paid_tasks.rake +0 -4
  60. data/test/paid/alias_test.rb +0 -22
  61. data/test/paid/certificate_blacklist_test.rb +0 -18
  62. data/test/paid/list_object_test.rb +0 -16
  63. data/test/paid/paid_object_test.rb +0 -27
  64. data/test/paid/properties_test.rb +0 -103
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c131f42824035c881ba3eaf3896d2a8e79aa9a26
4
- data.tar.gz: c2b902c2b3d7e0fba75c67b71ec3cff06db9fc98
3
+ metadata.gz: 19719c911ec26dca4ded160c1bd21384fd783939
4
+ data.tar.gz: 29fe6d251b7c798dcc9d962a9b3cd1f7b9366d0d
5
5
  SHA512:
6
- metadata.gz: 97115b5ea5004e528b213f9fc9800d5adfebe63d6ff7c0ed95f98f06a04d4acc0523067a285707521a6ce19ef365ec38768aed459cfa6da6e37afe61a72dd339
7
- data.tar.gz: b1b15a66a2e5c093635e35d790bc9c801ad03143f595e1d86c512a26f7e88885a6adbeb69731fd1ca5ee5fa630c90f013549ec1d2b3e8398a1cf1f1bed29258c
6
+ metadata.gz: 2cb3e08c5d99736ae837e89231465b8a90f24a2558ef0ebb897bf1cc8d6483a1ad99fcc9a8e6228fd30092c5266d6fe52e817308d2af456354515fe677df89a3
7
+ data.tar.gz: b05a5e6fe08b76c77eae783f9d687bc44fd2c0806c38fcaf5add389ef59ec5d3ff0d989bbf03f84011d9ac6b3cefc6de8c0ccd01713edbff624e4127725fb317
data/.gitignore CHANGED
@@ -1 +1,5 @@
1
- pkg
1
+ pkg
2
+ Gemfile.lock
3
+ *.gem
4
+ test.rb
5
+ .ruby-version
data/.travis.yml ADDED
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - 2.0.0
8
+ - 2.1
9
+ - 2.2
10
+
11
+ gemfile:
12
+ - gemfiles/default-with-activesupport.gemfile
13
+ - gemfiles/json.gemfile
14
+ - gemfiles/yajl.gemfile
15
+
16
+ sudo: false
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 1.0 2015-03-04
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # Paid Ruby bindings ![Travis CI Status](https://travis-ci.org/paidapi/paid-ruby.svg?branch=master)
2
+
3
+
4
+ ## Installation
5
+
6
+ You don't need this source code unless you want to modify the gem. If
7
+ you just want to use the Paid Ruby bindings, you should run:
8
+
9
+ ```bash
10
+ gem install paid
11
+ ```
12
+
13
+ If you want to build the gem from source:
14
+
15
+ ```bash
16
+ gem build paid.gemspec
17
+ ```
18
+
19
+
20
+ ## Requirements
21
+
22
+ * Ruby 1.8.7 or above. (Ruby 1.8.6 may work if you load
23
+ ActiveSupport.) For Ruby versions before 1.9.2, you'll need to add this to your Gemfile:
24
+
25
+ ```ruby
26
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('1.9.2')
27
+ gem 'rest-client', '~> 1.6.8'
28
+ end
29
+ ```
30
+
31
+ * rest-client, json
32
+
33
+
34
+ ## Bundler
35
+
36
+ If you are installing via bundler, you should be sure to use the https
37
+ rubygems source in your Gemfile, as any gems fetched over http could potentially be compromised.
38
+
39
+ ```ruby
40
+ source 'https://rubygems.org'
41
+
42
+ gem 'rails'
43
+ gem 'paid'
44
+ ```
45
+
46
+
47
+ ## Development
48
+
49
+ Test cases can be run with: `bundle exec rake test`
50
+
51
+
52
+ ## Test Rake Task
53
+
54
+ To hit the API with some test calls run:
55
+
56
+ ```bash
57
+ bundle exec rake test_api["sk_test_api_key"]
58
+ ```
data/Rakefile CHANGED
@@ -1,34 +1,14 @@
1
- begin
2
- require 'bundler/setup'
3
- rescue LoadError
4
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
- end
1
+ require 'rake/testtask'
2
+ require './tasks/api_test.rb'
6
3
 
7
- require 'rdoc/task'
4
+ task :default => [:test]
8
5
 
9
- RDoc::Task.new(:rdoc) do |rdoc|
10
- rdoc.rdoc_dir = 'rdoc'
11
- rdoc.title = 'Paid'
12
- rdoc.options << '--line-numbers'
13
- rdoc.rdoc_files.include('README.rdoc')
14
- rdoc.rdoc_files.include('lib/**/*.rb')
6
+ Rake::TestTask.new do |t|
7
+ t.pattern = './test/**/*_test.rb'
15
8
  end
16
9
 
17
-
18
-
19
-
20
-
21
-
22
- Bundler::GemHelper.install_tasks
23
-
24
- require 'rake/testtask'
25
-
26
- Rake::TestTask.new(:test) do |t|
27
- t.libs << 'lib'
28
- t.libs << 'test'
29
- t.pattern = 'test/**/*_test.rb'
30
- t.verbose = false
10
+ task :test_api, [:api_key] do |t, args|
11
+ api_key = args[:api_key]
12
+ api_test = APITest.new(api_key)
13
+ api_test.run
31
14
  end
32
-
33
-
34
- task default: :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/bin/paid-console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
3
+
4
+ libs = " -r irb/completion"
5
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/paid'}"
6
+ puts "Loading paid gem"
7
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+ gemspec :path => File.join(File.dirname(__FILE__), "..")
3
+
4
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('1.9.3')
5
+ gem 'i18n', '< 0.7'
6
+ gem 'rest-client', '~> 1.6.8'
7
+ gem 'activesupport', '~> 3.2'
8
+ else
9
+ gem 'activesupport'
10
+ end
@@ -0,0 +1,12 @@
1
+ source "https://rubygems.org"
2
+ gemspec :path => File.join(File.dirname(__FILE__), "..")
3
+
4
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('1.9.3')
5
+ gem 'i18n', '< 0.7'
6
+ gem 'rest-client', '~> 1.6.8'
7
+ gem 'activesupport', '~> 3.2'
8
+ else
9
+ gem 'activesupport'
10
+ end
11
+
12
+ gem 'json'
@@ -0,0 +1,12 @@
1
+ source "https://rubygems.org"
2
+ gemspec :path => File.join(File.dirname(__FILE__), "..")
3
+
4
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('1.9.3')
5
+ gem 'i18n', '< 0.7'
6
+ gem 'rest-client', '~> 1.6.8'
7
+ gem 'activesupport', '~> 3.2'
8
+ else
9
+ gem 'activesupport'
10
+ end
11
+
12
+ gem 'yajl-ruby'
data/lib/paid.rb CHANGED
@@ -1,35 +1,30 @@
1
1
  # Paid Ruby bindings
2
- # API spec at https://docs.paidapi.com
2
+ # API spec at https://paid.com/docs/api
3
3
  require 'cgi'
4
4
  require 'set'
5
5
  require 'openssl'
6
6
  require 'rest_client'
7
7
  require 'json'
8
+ require 'base64'
8
9
 
9
10
  # Version
10
11
  require 'paid/version'
11
12
 
12
- # API operations
13
- require 'paid/api_operations/create'
14
- require 'paid/api_operations/update'
15
- require 'paid/api_operations/delete'
16
- require 'paid/api_operations/list'
17
-
18
13
  # Resources
19
- require 'paid/util'
20
- require 'paid/paid_object'
14
+ require 'paid/api_class'
21
15
  require 'paid/api_resource'
22
- require 'paid/singleton_api_resource'
23
- require 'paid/list_object'
24
- require 'paid/account'
25
- require 'paid/customer'
26
- require 'paid/certificate_blacklist'
27
- require 'paid/invoice'
16
+ require 'paid/api_singleton'
17
+ require 'paid/api_list'
18
+ require 'paid/util'
19
+
20
+ # Requires for classes
28
21
  require 'paid/transaction'
22
+ require 'paid/invoice'
29
23
  require 'paid/event'
30
- require 'paid/alias'
24
+ require 'paid/customer'
31
25
  require 'paid/plan'
32
26
  require 'paid/subscription'
27
+ require 'paid/account'
33
28
 
34
29
  # Errors
35
30
  require 'paid/errors/paid_error'
@@ -39,114 +34,93 @@ require 'paid/errors/invalid_request_error'
39
34
  require 'paid/errors/authentication_error'
40
35
 
41
36
  module Paid
42
- DEFAULT_CA_BUNDLE_PATH = File.dirname(__FILE__) + '/data/ca-certificates.crt'
43
- @api_base = 'https://api.paidapi.com'
44
-
45
- @ssl_bundle_path = DEFAULT_CA_BUNDLE_PATH
46
- @verify_ssl_certs = false
47
- @CERTIFICATE_VERIFIED = false
48
-
37
+ @api_base = "https://api.paidapi.com"
38
+ @api_key = nil
49
39
 
50
40
  class << self
51
- attr_accessor :api_key, :api_base, :verify_ssl_certs, :api_version
41
+ attr_accessor :api_key, :api_base, :api_test
52
42
  end
53
43
 
54
- def self.api_url(url='', api_base_url=nil)
55
- (api_base_url || @api_base) + url
44
+ def self.api_url(path='')
45
+ "#{@api_base}#{path}"
56
46
  end
57
47
 
58
- def self.request(method, url, api_key, params={}, headers={}, api_base_url=nil)
59
- api_base_url = api_base_url || @api_base
60
-
61
-
62
- unless api_key ||= @api_key
63
- raise AuthenticationError.new('No API key provided. ' +
64
- 'Set your API key using "Paid.api_key = <API-KEY>". ' +
65
- 'You can generate API keys from the Paid web interface. ' +
66
- 'See https://paidapi.com/api for details, or email hello@paidapi.com ' +
67
- 'if you have any questions.')
68
- end
69
-
70
- if api_key =~ /\s/
71
- raise AuthenticationError.new('Your API key is invalid, as it contains ' +
72
- 'whitespace. (HINT: You can double-check your API key from the ' +
73
- 'Paid web interface. See https://paidapi.com/api for details, or ' +
74
- 'email hello@paidapi.com if you have any questions.)')
75
- end
48
+ def self.request(method, path, params={}, headers={})
49
+ verify_api_key(api_key)
76
50
 
51
+ url = api_url(path)
77
52
 
78
53
  request_opts = { :verify_ssl => false }
79
54
 
80
- if ssl_preflight_passed?
81
- request_opts.update(:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
82
- :ssl_ca_file => @ssl_bundle_path)
83
- end
84
-
85
- if @verify_ssl_certs and !@CERTIFICATE_VERIFIED
86
- @CERTIFICATE_VERIFIED = CertificateBlacklist.check_ssl_cert(api_base_url, @ssl_bundle_path)
87
- end
88
-
89
- params = Util.objects_to_ids(params)
90
- url = api_url(url, api_base_url)
91
-
92
- case method.to_s.downcase.to_sym
93
- when :get, :head, :delete
94
- # Make params into GET parameters
95
- url += "#{URI.parse(url).query ? '&' : '?'}#{uri_encode(params)}" if params && params.any?
96
- payload = nil
97
- else
98
- if headers[:content_type] && headers[:content_type] == "multipart/form-data"
99
- payload = params
100
- else
101
- payload = uri_encode(params)
55
+ if [:get, :head, :delete].include?(method.to_s.downcase.to_sym)
56
+ unless params.empty?
57
+ url += URI.parse(url).query ? '&' : '?' + Util.query_string(params)
102
58
  end
59
+ params = nil
103
60
  end
104
61
 
105
- request_opts.update(:headers => request_headers(api_key).update(headers),
106
- :method => method, :open_timeout => 30,
107
- :payload => payload, :url => url, :timeout => 80)
62
+ headers = default_headers.update(basic_auth_headers(api_key)).update(headers)
63
+ request_opts.update(:headers => headers,
64
+ :method => method,
65
+ :open_timeout => 30,
66
+ :payload => params,
67
+ :url => url,
68
+ :timeout => 60)
69
+
108
70
  begin
109
71
  response = execute_request(request_opts)
110
- rescue SocketError => e
111
- handle_restclient_error(e, api_base_url)
112
- rescue NoMethodError => e
113
- # Work around RestClient bug
114
- if e.message =~ /\WRequestFailed\W/
115
- e = APIConnectionError.new('Unexpected HTTP response code')
116
- handle_restclient_error(e, api_base_url)
117
- else
118
- raise
119
- end
120
- rescue RestClient::ExceptionWithResponse => e
121
- if rcode = e.http_code and rbody = e.http_body
122
- handle_api_error(rcode, rbody)
123
- else
124
- handle_restclient_error(e, api_base_url)
125
- end
126
- rescue RestClient::Exception, Errno::ECONNREFUSED => e
127
- handle_restclient_error(e, api_base_url)
128
72
  rescue Exception => e
73
+ handle_request_error(e, url)
129
74
  end
130
75
 
76
+ parse(response)
77
+ end
131
78
 
132
- [parse(response), api_key]
79
+ # Mostly here for stubbing out during tests.
80
+ def self.execute_request(opts)
81
+ RestClient::Request.execute(opts)
133
82
  end
134
83
 
135
- private
84
+ def self.parse(response)
85
+ begin
86
+ json = JSON.parse(response.body)
87
+ rescue JSON::ParserError
88
+ raise APIError.generic(response.code, response.body)
89
+ end
136
90
 
137
- def self.ssl_preflight_passed?
138
- if !verify_ssl_certs && !@no_verify
139
- "Execute 'Paid.verify_ssl_certs = true' to enable verification."
91
+ # TODO(jonclahoun): Remove this when Paid's API returns the correct status code.
92
+ json = Util.symbolize_keys(json)
93
+ if json.has_key?(:error)
94
+ raise PaidError.new(json[:error][:message], response.code, response.body, json)
95
+ end
96
+ json
97
+ end
140
98
 
141
- @no_verify = true
99
+ def self.default_headers
100
+ headers = {
101
+ :user_agent => "Paid/::API_VERSION:: RubyBindings/#{Paid::VERSION}",
102
+ :content_type => 'application/x-www-form-urlencoded'
103
+ }
142
104
 
143
- elsif !Util.file_readable(@ssl_bundle_path) && !@no_bundle
144
- "because #{@ssl_bundle_path} isn't readable"
105
+ begin
106
+ headers.update(:x_paid_client_user_agent => JSON.generate(user_agent))
107
+ rescue => e
108
+ headers.update(:x_paid_client_raw_user_agent => user_agent.inspect,
109
+ :error => "#{e} (#{e.class})")
110
+ end
111
+ headers
112
+ end
145
113
 
146
- @no_bundle = true
114
+ def self.basic_auth_headers(api_key=@api_key)
115
+ api_key ||= @api_key
116
+ unless api_key
117
+ raise ArgumentError.new('No API key provided. Set your API key using "Paid.api_key = <API-KEY>".')
147
118
  end
148
119
 
149
- !(@no_verify || @no_bundle)
120
+ base_64_key = Base64.encode64("#{api_key}:")
121
+ {
122
+ "Authorization" => "Basic #{base_64_key}",
123
+ }
150
124
  end
151
125
 
152
126
  def self.user_agent
@@ -161,7 +135,6 @@ module Paid
161
135
  :publisher => 'paid',
162
136
  :uname => @uname
163
137
  }
164
-
165
138
  end
166
139
 
167
140
  def self.get_uname
@@ -170,95 +143,49 @@ module Paid
170
143
  "uname lookup failed"
171
144
  end
172
145
 
173
- def self.uri_encode(params)
174
- Util.flatten_params(params).
175
- map { |k,v| "#{k}=#{Util.url_encode(v)}" }.join('&')
176
- end
177
-
178
- def self.request_headers(api_key)
179
- headers = {
180
- :user_agent => "Paid/v0 RubyBindings/#{Paid::VERSION}",
181
- :authorization => "Bearer #{api_key}",
182
- :content_type => 'application/x-www-form-urlencoded'
183
- }
184
-
185
- headers[:paid_version] = api_version if api_version
186
-
187
- begin
188
- headers.update(:x_paid_client_user_agent => JSON.generate(user_agent))
189
- rescue => e
190
- headers.update(:x_paid_client_raw_user_agent => user_agent.inspect,
191
- :error => "#{e} (#{e.class})")
146
+ def self.verify_api_key(api_key)
147
+ unless api_key
148
+ raise AuthenticationError.new('No API key provided. ' +
149
+ 'Set your API key using "Paid.api_key = <API-KEY>". ' +
150
+ 'You can generate API keys from the Paid web interface. ' +
151
+ 'See http://docs.paidapi.com/#authentication for details, or email hello@paidapi.com ' +
152
+ 'if you have any questions.')
192
153
  end
193
- end
194
-
195
- def self.execute_request(opts)
196
- RestClient::Request.execute(opts)
197
- end
198
154
 
199
- def self.parse(response)
200
- begin
201
- # Would use :symbolize_names => true, but apparently there is
202
- # some library out there that makes symbolize_names not work.
203
- response = JSON.parse(response.body)
204
- rescue JSON::ParserError
205
- raise general_api_error(response.code, response.body)
155
+ if api_key =~ /\s/
156
+ raise AuthenticationError.new('Your API key is invalid, as it contains ' +
157
+ 'whitespace. (HINT: You can double-check your API key from the ' +
158
+ 'Paid web interface. See http://docs.paidapi.com/#authentication for details, or ' +
159
+ 'email hello@paidapi.com if you have any questions.)')
206
160
  end
207
-
208
- Util.symbolize_names(response)
209
- end
210
-
211
- def self.general_api_error(rcode, rbody)
212
- APIError.new("Invalid response object from API: #{rbody.inspect} " +
213
- "(HTTP response code was #{rcode})", rcode, rbody)
214
161
  end
215
162
 
216
- def self.handle_api_error(rcode, rbody)
217
- begin
218
- error_obj = JSON.parse(rbody)
219
- error_obj = Util.symbolize_names(error_obj)
220
- error = error_obj[:error] or raise PaidError.new # escape from parsing
221
-
222
- rescue JSON::ParserError, PaidError
223
- raise general_api_error(rcode, rbody)
163
+ def self.handle_request_error(error, url)
164
+ # First we see if this is an error with a response, and if it is
165
+ # we check to see if there is an http code and body to work with.
166
+ if error.is_a?(RestClient::ExceptionWithResponse)
167
+ if error.http_code && error.http_body
168
+ handle_api_error(error.http_code, error.http_body)
169
+ end
224
170
  end
225
171
 
226
- case rcode
227
- when 400, 404
228
- raise invalid_request_error error, rcode, rbody, error_obj
229
- when 401
230
- raise authentication_error error, rcode, rbody, error_obj
231
- else
232
- raise api_error error, rcode, rbody, error_obj
233
- end
172
+ # If we got here then the error hasn't been handled yet.
173
+ # Handle it as a connection error.
174
+ handle_connection_error(error, url)
234
175
 
176
+ # Finally if we get here we don't know what type of error it is, so just raise it.
177
+ raise error
235
178
  end
236
179
 
237
- def self.invalid_request_error(error, rcode, rbody, error_obj)
238
- InvalidRequestError.new(error[:message], error[:param], rcode,
239
- rbody, error_obj)
240
- end
180
+ def self.handle_connection_error(error, url)
181
+ message = "An error occurred while connecting to Paid at #{url}."
241
182
 
242
- def self.authentication_error(error, rcode, rbody, error_obj)
243
- AuthenticationError.new(error[:message], rcode, rbody, error_obj)
244
- end
245
-
246
- def self.api_error(error, rcode, rbody, error_obj)
247
- APIError.new(error[:message], rcode, rbody, error_obj)
248
- end
249
-
250
- def self.handle_restclient_error(e, api_base_url=nil)
251
- api_base_url = @api_base unless api_base_url
252
- connection_message = "Please check your internet connection and try again. " \
253
- "If this problem persists, you should check Paid's service status at " \
254
- "https://twitter.com/paidstatus, or let us know at hello@paidapi.com."
255
-
256
- case e
183
+ case error
257
184
  when RestClient::RequestTimeout
258
- message = "Could not connect to Paid (#{api_base_url}). #{connection_message}"
185
+ message += connection_message
259
186
 
260
187
  when RestClient::ServerBrokeConnection
261
- message = "The connection to the server (#{api_base_url}) broke before the " \
188
+ message = "The connection to the server at (#{url}) broke before the " \
262
189
  "request completed. #{connection_message}"
263
190
 
264
191
  when RestClient::SSLCertificateNotVerified
@@ -268,16 +195,41 @@ module Paid
268
195
  "If this problem persists, let us know at hello@paidapi.com."
269
196
 
270
197
  when SocketError
271
- message = "Unexpected error communicating when trying to connect to Paid. " \
198
+ message = "Unexpected error when trying to connect to Paid. " \
272
199
  "You may be seeing this message because your DNS is not working. " \
273
- "To check, try running 'host paidapi.com' from the command line."
200
+ "To check, try running 'host api.paidapi.com' from the command line."
274
201
 
275
202
  else
276
203
  message = "Unexpected error communicating with Paid. " \
277
- "If this problem persists, let us know at hello@paidapi.com."
204
+ "If this problem persists, let us know at hello@paidapi.com. #{connection_message}"
205
+ end
278
206
 
207
+ raise APIConnectionError.new(message + "\n\n(Network error: #{error.message}")
208
+ end
209
+
210
+ def self.connection_message
211
+ "Please check your internet connection and try again. " \
212
+ "If this problem persists, you should check Paid's service status at " \
213
+ "https://twitter.com/paidstatus, or let us know at hello@paidapi.com."
214
+ end
215
+
216
+ def self.handle_api_error(rcode, rbody)
217
+ begin
218
+ error_obj = JSON.parse(rbody)
219
+ rescue JSON::ParserError
220
+ raise APIError.generic(rcode, rbody)
279
221
  end
222
+ error_obj = Util.symbolize_keys(error_obj)
223
+ raise APIError.generic(rcode, rbody) unless error = error_obj[:error]
280
224
 
281
- raise APIConnectionError.new(message + "\n\n(Network error: #{e.message})")
225
+ case rcode
226
+ when 400, 404
227
+ raise InvalidRequestError.new(error[:message], error[:param], rcode, rbody, error_obj)
228
+ when 401
229
+ raise AuthenticationError.new(error[:message], rcode, rbody, error_obj)
230
+ else
231
+ raise APIError.new(error[:message], rcode, rbody, error_obj)
232
+ end
282
233
  end
234
+
283
235
  end