cwallet-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 860ab2f2b873e7639e4497bc45f82d243f8c8d34
4
+ data.tar.gz: 68c63caab0897059d19ab5972e857b1a58cb6213
5
+ SHA512:
6
+ metadata.gz: 0175fed8dbe3fbc1e5d084062273e2481022843ac9350ce8164a3fbb9eaa312f207f9fd8352f3523952fe0073c4524521cf49f1103f6cd20defc50537e72a2b5
7
+ data.tar.gz: c22d25525375f92e5aa94d09cb4ac4e958a5c4f484a8eba03a715a5ded2250dd7606eb6eafcd43b58520149a7a50e57018b8810a4ae2fde6954fd8f8f174ea25
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.gem
10
+ /lib/cwallet/wallet/cprivate
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.4.1
7
+ before_install: gem install bundler -v 1.16.4
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at leminhducktvn@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in cwallet-ruby.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Duc Le
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Cwallet::Ruby
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'cwallet-ruby'
9
+ ```
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install cwallet-ruby
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Development
24
+
25
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
26
+
27
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
28
+
29
+ ## Contributing
30
+
31
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/cwallet-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
32
+
33
+ ## License
34
+
35
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
36
+
37
+ ## Code of Conduct
38
+
39
+ Everyone interacting in the Cwallet::Ruby project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/cwallet-ruby/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cwallet/wallet"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "cwallet/wallet/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "cwallet-ruby"
7
+ spec.version = Cwallet::Wallet::VERSION
8
+ spec.authors = ["Duc Le"]
9
+ spec.email = ["leminhducktvn@gmail.com"]
10
+
11
+ spec.summary = %q{Client API for cwallet }
12
+ spec.description = %q{Client API for cwallet}
13
+ spec.homepage = "https://www.ducle.me"
14
+ spec.license = "MIT"
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.16"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", "~> 3.0"
28
+ spec.add_development_dependency "pry-byebug"
29
+ end
@@ -0,0 +1,21 @@
1
+ require "cwallet/wallet/version"
2
+
3
+ require "base64"
4
+ require 'net/https'
5
+ require 'json'
6
+
7
+ require 'cwallet/wallet/api_errors'
8
+ require 'cwallet/wallet/api_response'
9
+ require 'cwallet/wallet/api_client'
10
+ require 'cwallet/wallet/adapters/net_http.rb'
11
+ require 'cwallet/wallet/adapters/em_http.rb'
12
+ require 'cwallet/wallet/models/api_object'
13
+ require 'cwallet/wallet/models/account'
14
+ require 'cwallet/wallet/models/address'
15
+ require 'cwallet/wallet/models/transaction'
16
+ require 'cwallet/wallet/client'
17
+
18
+ module Cwallet
19
+ module Wallet
20
+ end
21
+ end
Binary file
@@ -0,0 +1,78 @@
1
+ module Cwallet
2
+ module Wallet
3
+ # EM-Http Adapter
4
+ class EMHTTPClient < APIClient
5
+ private
6
+
7
+ def http_verb(method, path, body = nil, headers = {})
8
+ if !EventMachine.reactor_running?
9
+ EM.run do
10
+ # FIXME: This doesn't work with paginated endpoints
11
+ http_verb(method, path, body) do |resp|
12
+ yield(resp)
13
+ EM.stop
14
+ end
15
+ end
16
+ else
17
+ headers['Content-Type'] = 'application/json'
18
+ headers['User-Agent'] = "coinbase/ruby-em/#{Coinbase::Wallet::VERSION}"
19
+ auth_headers(method, path, body).each do |key, val|
20
+ headers[key] = val
21
+ end
22
+
23
+ # NOTE: This is documented but not implemented in em-http-request
24
+ # https://github.com/igrigorik/em-http-request/issues/182
25
+ # https://github.com/igrigorik/em-http-request/pull/179
26
+ ssl_opts = { cert_chain_file: File.expand_path(File.join(File.dirname(__FILE__), 'ca-coinbase.crt')),
27
+ verify_peer: true }
28
+
29
+ case method
30
+ when 'GET'
31
+ req = EM::HttpRequest.new(@api_uri).get(path: path, head: headers, body: body, ssl: ssl_opts)
32
+ when 'POST'
33
+ req = EM::HttpRequest.new(@api_uri).put(path: path, head: headers, body: body, ssl: ssl_opts)
34
+ when 'POST'
35
+ req = EM::HttpRequest.new(@api_uri).post(path: path, head: headers, body: body, ssl: ssl_opts)
36
+ when 'DELETE'
37
+ req = EM::HttpRequest.new(@api_uri).delete(path: path, head: headers, ssl: ssl_opts)
38
+ else raise
39
+ end
40
+ req.callback do |resp|
41
+ out = EMHTTPResponse.new(resp)
42
+ Coinbase::Wallet::check_response_status(out)
43
+ yield(out)
44
+ end
45
+ req.errback do |resp|
46
+ raise APIError, "#{method} #{@api_uri}#{path}: #{resp.error}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ # EM-Http response object
53
+ class EMHTTPResponse < APIResponse
54
+ def body
55
+ JSON.parse(@response.response)
56
+ end
57
+
58
+ def data
59
+ body['data']
60
+ end
61
+
62
+ def body=(body)
63
+ @response.response = body.to_json
64
+ end
65
+
66
+ def headers
67
+ out = @response.response_header.map do |key, val|
68
+ [ key.upcase.gsub('_', '-'), val ]
69
+ end
70
+ out.to_h
71
+ end
72
+
73
+ def status
74
+ @response.response_header.status
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,66 @@
1
+ module Cwallet
2
+ module Wallet
3
+ class NetHTTPClient < APIClient
4
+ def initialize(base_uri, options = {})
5
+ @conn = Net::HTTP.new(base_uri.host, base_uri.port)
6
+ @conn.use_ssl = true if base_uri.scheme == 'https'
7
+ end
8
+
9
+ private
10
+
11
+ def http_verb(method, path, body = nil, headers = {})
12
+ case method
13
+ when 'GET' then req = Net::HTTP::Get.new(path)
14
+ when 'PUT' then req = Net::HTTP::Put.new(path)
15
+ when 'POST' then req = Net::HTTP::Post.new(path)
16
+ when 'DELETE' then req = Net::HTTP::Delete.new(path)
17
+ else raise
18
+ end
19
+
20
+ req.body = body
21
+
22
+ req['Content-Type'] = 'application/json'
23
+ req['User-Agent'] = "cwallet/ruby/#{Cwallet::Wallet::VERSION}"
24
+ auth_headers(method, path, body).each do |key, val|
25
+ req[key] = val
26
+ end
27
+ headers.each do |key, val|
28
+ req[key] = val
29
+ end
30
+
31
+ resp = @conn.request(req)
32
+ out = NetHTTPResponse.new(resp)
33
+ Cwallet::Wallet::check_response_status(out)
34
+ yield(out)
35
+ out.data
36
+ end
37
+ end
38
+
39
+ # Net-Http response object
40
+ class NetHTTPResponse < APIResponse
41
+ def body
42
+ JSON.parse(@response.body) rescue {}
43
+ end
44
+
45
+ def body=(body)
46
+ @response.body = body.to_json
47
+ end
48
+
49
+ def data
50
+ body['data']
51
+ end
52
+
53
+ def headers
54
+ out = @response.to_hash.map do |key, val|
55
+ [ key.upcase.gsub('_', '-'), val.count == 1 ? val.first : val ]
56
+ end
57
+ out.to_h
58
+ end
59
+
60
+ def status
61
+ @response.code.to_i
62
+ end
63
+ end
64
+ end
65
+ end
66
+
@@ -0,0 +1,734 @@
1
+ module Cwallet
2
+ module Wallet
3
+ class APIClient
4
+ def auth_headers(method, path, body)
5
+ raise NotImplementedError, "APIClient is not intended to be used directly"
6
+ end
7
+
8
+ #
9
+ # Market Data
10
+ #
11
+ def currencies(params = {})
12
+ out = nil
13
+ get("/v2/currencies", params) do |resp|
14
+ out = resp.data.map { |item| APIObject.new(self, item) }
15
+ yield(out, resp) if block_given?
16
+ end
17
+ out
18
+ end
19
+
20
+ def exchange_rates(params = {})
21
+ out = nil
22
+ get("/v2/exchange-rates", params) do |resp|
23
+ out = APIObject.new(self, resp.data)
24
+ yield(out, resp) if block_given?
25
+ end
26
+ out
27
+ end
28
+
29
+ def buy_price(params = {})
30
+ out = nil
31
+ pair = determine_currency_pair(params)
32
+
33
+ get("/v2/prices/#{pair}/buy", params) do |resp|
34
+ out = APIObject.new(self, resp.data)
35
+ yield(out, resp) if block_given?
36
+ end
37
+ out
38
+ end
39
+
40
+ def sell_price(params = {})
41
+ out = nil
42
+ pair = determine_currency_pair(params)
43
+
44
+ get("/v2/prices/#{pair}/sell", params) do |resp|
45
+ out = APIObject.new(self, resp.data)
46
+ yield(out, resp) if block_given?
47
+ end
48
+ out
49
+ end
50
+
51
+ def spot_price(params = {})
52
+ out = nil
53
+ pair = determine_currency_pair(params)
54
+
55
+ get("/v2/prices/#{pair}/spot", params) do |resp|
56
+ out = APIObject.new(self, resp.data)
57
+ yield(out, resp) if block_given?
58
+ end
59
+ out
60
+ end
61
+
62
+ def historic_prices(params = {})
63
+ out = nil
64
+ get("/v2/prices/historic", params) do |resp|
65
+ out = APIObject.new(self, resp.data)
66
+ yield(out, resp) if block_given?
67
+ end
68
+ out
69
+ end
70
+
71
+ def time(params = {})
72
+ out = nil
73
+ get("/v2/time", params) do |resp|
74
+ out = APIObject.new(self, resp.data)
75
+ yield(out, resp) if block_given?
76
+ end
77
+ out
78
+ end
79
+
80
+ #
81
+ # Users
82
+ #
83
+ def user(user_id, params = {})
84
+ out = nil
85
+ get("/v2/users/#{user_id}", params) do |resp|
86
+ out = User.new(self, resp.data)
87
+ yield(out, resp) if block_given?
88
+ end
89
+ out
90
+ end
91
+
92
+ def current_user(params = {})
93
+ out = nil
94
+ get("/v2/user", params) do |resp|
95
+ out = CurrentUser.new(self, resp.data)
96
+ yield(out, resp) if block_given?
97
+ end
98
+ out
99
+ end
100
+
101
+ def auth_info(params = {})
102
+ out = nil
103
+ get("/v2/user/auth", params) do |resp|
104
+ out = APIObject.new(self, resp.data)
105
+ yield(out, resp) if block_given?
106
+ end
107
+ out
108
+ end
109
+
110
+ def update_current_user(params = {})
111
+ out = nil
112
+ put("/v2/user", params) do |resp|
113
+ out = CurrentUser.new(self, resp.data)
114
+ yield(out, resp) if block_given?
115
+ end
116
+ out
117
+ end
118
+
119
+ #
120
+ # Accounts
121
+ #
122
+ def accounts(params = {})
123
+ out = nil
124
+ get("/v2/accounts", params) do |resp|
125
+ out = resp.data.map { |item| Account.new(self, item) }
126
+ yield(out, resp) if block_given?
127
+ end
128
+ out
129
+ end
130
+
131
+ def account(account_id, params = {})
132
+ out = nil
133
+ get("/v2/accounts/#{account_id}", params) do |resp|
134
+ out = Account.new(self, resp.data)
135
+ yield(out, resp) if block_given?
136
+ end
137
+ out
138
+ end
139
+
140
+ def primary_account(params = {})
141
+ out = nil
142
+ get("/v2/accounts/primary", params) do |resp|
143
+ out = Account.new(self, resp.data)
144
+ yield(out, resp) if block_given?
145
+ end
146
+ out
147
+ end
148
+
149
+ def set_primary_account(account_id, params = {})
150
+ out = nil
151
+ post("/v2/accounts/#{account_id}/primary", params) do |resp|
152
+ out = Account.new(self, resp.data)
153
+ yield(out, resp) if block_given?
154
+ end
155
+ out
156
+ end
157
+
158
+ def create_account(params = {})
159
+ out = nil
160
+ post("/v2/accounts", params) do |resp|
161
+ out = Account.new(self, resp.data)
162
+ yield(out, resp) if block_given?
163
+ end
164
+ out
165
+ end
166
+
167
+ def update_account(account_id, params = {})
168
+ out = nil
169
+ put("/v2/accounts/#{account_id}", params) do |resp|
170
+ out = Account.new(self, resp.data)
171
+ yield(out, resp) if block_given?
172
+ end
173
+ out
174
+ end
175
+
176
+ def delete_account(account_id, params = {})
177
+ out = nil
178
+ delete("/v2/accounts/#{account_id}", params) do |resp|
179
+ out = APIObject.new(self, resp.data)
180
+ yield(out, resp) if block_given?
181
+ end
182
+ out
183
+ end
184
+
185
+ #
186
+ # Notifications
187
+ #
188
+ def notifications(params = {})
189
+ out = nil
190
+ get("/v2/notifications", params) do |resp|
191
+ out = resp.data.map { |item| APIObject.new(self, item) }
192
+ yield(out, resp) if block_given?
193
+ end
194
+ out
195
+ end
196
+
197
+ def notification(notification_id, params = {})
198
+ out = nil
199
+ get("/v2/notifications/#{notification_id}", params) do |resp|
200
+ out = APIObject.new(self, resp.data)
201
+ yield(out, resp) if block_given?
202
+ end
203
+ out
204
+ end
205
+
206
+
207
+ #
208
+ # Addresses
209
+ #
210
+ def addresses(account_id, params = {})
211
+ out = nil
212
+ get("/v2/accounts/#{account_id}/addresses", params) do |resp|
213
+ out = resp.data.map { |item| Address.new(self, item) }
214
+ yield(out, resp) if block_given?
215
+ end
216
+ out
217
+ end
218
+
219
+ def address(account_id, address_id, params = {})
220
+ out = nil
221
+ get("/v2/accounts/#{account_id}/addresses/#{address_id}", params) do |resp|
222
+ out = Address.new(self, resp.data)
223
+ yield(out, resp) if block_given?
224
+ end
225
+ out
226
+ end
227
+
228
+ def address_transactions(account_id, address_id, params = {})
229
+ out = nil
230
+ get("/v2/accounts/#{account_id}/addresses/#{address_id}/transactions", params) do |resp|
231
+ out = resp.data.map { |item| Transaction.new(self, item) }
232
+ yield(out, resp) if block_given?
233
+ end
234
+ out
235
+ end
236
+
237
+ def create_address(account_id, params = {})
238
+ out = nil
239
+ post("/v2/accounts/#{account_id}/addresses", params) do |resp|
240
+ out = Address.new(self, resp.data)
241
+ yield(out, resp) if block_given?
242
+ end
243
+ out
244
+ end
245
+
246
+ #
247
+ # Transactions
248
+ #
249
+ def transactions(account_id, params = {})
250
+ out = nil
251
+ get("/v2/accounts/#{account_id}/transactions", params) do |resp|
252
+ out = resp.data.map { |item| Transaction.new(self, item) }
253
+ yield(out, resp) if block_given?
254
+ end
255
+ out
256
+ end
257
+
258
+ def transaction(account_id, transaction_id, params = {})
259
+ out = nil
260
+ get("/v2/accounts/#{account_id}/transactions/#{transaction_id}", params) do |resp|
261
+ out = Transaction.new(self, resp.data)
262
+ yield(out, resp) if block_given?
263
+ end
264
+ out
265
+ end
266
+
267
+ def send(account_id, params = {})
268
+ [ :to, :amount ].each do |param|
269
+ raise APIError, "Missing parameter: #{param}" unless params.include? param
270
+ end
271
+ params['type'] = 'send'
272
+
273
+ out = nil
274
+ post("/v2/accounts/#{account_id}/transactions", params) do |resp|
275
+ out = Transaction.new(self, resp.data)
276
+ yield(out, resp) if block_given?
277
+ end
278
+ out
279
+ end
280
+
281
+ def transfer(account_id, params = {})
282
+ [ :to, :amount ].each do |param|
283
+ raise APIError, "Missing parameter: #{param}" unless params.include? param
284
+ end
285
+ params['type'] = 'transfer'
286
+
287
+ out = nil
288
+ post("/v2/accounts/#{account_id}/transactions", params) do |resp|
289
+ out = Transaction.new(self, resp.data)
290
+ yield(out, resp) if block_given?
291
+ end
292
+ out
293
+ end
294
+
295
+ def request(account_id, params = {})
296
+ [ :to, :amount, :currency ].each do |param|
297
+ raise APIError, "Missing parameter: #{param}" unless params.include? param
298
+ end
299
+ params['type'] = 'request'
300
+
301
+ out = nil
302
+ post("/v2/accounts/#{account_id}/transactions", params) do |resp|
303
+ out = Request.new(self, resp.data)
304
+ yield(out, resp) if block_given?
305
+ end
306
+ out
307
+ end
308
+
309
+ def resend_request(account_id, transaction_id, params = {})
310
+ out = nil
311
+ post("/v2/accounts/#{account_id}/transactions/#{transaction_id}/resend", params) do |resp|
312
+ out = APIObject.new(self, resp.data)
313
+ yield(out, resp) if block_given?
314
+ end
315
+ out
316
+ end
317
+
318
+ def cancel_request(account_id, transaction_id, params = {})
319
+ out = nil
320
+ delete("/v2/accounts/#{account_id}/transactions/#{transaction_id}", params) do |resp|
321
+ out = APIObject.new(self, resp.data)
322
+ yield(out, resp) if block_given?
323
+ end
324
+ out
325
+ end
326
+
327
+ def complete_request(account_id, transaction_id, params = {})
328
+ out = nil
329
+ post("/v2/accounts/#{account_id}/transactions/#{transaction_id}/complete", params) do |resp|
330
+ out = APIObject.new(self, resp.data)
331
+ yield(out, resp) if block_given?
332
+ end
333
+ out
334
+ end
335
+
336
+ #
337
+ # Buys
338
+ #
339
+ def list_buys(account_id, params={})
340
+ out = nil
341
+ get("/v2/accounts/#{account_id}/buys", params) do |resp|
342
+ out = resp.data.map { |item| Transfer.new(self, item) }
343
+ yield(out, resp) if block_given?
344
+ end
345
+ out
346
+ end
347
+
348
+ def list_buy(account_id, transaction_id, params = {})
349
+ out = nil
350
+ get("/v2/accounts/#{account_id}/buys/#{transaction_id}", params) do |resp|
351
+ out = Transfer.new(self, resp.data)
352
+ yield(out, resp) if block_given?
353
+ end
354
+ out
355
+ end
356
+
357
+ def buy(account_id, params = {})
358
+ raise APIError, "Missing parameter: 'amount' or 'total'" unless params.include? :amount or params.include? :total
359
+
360
+ out = nil
361
+ post("/v2/accounts/#{account_id}/buys", params) do |resp|
362
+ out = Transfer.new(self, resp.data)
363
+ yield(out, resp) if block_given?
364
+ end
365
+ out
366
+ end
367
+
368
+ def commit_buy(account_id, transaction_id, params = {})
369
+ out = nil
370
+ post("/v2/accounts/#{account_id}/buys/#{transaction_id}/commit", params) do |resp|
371
+ out = Transfer.new(self, resp.data)
372
+ yield(out, resp) if block_given?
373
+ end
374
+ out
375
+ end
376
+
377
+ #
378
+ # Sells
379
+ #
380
+ def list_sells(account_id, params = {})
381
+ out = nil
382
+ get("/v2/accounts/#{account_id}/sells", params) do |resp|
383
+ out = resp.data.map { |item| Transfer.new(self, item) }
384
+ yield(out, resp) if block_given?
385
+ end
386
+ out
387
+ end
388
+
389
+ def list_sell(account_id, transaction_id, params = {})
390
+ out = nil
391
+ get("/v2/accounts/#{account_id}/sells/#{transaction_id}", params) do |resp|
392
+ out = Transfer.new(self, resp.data)
393
+ yield(out, resp) if block_given?
394
+ end
395
+ out
396
+ end
397
+
398
+ def sell(account_id, params = {})
399
+ raise APIError, "Missing parameter: 'amount' or 'total'" unless params.include? :amount or params.include? :total
400
+
401
+ out = nil
402
+ post("/v2/accounts/#{account_id}/sells", params) do |resp|
403
+ out = Transfer.new(self, resp.data)
404
+ yield(out, resp) if block_given?
405
+ end
406
+ out
407
+ end
408
+
409
+ def commit_sell(account_id, transaction_id, params = {})
410
+ out = nil
411
+ post("/v2/accounts/#{account_id}/sells/#{transaction_id}/commit", params) do |resp|
412
+ out = Transfer.new(self, resp.data)
413
+ yield(out, resp) if block_given?
414
+ end
415
+ out
416
+ end
417
+
418
+ #
419
+ # Deposits
420
+ #
421
+ def list_deposits(account_id, params={})
422
+ out = nil
423
+ get("/v2/accounts/#{account_id}/deposits", params) do |resp|
424
+ out = resp.data.map { |item| Transfer.new(self, item) }
425
+ yield(out, resp) if block_given?
426
+ end
427
+ out
428
+ end
429
+
430
+ def list_deposit(account_id, transaction_id, params = {})
431
+ out = nil
432
+ get("/v2/accounts/#{account_id}/deposits/#{transaction_id}", params) do |resp|
433
+ out = Transfer.new(self, resp.data)
434
+ yield(out, resp) if block_given?
435
+ end
436
+ out
437
+ end
438
+
439
+ def deposit(account_id, params = {})
440
+ [ :amount ].each do |param|
441
+ raise APIError, "Missing parameter: #{param}" unless params.include? param
442
+ end
443
+
444
+ out = nil
445
+ post("/v2/accounts/#{account_id}/deposits", params) do |resp|
446
+ out = Transfer.new(self, resp.data)
447
+ yield(out, resp) if block_given?
448
+ end
449
+ out
450
+ end
451
+
452
+ def commit_deposit(account_id, transaction_id, params = {})
453
+ out = nil
454
+ post("/v2/accounts/#{account_id}/deposits/#{transaction_id}/commit", params) do |resp|
455
+ out = APIObject.new(self, resp.data)
456
+ yield(out, resp) if block_given?
457
+ end
458
+ out
459
+ end
460
+
461
+ #
462
+ # withdrawals
463
+ #
464
+ def list_withdrawals(account_id, params={})
465
+ out = nil
466
+ get("/v2/accounts/#{account_id}/withdrawals", params) do |resp|
467
+ out = resp.data.map { |item| Transfer.new(self, item) }
468
+ yield(out, resp) if block_given?
469
+ end
470
+ out
471
+ end
472
+
473
+ def list_withdrawal(account_id, transaction_id, params = {})
474
+ out = nil
475
+ get("/v2/accounts/#{account_id}/withdrawals/#{transaction_id}", params) do |resp|
476
+ out = Transfer.new(self, resp.data)
477
+ yield(out, resp) if block_given?
478
+ end
479
+ out
480
+ end
481
+
482
+ def withdraw(account_id, params = {})
483
+ [ :amount ].each do |param|
484
+ raise APIError, "Missing parameter: #{param}" unless params.include? param
485
+ end
486
+
487
+ out = nil
488
+ post("/v2/accounts/#{account_id}/withdrawals", params) do |resp|
489
+ out = Transfer.new(self, resp.data)
490
+ yield(out, resp) if block_given?
491
+ end
492
+ out
493
+ end
494
+
495
+ def commit_withdrawal(account_id, transaction_id, params = {})
496
+ out = nil
497
+ post("/v2/accounts/#{account_id}/withdrawals/#{transaction_id}/commit", params) do |resp|
498
+ out = APIObject.new(self, resp.data)
499
+ yield(out, resp) if block_given?
500
+ end
501
+ out
502
+ end
503
+
504
+ #
505
+ # Payment Methods
506
+ #
507
+ def payment_methods(params = {})
508
+ out = nil
509
+ get("/v2/payment-methods", params) do |resp|
510
+ out = resp.data.map { |item| APIObject.new(self, item) }
511
+ yield(out, resp) if block_given?
512
+ end
513
+ out
514
+ end
515
+
516
+ def payment_method(payment_method_id, params = {})
517
+ out = nil
518
+ get("/v2/payment-methods/#{payment_method_id}", params) do |resp|
519
+ out = APIObject.new(self, resp.data)
520
+ yield(out, resp) if block_given?
521
+ end
522
+ out
523
+ end
524
+
525
+ #
526
+ # Merchants
527
+ #
528
+ def merchant(merchant_id, params = {})
529
+ out = nil
530
+ get("/v2/merchants/#{merchant_id}", params) do |resp|
531
+ out = APIObject.new(self, resp.data)
532
+ yield(out, resp) if block_given?
533
+ end
534
+ out
535
+ end
536
+
537
+ #
538
+ # Orders
539
+ #
540
+ def orders(params = {})
541
+ out = nil
542
+ get("/v2/orders", params) do |resp|
543
+ out = resp.data.map { |item| Order.new(self, item) }
544
+ yield(out, resp) if block_given?
545
+ end
546
+ out
547
+ end
548
+
549
+ def order(order_id, params = {})
550
+ out = nil
551
+ get("/v2/orders/#{order_id}", params) do |resp|
552
+ out = Order.new(self, resp.data)
553
+ yield(out, resp) if block_given?
554
+ end
555
+ out
556
+ end
557
+
558
+ def create_order(params = {})
559
+ [ :amount, :currency, :name ].each do |param|
560
+ fail APIError, "Missing parameter: #{param}" unless params.include? param
561
+ end
562
+
563
+ out = nil
564
+ post("/v2/orders", params) do |resp|
565
+ out = Order.new(self, resp.data)
566
+ yield(out, resp) if block_given?
567
+ end
568
+ out
569
+ end
570
+
571
+ def refund_order(order_id, params={})
572
+ [ :currency ].each do |param|
573
+ fail APIError, "Missing parameter: #{param}" unless params.include? param
574
+ end
575
+
576
+ out = nil
577
+ post("/v2/orders/#{order_id}/refund", params) do |resp|
578
+ out = Order.new(self, resp.data)
579
+ yield(out, resp) if block_given?
580
+ end
581
+ out
582
+ end
583
+
584
+ #
585
+ # Checkouts
586
+ #
587
+ def checkouts(params = {})
588
+ out = nil
589
+ get("/v2/checkouts", params) do |resp|
590
+ out = resp.data.map { |item| Checkout.new(self, item) }
591
+ yield(out, resp) if block_given?
592
+ end
593
+ out
594
+ end
595
+
596
+ def checkout(checkout_id, params = {})
597
+ out = nil
598
+ get("/v2/checkouts/#{checkout_id}", params) do |resp|
599
+ out = Checkout.new(self, resp.data)
600
+ yield(out, resp) if block_given?
601
+ end
602
+ out
603
+ end
604
+
605
+ def create_checkout(params = {})
606
+ [ :amount, :currency, :name ].each do |param|
607
+ fail APIError, "Missing parameter: #{param}" unless params.include? param
608
+ end
609
+
610
+ out = nil
611
+ post("/v2/checkouts", params) do |resp|
612
+ out = Checkout.new(self, resp.data)
613
+ yield(out, resp) if block_given?
614
+ end
615
+ out
616
+ end
617
+
618
+ def checkout_orders(checkout_id, params = {})
619
+ out = nil
620
+ get("/v2/checkouts/#{checkout_id}/orders", params) do |resp|
621
+ out = resp.data.map { |item| Order.new(self, item) }
622
+ yield(out, resp) if block_given?
623
+ end
624
+ out
625
+ end
626
+
627
+ def create_checkout_order(checkout_id, params={})
628
+ out = nil
629
+ post("/v2/checkouts/#{checkout_id}/orders", params) do |resp|
630
+ out = Order.new(self, resp.data)
631
+ yield(out, resp) if block_given?
632
+ end
633
+ out
634
+ end
635
+
636
+ #
637
+ # HTTP Stuff
638
+ #
639
+ def get(path, params)
640
+ uri = path
641
+ if params.count > 0
642
+ uri += "?#{URI.encode_www_form(params)}"
643
+ end
644
+
645
+ headers = {}
646
+ if params.has_key? :two_factor_token
647
+ headers['CB-2FA-TOKEN'] = params[:two_factor_token]
648
+ params.delete(:two_factor_token)
649
+ end
650
+
651
+ http_verb('GET', uri, nil, headers) do |resp|
652
+ if params[:fetch_all] == true &&
653
+ resp.body.has_key?('pagination') &&
654
+ resp.body['pagination']['next_uri'] != nil
655
+ params[:starting_after] = resp.body['data'].last['id']
656
+ get(path, params) do |page|
657
+ body = resp.body
658
+ body['data'] += page.data
659
+ resp.body = body
660
+ yield(resp)
661
+ end
662
+ else
663
+ yield(resp)
664
+ end
665
+ end
666
+ end
667
+
668
+ def put(path, params)
669
+ headers = {}
670
+ if params.has_key? :two_factor_token
671
+ headers['CB-2FA-TOKEN'] = params[:two_factor_token]
672
+ params.delete(:two_factor_token)
673
+ end
674
+
675
+ http_verb('PUT', path, params.to_json, headers) do |resp|
676
+ yield(resp)
677
+ end
678
+ end
679
+
680
+ def post(path, params)
681
+ headers = {}
682
+ if params.has_key? :two_factor_token
683
+ headers['CB-2FA-TOKEN'] = params[:two_factor_token]
684
+ params.delete(:two_factor_token)
685
+ end
686
+
687
+ http_verb('POST', path, params.to_json, headers) do |resp|
688
+ yield(resp)
689
+ end
690
+ end
691
+
692
+ def delete(path, params)
693
+ headers = {}
694
+ if params.has_key? :two_factor_token
695
+ headers['CB-2FA-TOKEN'] = params[:two_factor_token]
696
+ params.delete(:two_factor_token)
697
+ end
698
+
699
+ http_verb('DELETE', path, nil, headers) do |resp|
700
+ yield(resp)
701
+ end
702
+ end
703
+
704
+ CALLBACK_DIGEST = OpenSSL::Digest.new("SHA256")
705
+ def self.verify_callback(body, signature)
706
+ return false unless callback_signing_public_key
707
+ callback_signing_public_key.verify(CALLBACK_DIGEST, signature.unpack("m0")[0], body)
708
+ rescue OpenSSL::PKey::RSAError, ArgumentError
709
+ false
710
+ end
711
+
712
+ def self.callback_signing_public_key
713
+ @@callback_signing_public_key ||= nil
714
+ return @@callback_signing_public_key if @@callback_signing_public_key
715
+ path = File.expand_path(File.join(File.dirname(__FILE__), 'cpub.pub'))
716
+ @@callback_signing_public_key = OpenSSL::PKey::RSA.new(File.read(path))
717
+ end
718
+
719
+ def callback_signing_public_key
720
+ Cwallet::Wallet::APIClient.callback_signing_public_key
721
+ end
722
+
723
+ def verify_callback(body, signature)
724
+ Cwallet::Wallet::APIClient.verify_callback(body, signature)
725
+ end
726
+
727
+ private
728
+
729
+ def determine_currency_pair(params)
730
+ Cwallet::Util.determine_currency_pair(params)
731
+ end
732
+ end
733
+ end
734
+ end