gocoin 0.1.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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/LICENSE +191 -0
  4. data/README.md +242 -0
  5. data/Rakefile +7 -0
  6. data/VERSION +1 -0
  7. data/history.md +21 -0
  8. data/lib/gocoin.rb +27 -0
  9. data/lib/gocoin/api.rb +35 -0
  10. data/lib/gocoin/api/accounts.rb +16 -0
  11. data/lib/gocoin/api/invoices.rb +33 -0
  12. data/lib/gocoin/api/merchant.rb +38 -0
  13. data/lib/gocoin/api/merchants/currencies.rb +35 -0
  14. data/lib/gocoin/api/merchants/currency_conversions.rb +35 -0
  15. data/lib/gocoin/api/merchants/payouts.rb +35 -0
  16. data/lib/gocoin/api/user.rb +43 -0
  17. data/lib/gocoin/auth.rb +51 -0
  18. data/lib/gocoin/client.rb +201 -0
  19. data/lib/gocoin/errors/api_connection_error.rb +4 -0
  20. data/lib/gocoin/errors/api_error.rb +4 -0
  21. data/lib/gocoin/errors/authentication_error.rb +4 -0
  22. data/lib/gocoin/errors/gocoin_error.rb +20 -0
  23. data/lib/gocoin/errors/invalid_request_error.rb +10 -0
  24. data/lib/gocoin/util.rb +29 -0
  25. data/lib/gocoin/version.rb +3 -0
  26. data/lib/gocoin/xrate.rb +28 -0
  27. data/spec/gocoin/api/accounts_spec.rb +32 -0
  28. data/spec/gocoin/api/invoices_spec.rb +67 -0
  29. data/spec/gocoin/api/merchant_spec.rb +66 -0
  30. data/spec/gocoin/api/merchants/currencies_spec.rb +64 -0
  31. data/spec/gocoin/api/merchants/currency_conversions_spec.rb +63 -0
  32. data/spec/gocoin/api/merchants/payouts_spec.rb +63 -0
  33. data/spec/gocoin/api/user_spec.rb +88 -0
  34. data/spec/gocoin/api_spec.rb +19 -0
  35. data/spec/gocoin/auth_spec.rb +40 -0
  36. data/spec/gocoin/client_spec.rb +139 -0
  37. data/spec/gocoin/xrate_spec.rb +37 -0
  38. data/spec/spec_helper.rb +6 -0
  39. metadata +136 -0
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.2
@@ -0,0 +1,21 @@
1
+ ###Changelog
2
+
3
+ ###### v0.1.1:
4
+ Initial release: <br>
5
+ Selected methods for User, Merchant, and Invoices
6
+
7
+ ###### v0.1.2:
8
+ Decapitalized all instances of gocoin (except class name Gocoin). <br>
9
+ This includes the gem name itself
10
+
11
+ Added methods:
12
+ * client.accounts.transactions
13
+ * client.merchant.currencies.get
14
+ * client.merchant.currencies.list
15
+ * client.merchant.currencies.update
16
+ * client.merchant.currency_conversions.get
17
+ * client.merchant.currency_conversions.list
18
+ * client.merchant.currency_conversions.request
19
+ * client.merchant.payouts.get
20
+ * client.merchant.payouts.list
21
+ * client.merchant.payouts.request
@@ -0,0 +1,27 @@
1
+ require 'cgi'
2
+ require 'json'
3
+ require 'socket'
4
+ require 'rest_client'
5
+ require 'logger'
6
+
7
+ require 'gocoin/version'
8
+ require 'gocoin/util'
9
+
10
+ require 'gocoin/errors/gocoin_error'
11
+ require 'gocoin/errors/api_connection_error'
12
+ require 'gocoin/errors/api_error'
13
+ require 'gocoin/errors/authentication_error'
14
+ require 'gocoin/errors/invalid_request_error'
15
+
16
+ require 'gocoin/client'
17
+ require 'gocoin/auth'
18
+ require 'gocoin/api'
19
+ require 'gocoin/xrate'
20
+
21
+ require 'gocoin/api/invoices'
22
+ require 'gocoin/api/merchant'
23
+ require 'gocoin/api/user'
24
+ require 'gocoin/api/accounts'
25
+ require 'gocoin/api/merchants/currencies'
26
+ require 'gocoin/api/merchants/currency_conversions'
27
+ require 'gocoin/api/merchants/payouts'
@@ -0,0 +1,35 @@
1
+ module Gocoin
2
+ class API
3
+
4
+ attr_reader :client, :user, :merchant, :invoices, :accounts
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ @accounts = Gocoin::Accounts.new(self)
9
+ @invoices = Gocoin::Invoices.new(self)
10
+ @merchant = Gocoin::Merchant.new(self)
11
+ @user = Gocoin::User.new(self)
12
+ end
13
+
14
+ def request(route, options = {})
15
+ @client.logger.debug 'Gocoin::API#request called.'
16
+ raise 'Gocoin::API#request: Options is not a hash object' unless options.nil? || options.kind_of?(Hash)
17
+ raise 'Gocoin::API#request: API not ready. Token was not defined' unless @client.token
18
+
19
+ headers = options[:headers] ? @client.headers.merge(options[:headers]) : @client.headers.dup
20
+ headers['Authorization'] = "Bearer #{@client.token}"
21
+
22
+ options = @client.options.merge(options)
23
+ payload = options[:payload] ? options[:payload].to_json : nil
24
+
25
+ config = {
26
+ url: "#{@client.http_prefix(options[:secure])}#{options[:host]}#{@client.port}#{options[:path]}/#{options[:api_version]}#{route}",
27
+ method: options[:method],
28
+ headers: headers,
29
+ }
30
+ config[:payload] = options[:payload].to_json if options[:payload]
31
+ @client.raw_request config
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,16 @@
1
+ module Gocoin
2
+ class Accounts
3
+
4
+ def initialize(api)
5
+ @api = api
6
+ end
7
+
8
+ def transactions(account_id, params = {})
9
+ @api.client.logger.debug 'Gocoin::Accounts#transactions called.'
10
+ route = "/accounts/#{account_id}/transactions?#{Util.hash_to_url_params(params)}"
11
+ options = {}
12
+ @api.request route, options
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ module Gocoin
2
+ class Invoices
3
+
4
+ def initialize(api)
5
+ @api = api
6
+ end
7
+
8
+ def create(merchant_id, params)
9
+ @api.client.logger.debug 'Gocoin::Invoices#create called.'
10
+ route = "/merchants/#{merchant_id}/invoices"
11
+ options = {
12
+ method: 'POST',
13
+ payload: params
14
+ }
15
+ @api.request route, options
16
+ end
17
+
18
+ def get(id)
19
+ @api.client.logger.debug 'Gocoin::Invoices#get called.'
20
+ route = "/invoices/#{id}"
21
+ options = {}
22
+ @api.request route, options
23
+ end
24
+
25
+ def search(params = {})
26
+ @api.client.logger.debug 'Gocoin::Invoices#search called.'
27
+ route = "/invoices/search?#{Util.hash_to_url_params(params)}"
28
+ options = {}
29
+ @api.request route, options
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ module Gocoin
2
+ class Merchant
3
+
4
+ attr_reader :currencies, :currency_conversions, :payouts
5
+
6
+ def initialize(api)
7
+ @api = api
8
+ @currencies = Gocoin::Merchants::Currencies.new(api)
9
+ @currency_conversions = Gocoin::Merchants::CurrencyConversions.new(api)
10
+ @payouts = Gocoin::Merchants::Payouts.new(api)
11
+ end
12
+
13
+ def get(id)
14
+ @api.client.logger.debug 'Gocoin::Merchant#get called.'
15
+ route = "/merchants/#{id}"
16
+ options = {}
17
+ @api.request route, options
18
+ end
19
+
20
+ def update(id, params)
21
+ @api.client.logger.debug 'Gocoin::Merchant#update called.'
22
+ route = "/merchants/#{id}"
23
+ options = {
24
+ method: 'PATCH',
25
+ payload: params
26
+ }
27
+ @api.request route, options
28
+ end
29
+
30
+ def accounts(id)
31
+ @api.client.logger.debug 'Gocoin::Merchant#accounts called.'
32
+ route = "/merchants/#{id}/accounts"
33
+ options = {}
34
+ @api.request route, options
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ module Gocoin
2
+ module Merchants
3
+ class Currencies
4
+
5
+ def initialize(api)
6
+ @api = api
7
+ end
8
+
9
+ def get(merchant_id, currency_code)
10
+ @api.client.logger.debug 'Gocoin::Merchants::Currencies#get called.'
11
+ route = "/merchants/#{merchant_id}/currencies/#{currency_code}"
12
+ options = {}
13
+ @api.request route, options
14
+ end
15
+
16
+ def list(merchant_id)
17
+ @api.client.logger.debug 'Gocoin::Merchants::Currencies#list called.'
18
+ route = "/merchants/#{merchant_id}/currencies"
19
+ options = {}
20
+ @api.request route, options
21
+ end
22
+
23
+ def update(merchant_id, currency_code, params)
24
+ @api.client.logger.debug 'Gocoin::Merchants::Currencies#update called.'
25
+ route = "/merchants/#{merchant_id}/currencies/#{currency_code}"
26
+ options = {
27
+ method: 'PATCH',
28
+ payload: params
29
+ }
30
+ @api.request route, options
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ module Gocoin
2
+ module Merchants
3
+ class CurrencyConversions
4
+
5
+ def initialize(api)
6
+ @api = api
7
+ end
8
+
9
+ def get(merchant_id, currency_conversion_id)
10
+ @api.client.logger.debug 'Gocoin::Merchants::CurrencyConversion#get called.'
11
+ route = "/merchants/#{merchant_id}/currency_conversions/#{currency_conversion_id}"
12
+ options = {}
13
+ @api.request route, options
14
+ end
15
+
16
+ def list(merchant_id)
17
+ @api.client.logger.debug 'Gocoin::Merchants::CurrencyConversion#list called.'
18
+ route = "/merchants/#{merchant_id}/currency_conversions"
19
+ options = {}
20
+ @api.request route, options
21
+ end
22
+
23
+ def request(merchant_id, params)
24
+ @api.client.logger.debug 'Gocoin::Merchants::CurrencyConversion#request called.'
25
+ route = "/merchants/#{merchant_id}/currency_conversions"
26
+ options = {
27
+ method: 'POST',
28
+ payload: params
29
+ }
30
+ @api.request route, options
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ module Gocoin
2
+ module Merchants
3
+ class Payouts
4
+
5
+ def initialize(api)
6
+ @api = api
7
+ end
8
+
9
+ def get(merchant_id, payout_id)
10
+ @api.client.logger.debug 'Gocoin::Merchants::Payouts#get called.'
11
+ route = "/merchants/#{merchant_id}/payouts/#{payout_id}"
12
+ options = {}
13
+ @api.request route, options
14
+ end
15
+
16
+ def list(merchant_id)
17
+ @api.client.logger.debug 'Gocoin::Merchants::Payouts#list called.'
18
+ route = "/merchants/#{merchant_id}/payouts"
19
+ options = {}
20
+ @api.request route, options
21
+ end
22
+
23
+ def request(merchant_id, params)
24
+ @api.client.logger.debug 'Gocoin::Merchants::Payouts#request called.'
25
+ route = "/merchants/#{merchant_id}/payouts"
26
+ options = {
27
+ method: 'POST',
28
+ payload: params
29
+ }
30
+ @api.request route, options
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,43 @@
1
+ module Gocoin
2
+ class User
3
+
4
+ def initialize(api)
5
+ @api = api
6
+ end
7
+
8
+ def get(id)
9
+ @api.client.logger.debug 'Gocoin::User#get called.'
10
+ route = "/users/#{id}"
11
+ options = {}
12
+ @api.request route, options
13
+ end
14
+
15
+ def self
16
+ @api.client.logger.debug 'Gocoin::User#self called.'
17
+ route = "/user"
18
+ options = {}
19
+ @api.request route, options
20
+ end
21
+
22
+ def update(id, params)
23
+ @api.client.logger.debug 'Gocoin::User#update called.'
24
+ route = "/users/#{id}"
25
+ options = {
26
+ method: 'PATCH',
27
+ payload: params
28
+ }
29
+ @api.request route, options
30
+ end
31
+
32
+ def update_password(id, params)
33
+ @api.client.logger.debug 'Gocoin::User#update_password called.'
34
+ route = "/users/#{id}/password"
35
+ options = {
36
+ method: 'PATCH',
37
+ payload: params
38
+ }
39
+ @api.request route, options
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,51 @@
1
+ module Gocoin
2
+ class Auth
3
+
4
+ REQUIRED_CODE_PARAMS = %w[grant_type client_id client_secret code redirect_uri]
5
+
6
+ attr_reader :client
7
+
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ def authenticate(options = {})
13
+ @client.logger.debug 'Gocoin::Auth#authenticate method called.'
14
+
15
+ headers = @client.headers.merge(options[:headers] || {})
16
+ options = @client.options.merge options
17
+ verify_required_params options
18
+ route = '/oauth/token'
19
+
20
+ config = {
21
+ url: "#{@client.http_prefix}#{@client.options[:host]}#{@client.port}#{@client.options[:path]}/#{@client.options[:api_version]}#{route}",
22
+ method: 'POST',
23
+ headers: headers,
24
+ payload: options.to_json
25
+ }
26
+
27
+ @client.raw_request config
28
+ end
29
+
30
+ def construct_code_url(params)
31
+ "https://#{@client.options[:dashboard_host]}/auth?#{Util.hash_to_url_params(params)}"
32
+ end
33
+
34
+ private
35
+
36
+ def verify_required_params(options)
37
+ if options[:grant_type] == 'authorization_code'
38
+ required = REQUIRED_CODE_PARAMS
39
+ else
40
+ raise 'Gocoin::Auth#authenticate: grant_type was not defined properly or is unsupported'
41
+ end
42
+
43
+ @client.logger.debug "Required params: #{required}"
44
+ required.each do |required_attribute|
45
+ raise "Gocoin::Auth#authenticate requires '#{required_attribute}' option." unless options[required_attribute.to_sym]
46
+ end
47
+ options
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,201 @@
1
+ module Gocoin
2
+ class Client
3
+
4
+ attr_reader :api, :auth, :xrate, :user, :merchant, :accounts, :invoices, :apps, :headers, :options, :logger
5
+ attr_accessor :token
6
+
7
+ def initialize(options = {})
8
+ @defaults = {
9
+ client_id: nil,
10
+ client_secret: nil,
11
+ host: 'api.gocoin.com',
12
+ port: nil,
13
+ path: '/api',
14
+ api_version: 'v1',
15
+ secure: true,
16
+ method: 'GET',
17
+ headers: nil,
18
+ grant_type: 'authorization_code',
19
+ request_id: nil,
20
+ dashboard_host: 'dashboard.gocoin.com',
21
+ xrate_host: 'x.g0cn.com',
22
+ log_file: nil
23
+ }
24
+
25
+ @default_headers = {
26
+ 'Content-Type' => 'application/json'
27
+ }
28
+
29
+ @options = @defaults.merge options
30
+ @logger = Logger.new(@options[:log_file] || STDOUT)
31
+ @headers = @options[:headers] || @default_headers
32
+ @headers['X-Request-Id'] = @options[:request_id] if @options[:request_id]
33
+
34
+ @auth = Gocoin::Auth.new(self)
35
+ @api = Gocoin::API.new(self)
36
+ @xrate = Gocoin::Xrate.new(self)
37
+
38
+ @accounts = @api.accounts
39
+ @invoices = @api.invoices
40
+ @merchant = @api.merchant
41
+ @user = @api.user
42
+
43
+ @options[:secure] = false if @options[:secure] == 'false'
44
+ end
45
+
46
+ def authenticate(options)
47
+ @logger.debug 'Gocoin::Client#authenticate method called.'
48
+ @auth.authenticate options
49
+ end
50
+
51
+ def http_prefix(secure = true)
52
+ secure ? 'https://' : 'http://'
53
+ end
54
+
55
+ def port
56
+ @options[:port] ? ":#{@options[:port]}" : nil
57
+ end
58
+
59
+ def raw_request(config)
60
+ unless config[:payload].nil?
61
+ config[:headers]['Content-Length'] = config[:payload].length
62
+ end
63
+
64
+ log_config = config.dup
65
+ log_config[:headers] = config[:headers].dup
66
+ @logger.debug 'Raw request made' + strip_secure_from_raw(log_config).to_s
67
+
68
+ error_response = nil
69
+ begin
70
+ response = RestClient::Request.execute(
71
+ config.merge(
72
+ open_timeout: 30,
73
+ timeout: 80
74
+ )
75
+ )
76
+ rescue SocketError => error_response
77
+ handle_restclient_error error_response
78
+ rescue NoMethodError => error_response
79
+ if error_response.message =~ /\WRequestFailed\W/
80
+ error_response = APIConnectionError.new('Unexpected HTTP response code')
81
+ handle_restclient_error error_response
82
+ else
83
+ raise
84
+ end
85
+ rescue RestClient::ExceptionWithResponse => error_response
86
+ if rcode = error_response.http_code and rbody = error_response.http_body
87
+ handle_api_error rcode, rbody
88
+ else
89
+ handle_restclient_error error_response
90
+ end
91
+ rescue RestClient::Exception, Errno::ECONNREFUSED => error_response
92
+ handle_restclient_error error_response
93
+ end
94
+
95
+ return parse_error(error_response) if error_response
96
+ parse response
97
+ end
98
+
99
+ private
100
+
101
+ def strip_secure_from_raw(obj)
102
+ strippable = %w[client_secret password current_password password_confirmation]
103
+ if obj[:payload]
104
+ obj[:payload] = JSON.parse obj[:payload]
105
+ strippable.each do |k|
106
+ obj[:payload][k] = "<#{k}>" if obj[:payload][k]
107
+ end
108
+ end
109
+ obj[:headers]['Authorization'] = '<bearer>' if obj[:headers]['Authorization']
110
+
111
+ obj
112
+ end
113
+
114
+ def parse(response)
115
+ begin
116
+ response = JSON.parse response.body
117
+ rescue JSON::ParserError
118
+ raise general_api_error response.code, response.body
119
+ end
120
+
121
+ Util.symbolize_names response
122
+ end
123
+
124
+ def parse_error(error_response)
125
+ begin
126
+ response = JSON.parse error_response.http_body
127
+ rescue JSON::ParserError
128
+ raise general_api_error error_response.http_code, error_response.error_body
129
+ end
130
+
131
+ response[:status] = error_response.http_code
132
+
133
+ Util.symbolize_names response
134
+ end
135
+
136
+ def general_api_error(rcode, rbody)
137
+ APIError.new( "Invalid response object from API: #{rbody.inspect} " +
138
+ "(HTTP response code was #{rcode}", rcode, rbody)
139
+ end
140
+
141
+ def handle_api_error(rcode, rbody)
142
+ begin
143
+ error_obj = JSON.parse rbody
144
+ error_obj = Util.symbolize_names(error_obj)
145
+ error = error_obj[:error_description] or error_obj[:errors] or raise GocoinError.new
146
+ rescue JSON::ParserError, GocoinError
147
+ raise general_api_error(rcode, rbody)
148
+ end
149
+
150
+ case rcode
151
+ when 400, 404
152
+ raise invalid_request_error error, rcode, rbody, error_obj
153
+ when 401
154
+ raise authentication_error error, rcode, rbody, error_obj
155
+ when 422 # Don't raise an error. Pass back a JSON object
156
+ return
157
+ else
158
+ raise api_error error, rcode, rbody, error_obj
159
+ end
160
+ end
161
+
162
+ def invalid_request_error(error, rcode, rbody, error_obj)
163
+ InvalidRequestError.new(error, rcode, rbody, error_obj)
164
+ end
165
+
166
+ def authentication_error(error, rcode, rbody, error_obj)
167
+ AuthenticationError.new(error, rbody, error_obj)
168
+ end
169
+
170
+ def api_error(error, rcode, rbody, error_obj)
171
+ APIError.new(error, rcode, rbody, error_obj)
172
+ end
173
+
174
+ def handle_restclient_error(e)
175
+ case e
176
+ when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
177
+ message = "Could not connect to GoCoin (#{@options[:host]}). " +
178
+ "Please check your internet connection and try again. " +
179
+ "If this problem persists, let us know at tech@gocoin.com."
180
+
181
+ when RestClient::SSLCertificateNotVerified
182
+ message = "Could not verify GoCoin's SSL certificate. " +
183
+ "Please make sure that your network is not intercepting certificates. " +
184
+ "(Try going to https://#{options[:host]}/#{options[:api_version]} in your browser.) " +
185
+ "If this problem persists, let us know at tech@gocoin.com."
186
+
187
+ when SocketError
188
+ message = "Unexpected error communicating when trying to connect to GoCoin. " +
189
+ "You may be seeing this message because your DNS is not working. " +
190
+ "To check, try running 'host gocoin.com' from the command line."
191
+
192
+ else
193
+ message = "Unexpected error communicating with GoCoin. " +
194
+ "If this problem persists, let us know at tech@gocoin.com."
195
+ end
196
+
197
+ raise APIConnectionError.new(message + "\n\n(Network error: #{e.message})")
198
+ end
199
+
200
+ end
201
+ end