cwallet-ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/cwallet-ruby.gemspec +29 -0
- data/lib/cwallet/wallet.rb +21 -0
- data/lib/cwallet/wallet/.DS_Store +0 -0
- data/lib/cwallet/wallet/adapters/em_http.rb +78 -0
- data/lib/cwallet/wallet/adapters/net_http.rb +66 -0
- data/lib/cwallet/wallet/api_client.rb +734 -0
- data/lib/cwallet/wallet/api_errors.rb +120 -0
- data/lib/cwallet/wallet/api_response.rb +41 -0
- data/lib/cwallet/wallet/client.rb +101 -0
- data/lib/cwallet/wallet/cpub.pub +9 -0
- data/lib/cwallet/wallet/models/account.rb +193 -0
- data/lib/cwallet/wallet/models/address.rb +12 -0
- data/lib/cwallet/wallet/models/api_object.rb +46 -0
- data/lib/cwallet/wallet/models/transaction.rb +20 -0
- data/lib/cwallet/wallet/version.rb +5 -0
- metadata +125 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
module Cwallet
|
2
|
+
module Wallet
|
3
|
+
def self.format_error(resp)
|
4
|
+
error = resp.body && (resp.body['errors'] || resp.body['warnings']).first
|
5
|
+
return resp.body unless error
|
6
|
+
message = error['message']
|
7
|
+
message += " (#{error['url']})" if error["url"]
|
8
|
+
message
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.check_response_status(resp)
|
12
|
+
(resp.body['warnings'] || []).each do |warning|
|
13
|
+
message = "WARNING: #{warning['message']}"
|
14
|
+
message += " (#{warning['url']})" if warning["url"]
|
15
|
+
$stderr.puts message
|
16
|
+
end
|
17
|
+
|
18
|
+
# OAuth2 errors
|
19
|
+
if resp.status >= 400 && resp.body['error']
|
20
|
+
raise APIError, resp.body['error_description']
|
21
|
+
end
|
22
|
+
|
23
|
+
# Regular errors
|
24
|
+
if resp.body['errors']
|
25
|
+
case resp.status
|
26
|
+
when 400
|
27
|
+
case resp.body['errors'].first['id']
|
28
|
+
when 'param_required' then raise ParamRequiredError, format_error(resp)
|
29
|
+
when 'invalid_request' then raise InvalidRequestError, format_error(resp)
|
30
|
+
when 'personal_details_required' then raise PersonalDetailsRequiredError, format_error(resp)
|
31
|
+
end
|
32
|
+
raise BadRequestError, format_error(resp)
|
33
|
+
when 401
|
34
|
+
case resp.body['errors'].first['id']
|
35
|
+
when 'authentication_error' then raise AuthenticationError, format_error(resp)
|
36
|
+
when 'unverified_email' then raise UnverifiedEmailError, format_error(resp)
|
37
|
+
when 'invalid_token' then raise InvalidTokenError, format_error(resp)
|
38
|
+
when 'revoked_token' then raise RevokedTokenError, format_error(resp)
|
39
|
+
when 'expired_token' then raise ExpiredTokenError, format_error(resp)
|
40
|
+
end
|
41
|
+
raise AuthenticationError, format_error(resp)
|
42
|
+
when 402 then raise TwoFactorRequiredError, format_error(resp)
|
43
|
+
when 403 then raise InvalidScopeError, format_error(resp)
|
44
|
+
when 404 then raise NotFoundError, format_error(resp)
|
45
|
+
when 422 then raise ValidationError, format_error(resp)
|
46
|
+
when 429 then raise RateLimitError, format_error(resp)
|
47
|
+
when 500 then raise InternalServerError, format_error(resp)
|
48
|
+
when 503 then raise ServiceUnavailableError, format_error(resp)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
if resp.status > 400
|
53
|
+
raise APIError, "[#{resp.status}] #{resp.body}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Rest API Errors
|
59
|
+
#
|
60
|
+
class APIError < RuntimeError
|
61
|
+
end
|
62
|
+
|
63
|
+
# Status 400
|
64
|
+
class BadRequestError < APIError
|
65
|
+
end
|
66
|
+
|
67
|
+
class ParamRequiredError < APIError
|
68
|
+
end
|
69
|
+
|
70
|
+
class InvalidRequestError < APIError
|
71
|
+
end
|
72
|
+
|
73
|
+
class PersonalDetailsRequiredError < APIError
|
74
|
+
end
|
75
|
+
|
76
|
+
# Status 401
|
77
|
+
class AuthenticationError < APIError
|
78
|
+
end
|
79
|
+
|
80
|
+
class UnverifiedEmailError < APIError
|
81
|
+
end
|
82
|
+
|
83
|
+
class InvalidTokenError < APIError
|
84
|
+
end
|
85
|
+
|
86
|
+
class RevokedTokenError < APIError
|
87
|
+
end
|
88
|
+
|
89
|
+
class ExpiredTokenError < APIError
|
90
|
+
end
|
91
|
+
|
92
|
+
# Status 402
|
93
|
+
class TwoFactorRequiredError < APIError
|
94
|
+
end
|
95
|
+
|
96
|
+
# Status 403
|
97
|
+
class InvalidScopeError < APIError
|
98
|
+
end
|
99
|
+
|
100
|
+
# Status 404
|
101
|
+
class NotFoundError < APIError
|
102
|
+
end
|
103
|
+
|
104
|
+
# Status 422
|
105
|
+
class ValidationError < APIError
|
106
|
+
end
|
107
|
+
|
108
|
+
# Status 429
|
109
|
+
class RateLimitError < APIError
|
110
|
+
end
|
111
|
+
|
112
|
+
# Status 500
|
113
|
+
class InternalServerError < APIError
|
114
|
+
end
|
115
|
+
|
116
|
+
# Status 503
|
117
|
+
class ServiceUnavailableError < APIError
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Cwallet
|
2
|
+
module Wallet
|
3
|
+
# Encapsulate data for an API response
|
4
|
+
class APIResponse
|
5
|
+
attr_reader :received_at
|
6
|
+
attr_accessor :client
|
7
|
+
attr_accessor :method
|
8
|
+
attr_accessor :params
|
9
|
+
|
10
|
+
def initialize(resp)
|
11
|
+
@received_at = Time.now
|
12
|
+
@response = resp
|
13
|
+
end
|
14
|
+
|
15
|
+
def raw
|
16
|
+
@response
|
17
|
+
end
|
18
|
+
|
19
|
+
def body
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
alias_method :data, :body
|
23
|
+
|
24
|
+
def body=(body)
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
def headers
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
def status
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def has_more?
|
37
|
+
body.has_key?('pagination') && body['pagination']['next_uri'] != nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Cwallet
|
2
|
+
module Wallet
|
3
|
+
BASE_API_URL = 'https://stg-wallet.fiahub.com'.freeze
|
4
|
+
API_VERSION = '2018-10-10'.freeze
|
5
|
+
|
6
|
+
class Client < NetHTTPClient
|
7
|
+
def initialize(options={})
|
8
|
+
[ :api_key, :api_secret ].each do |opt|
|
9
|
+
raise unless options.has_key? opt
|
10
|
+
end
|
11
|
+
@api_key = options[:api_key]
|
12
|
+
@api_secret = options[:api_secret]
|
13
|
+
@api_uri = URI.parse(options[:api_url] || BASE_API_URL)
|
14
|
+
super(@api_uri, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def auth_headers(method, path, body)
|
18
|
+
ts = Time.now.to_i.to_s
|
19
|
+
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'),
|
20
|
+
@api_secret,
|
21
|
+
ts + method + path + body.to_s)
|
22
|
+
{ 'CB-ACCESS-KEY' => @api_key,
|
23
|
+
'CB-ACCESS-SIGN' => signature,
|
24
|
+
'CB-ACCESS-TIMESTAMP' => ts,
|
25
|
+
'CB-VERSION' => API_VERSION }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class OAuthClient < NetHTTPClient
|
30
|
+
attr_accessor :access_token, :refresh_token
|
31
|
+
|
32
|
+
def initialize(options={})
|
33
|
+
raise unless options.has_key? :access_token
|
34
|
+
@access_token = options[:access_token]
|
35
|
+
@refresh_token = options[:refresh_token]
|
36
|
+
@oauth_uri = URI.parse(options[:api_url] || BASE_API_URL)
|
37
|
+
super(@oauth_uri, options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def auth_headers(method, path, body)
|
41
|
+
{ 'Authorization' => "Bearer #{@access_token}",
|
42
|
+
'CB-VERSION' => API_VERSION }
|
43
|
+
end
|
44
|
+
|
45
|
+
def authorize!(redirect_url, params = {})
|
46
|
+
raise NotImplementedError
|
47
|
+
end
|
48
|
+
|
49
|
+
def revoke!(params = {})
|
50
|
+
params[:token] ||= @access_token
|
51
|
+
|
52
|
+
out = nil
|
53
|
+
post("/oauth/revoke", params) do |resp|
|
54
|
+
out = APIObject.new(self, resp.body)
|
55
|
+
yield(out, resp) if block_given?
|
56
|
+
end
|
57
|
+
out
|
58
|
+
end
|
59
|
+
|
60
|
+
def refresh!(params = {})
|
61
|
+
params[:grant_type] = 'refresh_token'
|
62
|
+
params[:refresh_token] ||= @refresh_token
|
63
|
+
|
64
|
+
raise "Missing Parameter: refresh_token" unless params.has_key?(:refresh_token)
|
65
|
+
|
66
|
+
out = nil
|
67
|
+
post("/oauth/token", params) do |resp|
|
68
|
+
out = APIObject.new(self, resp.body)
|
69
|
+
# Update tokens to current instance
|
70
|
+
# Developer should always persist them
|
71
|
+
@access_token = out.access_token
|
72
|
+
@refresh_token = out.refresh_token
|
73
|
+
yield(out, resp) if block_given?
|
74
|
+
end
|
75
|
+
out
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class AsyncClient < EMHTTPClient
|
80
|
+
def initialize(options={})
|
81
|
+
[ :api_key, :api_secret ].each do |opt|
|
82
|
+
raise unless options.has_key? opt
|
83
|
+
end
|
84
|
+
@api_key = options[:api_key]
|
85
|
+
@api_secret = options[:api_secret]
|
86
|
+
@api_uri = URI.parse(options[:api_url] || BASE_API_URL)
|
87
|
+
end
|
88
|
+
|
89
|
+
def auth_headers(method, path, body)
|
90
|
+
ts = Time.now.to_i.to_s
|
91
|
+
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'),
|
92
|
+
@api_secret,
|
93
|
+
ts + method + path + body.to_s)
|
94
|
+
{ 'CB-ACCESS-KEY' => @api_key,
|
95
|
+
'CB-ACCESS-SIGN' => signature,
|
96
|
+
'CB-ACCESS-TIMESTAMP' => ts,
|
97
|
+
'CB-VERSION' => API_VERSION }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
-----BEGIN PUBLIC KEY-----
|
2
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi/BwkgBBZESQNFEAuuFN
|
3
|
+
M+entJ+ULJAcBi5RNyU36Si4VjsLIdIiVDGso7tOgPVb0aCzjJdIGeVAxxaUJJzS
|
4
|
+
Jgf6/a3T4F8ZEvf0OndoxcHe6M9hh4O+ImcWbTL9VRlflSQ3Ebu8ZDvWYjsQf7xR
|
5
|
+
1mmk6QAPh294coCCCFNgMOcQsg/gNETD65OddQbwU+GRPjcM4qI5HvcTPYP+uiVT
|
6
|
+
rCeJh7mzDdFgJ/CqtSghAxiriQeTZTJ3FyRmAQU35+Ih0T7/JWVLeY6a1bTTf3k/
|
7
|
+
Z58l0eAhlJs5JBPuaVY5JFB4CfeAecAvBXXn/4SJ25MnFcGP4UcdKZwk/m/UjDsa
|
8
|
+
EwIDAQAB
|
9
|
+
-----END PUBLIC KEY-----
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module Cwallet
|
2
|
+
module Wallet
|
3
|
+
class Account < APIObject
|
4
|
+
def update!(params = {})
|
5
|
+
@client.update_account(self['id'], params) do |data, resp|
|
6
|
+
update(data)
|
7
|
+
yield(data, resp) if block_given?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def make_primary!(params = {})
|
12
|
+
@client.set_primary_account(self['id'], params) do |data, resp|
|
13
|
+
update(data)
|
14
|
+
yield(data, resp) if block_given?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete!(params = {})
|
19
|
+
@client.delete_account(self['id'], params) do |data, resp|
|
20
|
+
yield(data, resp) if block_given?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Addresses
|
26
|
+
#
|
27
|
+
def addresses(params = {})
|
28
|
+
@client.addresses(self['id'], params) do |data, resp|
|
29
|
+
yield(data, resp) if block_given?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def address(address_id, params = {})
|
34
|
+
@client.address(self['id'], address_id, params) do |data, resp|
|
35
|
+
yield(data, resp) if block_given?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def address_transactions(address_id, params = {})
|
40
|
+
@client.address_transactions(self['id'], address_id, params) do |data, resp|
|
41
|
+
yield(data, resp) if block_given?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_address(params = {})
|
46
|
+
@client.create_address(self['id'], params) do |data, resp|
|
47
|
+
yield(data, resp) if block_given?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Transactions
|
53
|
+
#
|
54
|
+
def transactions(params = {})
|
55
|
+
@client.transactions(self['id'], params) do |data, resp|
|
56
|
+
yield(data, resp) if block_given?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def transaction(transaction_id, params = {})
|
61
|
+
@client.transaction(self['id'], transaction_id, params) do |data, resp|
|
62
|
+
yield(data, resp) if block_given?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def send(params = {})
|
67
|
+
@client.send(self['id'], params) do |data, resp|
|
68
|
+
yield(data, resp) if block_given?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def transfer(params = {})
|
73
|
+
@client.transfer(self['id'], params) do |data, resp|
|
74
|
+
yield(data, resp) if block_given?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def request(params = {})
|
79
|
+
@client.request(self['id'], params) do |data, resp|
|
80
|
+
yield(data, resp) if block_given?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Buys
|
86
|
+
#
|
87
|
+
def list_buys(params = {})
|
88
|
+
@client.list_buys(self['id'], params) do |data, resp|
|
89
|
+
yield(data, resp) if block_given?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def list_buy(transaction_id, params = {})
|
94
|
+
@client.list_buy(self['id'], transaction_id, params) do |data, resp|
|
95
|
+
yield(data, resp) if block_given?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def buy(params = {})
|
100
|
+
@client.buy(self['id'], params) do |data, resp|
|
101
|
+
yield(data, resp) if block_given?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def commit_buy(transaction_id, params = {})
|
106
|
+
@client.commit_buy(self['id'], transaction_id, params) do |data, resp|
|
107
|
+
yield(data, resp) if block_given?
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Sells
|
113
|
+
#
|
114
|
+
def list_sells(params = {})
|
115
|
+
@client.list_sells(self['id'], params) do |data, resp|
|
116
|
+
yield(data, resp) if block_given?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def list_sell(transaction_id, params = {})
|
121
|
+
@client.list_sell(self['id'], transaction_id, params) do |data, resp|
|
122
|
+
yield(data, resp) if block_given?
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def sell(params = {})
|
127
|
+
@client.sell(self['id'], params) do |data, resp|
|
128
|
+
yield(data, resp) if block_given?
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def commit_sell(transaction_id, params = {})
|
133
|
+
@client.commit_sell(self['id'], transaction_id, params) do |data, resp|
|
134
|
+
yield(data, resp) if block_given?
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Deposit
|
140
|
+
#
|
141
|
+
def list_deposits(params = {})
|
142
|
+
@client.list_deposits(self['id'], params) do |data, resp|
|
143
|
+
yield(data, resp) if block_given?
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def list_deposit(transaction_id, params = {})
|
148
|
+
@client.list_deposit(self['id'], transaction_id, params) do |data, resp|
|
149
|
+
yield(data, resp) if block_given?
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def deposit(params = {})
|
154
|
+
@client.deposit(self['id'], params) do |data, resp|
|
155
|
+
yield(data, resp) if block_given?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def commit_deposit(transaction_id, params = {})
|
160
|
+
@client.commit_deposit(self['id'], transaction_id, params) do |data, resp|
|
161
|
+
yield(data, resp) if block_given?
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
#
|
166
|
+
# Withdrawals
|
167
|
+
#
|
168
|
+
def list_withdrawals(params = {})
|
169
|
+
@client.list_withdrawals(self['id'], params) do |data, resp|
|
170
|
+
yield(data, resp) if block_given?
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def list_withdrawal(transaction_id, params = {})
|
175
|
+
@client.list_withdrawal(self['id'], transaction_id, params) do |data, resp|
|
176
|
+
yield(data, resp) if block_given?
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def withdraw(params = {})
|
181
|
+
@client.withdraw(self['id'], params) do |data, resp|
|
182
|
+
yield(data, resp) if block_given?
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def commit_withdrawal(transaction_id, params = {})
|
187
|
+
@client.commit_withdrawal(self['id'], transaction_id, params) do |data, resp|
|
188
|
+
yield(data, resp) if block_given?
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|