coinkite 0.9

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f9e4b935ab46f376d664cd1d96c0b29ccf0cbe1a
4
+ data.tar.gz: a0b409931a40c8522e174eab4b81032768f287ff
5
+ SHA512:
6
+ metadata.gz: 29d35f924d8d07e68585512e75ede9ff96594d940597fa68171f5154d9eeabb563e3d255330b35ef6fac75493e1e11bb064066c2763d5afefbbf689bd6374377
7
+ data.tar.gz: e98d7d1636cdefdf94142bc3e3c464290c759f3b1d24102383022071a49d79f4138f63a00818117c93727452bdc558d68b57acc6e80618c0818cd7589aefe3dd
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .env
2
+ .idea/
3
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Coinkite API Code for Ruby
2
+
3
+ [Learn more about Coinkite's API here](https://docs.coinkite.com/)
4
+ and visit the [Coinkite Main Site](https://coinkite.com/) to open your
5
+ account today!
6
+
7
+ ## Introduction
8
+
9
+ Every request made to the Coinkite API requires three special header
10
+ lines. This code can generate the timestamp and signature value
11
+ required for 2 of those 3 headers. The remaining header is just
12
+ the API key itself.
13
+
14
+ Header lines you need:
15
+
16
+ X-CK-Key: K5555a555-55a555aa-a55aa5a5555aaa5a
17
+ X-CK-Timestamp: 2014-06-23T03:10:04.905376
18
+ X-CK-Sign: 0aa7755aaa45189a98a5a8a887a564aa55aa5aa4aa7a98aa2858aaa60a5a56aa
19
+
20
+ ## How to Install
21
+
22
+ Replace the two values shown here.
23
+
24
+ ````ruby
25
+ API_KEY = 'this-is-my-key'
26
+ API_SECRET = 'this-is-my-secret'
27
+ ````
28
+
29
+ The keys you need can be created on
30
+ [Coinkite.com under Merchant / API]([https://coinkite.com/merchant/api).
31
+
32
+
33
+ ## More about Coinkite
34
+
35
+ Coinkite is the world's easiest and most powerful web wallet for
36
+ safely holding all your cryptocurrencies, including Bitcoin and Litecoin.
37
+
38
+ [Learn more about all we offer](https://coinkite.com/)
39
+
40
+ ## Liscense
41
+
42
+ Copyright (C) 2013 Coinkite Inc. (https://coinkite.com)
43
+
44
+ Permission is hereby granted, free of charge, to any person obtaining a copy
45
+ of this software and associated documentation files (the "Software"), to deal
46
+ in the Software without restriction, including without limitation the rights
47
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
48
+ copies of the Software, and to permit persons to whom the Software is
49
+ furnished to do so, subject to the following conditions:
50
+
51
+ The above copyright notice and this permission notice shall be included in
52
+ all copies or substantial portions of the Software.
53
+
54
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
55
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
56
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
57
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
58
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
59
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
60
+ THE SOFTWARE.
61
+
62
+
data/coinkite.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
+
3
+ require 'coinkite/version'
4
+
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = 'coinkite'
7
+ s.version = Coinkite::VERSION
8
+ s.summary = 'Ruby bindings for the Coinkite API'
9
+ s.description = 'Coinkite is the most secure way to transact in bitcoin online. See https://coinkite.com for details.'
10
+ s.authors = ['Ilia Lobsanov']
11
+ s.email = ['ilia@lobsanov.com']
12
+ s.homepage = 'https://docs.coinkite.com/api/index.html'
13
+ s.license = 'MIT'
14
+
15
+ s.add_dependency('rest-client', '~> 1.4')
16
+ s.add_dependency('mime-types', '>= 1.25', '< 3.0')
17
+ s.add_dependency('json', '~> 1.8')
18
+
19
+ s.add_development_dependency('rspec', '~>3.0')
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- spec/*`.split("\n")
23
+ s.require_paths = ['lib']
24
+ end
@@ -0,0 +1,21 @@
1
+ module Coinkite
2
+ class APIConnectionError < StandardError
3
+ attr_reader :message
4
+ attr_reader :http_status
5
+ attr_reader :http_body
6
+ attr_reader :json_body
7
+
8
+ def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil)
9
+ @message = message
10
+ @http_status = http_status
11
+ @http_body = http_body
12
+ @json_body = json_body
13
+ end
14
+
15
+ def to_s
16
+ status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
17
+ "#{status_string}#{@message}"
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,32 @@
1
+ module Coinkite
2
+ class CoinkiteError < StandardError
3
+ attr_reader :message
4
+ attr_reader :http_status
5
+ attr_reader :http_body
6
+ attr_reader :json_body
7
+
8
+ def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil)
9
+ @message = message
10
+ @http_status = http_status
11
+ @http_body = http_body
12
+ @json_body = json_body
13
+ end
14
+
15
+ def to_s
16
+ status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
17
+ "#{status_string}#{@message}"
18
+ end
19
+ end
20
+
21
+ class CKServerSideError < CoinkiteError
22
+
23
+ end
24
+
25
+ class CKMissingError < CoinkiteError
26
+
27
+ end
28
+
29
+ class CKArgumentError < CoinkiteError
30
+
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Coinkite
2
+ VERSION = '0.9'
3
+ end
data/lib/coinkite.rb ADDED
@@ -0,0 +1,165 @@
1
+ require 'cgi'
2
+ require 'time'
3
+ require 'openssl'
4
+ require 'rest-client'
5
+ require 'json'
6
+
7
+ require 'coinkite/version'
8
+
9
+ require 'coinkite/errors/api_connection_error'
10
+ require 'coinkite/errors/coinkite_error'
11
+
12
+ module Coinkite
13
+ class Client
14
+ DEFAULT_CA_BUNDLE_PATH = File.dirname(__FILE__) + '/data/ca-certificates.crt'
15
+
16
+ def initialize(api_key, api_secret)
17
+ @api_key = api_key
18
+ @api_secret = api_secret
19
+ @api_base = 'https://api.coinkite.com'
20
+ @ssl_bundle_path = DEFAULT_CA_BUNDLE_PATH
21
+ end
22
+
23
+ def api_url(url='')
24
+ @api_base + url
25
+ end
26
+
27
+ def request(method, endpoint, params={}, headers={})
28
+ unless api_key ||= @api_key
29
+ raise AuthenticationError.new('No API key provided. ' +
30
+ 'Set your API key using "Coinkite.api_key = <API-KEY>". ' +
31
+ 'You can generate API keys from the Coinkite web interface. ')
32
+ end
33
+
34
+ url = api_url(endpoint)
35
+
36
+ case method.to_s.downcase.to_sym
37
+ when :get, :head, :delete
38
+ url += "#{URI.parse(url).query ? '&' : '?'}#{uri_encode(params)}" if params && params.any?
39
+ payload = nil
40
+ else
41
+ payload = JSON.encode(params.fetch('_data', params))
42
+ headers['Content-Type'] = 'application/json'
43
+ end
44
+
45
+ request_opts = {}
46
+ request_opts.update(:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
47
+ :ssl_ca_file => @ssl_bundle_path,
48
+ :headers => request_headers(endpoint).update(headers),
49
+ :method => method, :open_timeout => 30,
50
+ :payload => payload, :url => url, :timeout => 80)
51
+
52
+ begin
53
+ response = RestClient::Request.execute(request_opts)
54
+ rescue SocketError => e
55
+ handle_restclient_error(e)
56
+ rescue NoMethodError => e
57
+ # Work around RestClient bug
58
+ if e.message =~ /\WRequestFailed\W/
59
+ e = APIConnectionError.new('Unexpected HTTP response code')
60
+ handle_restclient_error(e)
61
+ else
62
+ raise
63
+ end
64
+ rescue RestClient::ExceptionWithResponse => e
65
+ if rcode = e.http_code and rbody = e.http_body
66
+ if rcode == 429 and rbody.has_key?("wait_time")
67
+ sleep(rbody.wait_time)
68
+ retry
69
+ else
70
+ handle_api_error(rcode, rbody)
71
+ end
72
+ else
73
+ handle_restclient_error(e)
74
+ end
75
+ rescue RestClient::Exception, Errno::ECONNREFUSED => e
76
+ handle_restclient_error(e)
77
+ end
78
+
79
+ JSON.parse(response.body)
80
+ end
81
+
82
+ def uri_encode(params)
83
+ params.map { |k,v| "#{k}=#{url_encode(v)}" }.join('&')
84
+ end
85
+
86
+ def url_encode(key)
87
+ URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
88
+ end
89
+
90
+ def handle_restclient_error(e)
91
+ case e
92
+ when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
93
+ message = "Could not connect to Coinkite (#{@api_base}). " +
94
+ "Please check your internet connection and try again. " +
95
+ "If this problem persists, you should check Coinkite's service status at " +
96
+ "https://twitter.com/Coinkite, or let us know at support@coinkite.com."
97
+
98
+ when RestClient::SSLCertificateNotVerified
99
+ message = "Could not verify Coinkite's SSL certificate. " +
100
+ "Please make sure that your network is not intercepting certificates. " +
101
+ "(Try going to https://api.coinkite.com in your browser.) " +
102
+ "If this problem persists, let us know at support@coinkite.com."
103
+
104
+ when SocketError
105
+ message = "Unexpected error communicating when trying to connect to Coinkite. " +
106
+ "You may be seeing this message because your DNS is not working. " +
107
+ "To check, try running 'host coinkite.com' from the command line."
108
+
109
+ else
110
+ message = "Unexpected error communicating with Coinkite. " +
111
+ "If this problem persists, let us know at support@coinkite.com."
112
+
113
+ end
114
+
115
+ raise APIConnectionError.new(message + "\n\n(Network error: #{e.message})")
116
+ end
117
+
118
+ def handle_api_error(rcode, rbody)
119
+ case rcode
120
+ when 400
121
+ raise CoinkiteError.new(rbody)
122
+ when 404
123
+ raise CoinkiteError.new(rbody)
124
+ else
125
+ raise CoinkiteError.new(rbody)
126
+ end
127
+ end
128
+
129
+ def make_signature(endpoint, force_ts=nil)
130
+ ts = force_ts || Time.now.utc.iso8601
131
+ data = endpoint + '|' + ts
132
+ hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('SHA256'), @api_secret, data)
133
+
134
+ return hmac, ts
135
+ end
136
+
137
+ def request_headers(endpoint, force_ts=nil)
138
+ signature, timestamp = make_signature(endpoint, force_ts)
139
+
140
+ {
141
+ 'X-CK-Key' => @api_key,
142
+ 'X-CK-Timestamp' => timestamp,
143
+ 'X-CK-Sign' => signature,
144
+ 'User-Agent' => "Coinkite/v1 RubyBindings/#{Coinkite::VERSION}"
145
+ }
146
+ end
147
+
148
+ def get(endpoint)
149
+ request('GET', endpoint)
150
+ end
151
+
152
+ def get_accounts
153
+ get('/v1/my/accounts')["results"]
154
+ end
155
+
156
+ def get_detail(refnum)
157
+ get("/v1/detail/#{refnum}")["detail"]
158
+ end
159
+
160
+ def get_balance(account)
161
+ get("/v1/account/#{account}")["account"]
162
+ end
163
+ end
164
+ end
165
+