bitpay-sdk 2.1.0
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 +7 -0
- data/.gitignore +7 -0
- data/.travis.yml +2 -0
- data/Gemfile +6 -0
- data/LICENSE.md +7 -0
- data/README.md +81 -0
- data/Rakefile +73 -0
- data/bitpay-sdk.gemspec +35 -0
- data/config/capybara.rb +6 -0
- data/config/constants.rb +3 -0
- data/features/creating_invoices.feature +26 -0
- data/features/pairing.feature +21 -0
- data/features/retrieving_invoices.feature +7 -0
- data/features/step_definitions/invoice_steps.rb +26 -0
- data/features/step_definitions/keygen_steps.rb +51 -0
- data/features/step_definitions/step_helpers.rb +79 -0
- data/lib/bitpay.rb +32 -0
- data/lib/bitpay/cacert.pem +3866 -0
- data/lib/bitpay/client.rb +181 -0
- data/lib/bitpay/key_utils.rb +130 -0
- data/lib/bitpay/version.rb +7 -0
- data/lib/harness.rb +40 -0
- data/spec/client_spec.rb +124 -0
- data/spec/features/pair_spec.rb +29 -0
- data/spec/features/pos_spec.rb +37 -0
- data/spec/features/setup_spec.rb +13 -0
- data/spec/features/verify_tokens_spec.rb +34 -0
- data/spec/key_utils_spec.rb +59 -0
- data/spec/set_constants.sh +10 -0
- data/spec/spec_helper.rb +34 -0
- metadata +269 -0
@@ -0,0 +1,181 @@
|
|
1
|
+
# license Copyright 2011-2014 BitPay, Inc., MIT License
|
2
|
+
# see http://opensource.org/licenses/MIT
|
3
|
+
# or https://github.com/bitpay/php-bitpay-client/blob/master/LICENSE
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
require 'net/https'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
require_relative 'key_utils'
|
10
|
+
|
11
|
+
module BitPay
|
12
|
+
# This class is used to instantiate a BitPay Client object. It is expected to be thread safe.
|
13
|
+
#
|
14
|
+
class Client
|
15
|
+
|
16
|
+
|
17
|
+
# @return [Client]
|
18
|
+
# @example
|
19
|
+
# # Create a client with a pem file created by the bitpay client:
|
20
|
+
# client = BitPay::Client.new
|
21
|
+
def initialize(opts={})
|
22
|
+
@pem = opts[:pem] || ENV['BITPAY_PEM'] || KeyUtils.generate_pem
|
23
|
+
@key = KeyUtils.create_key @pem
|
24
|
+
@priv_key = KeyUtils.get_private_key @key
|
25
|
+
@pub_key = KeyUtils.get_public_key @key
|
26
|
+
@client_id = KeyUtils.generate_sin_from_pem @pem
|
27
|
+
@uri = URI.parse opts[:api_uri] || API_URI
|
28
|
+
@user_agent = opts[:user_agent] || USER_AGENT
|
29
|
+
@https = Net::HTTP.new @uri.host, @uri.port
|
30
|
+
@https.use_ssl = true
|
31
|
+
@https.ca_file = CA_FILE
|
32
|
+
@tokens = opts[:tokens] || {}
|
33
|
+
|
34
|
+
# Option to disable certificate validation in extraordinary circumstance. NOT recommended for production use
|
35
|
+
@https.verify_mode = opts[:insecure] == true ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
|
36
|
+
|
37
|
+
# Option to enable http request debugging
|
38
|
+
@https.set_debug_output($stdout) if opts[:debug] == true
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
def pair_pos_client(claimCode)
|
43
|
+
raise BitPay::ArgumentError, "pairing code is not legal" unless verify_claim_code(claimCode)
|
44
|
+
response = set_pos_token(claimCode)
|
45
|
+
get_token 'pos'
|
46
|
+
response
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_invoice(price:, currency:, facade: 'pos', params:{})
|
50
|
+
raise BitPay::ArgumentError, "Illegal Argument: Price must be formatted as a float" unless ( price.is_a?(Numeric) || /^[[:digit:]]+(\.[[:digit:]]{2})?$/.match(price) )
|
51
|
+
raise BitPay::ArgumentError, "Illegal Argument: Currency is invalid." unless /^[[:upper:]]{3}$/.match(currency)
|
52
|
+
params.merge!({price: price, currency: currency})
|
53
|
+
response = send_request("POST", "invoices", facade: facade, params: params)
|
54
|
+
response["data"]
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_public_invoice(id:)
|
58
|
+
request = Net::HTTP::Get.new("/invoices/#{id}")
|
59
|
+
response = process_request(request)
|
60
|
+
response["data"]
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_token
|
64
|
+
end
|
65
|
+
|
66
|
+
def verify_token
|
67
|
+
server_tokens = load_tokens
|
68
|
+
@tokens.each{|key, value| return false if server_tokens[key] != value}
|
69
|
+
return true
|
70
|
+
end
|
71
|
+
## Generates REST request to api endpoint
|
72
|
+
|
73
|
+
def send_request(verb, path, facade: 'merchant', params: {}, token: nil)
|
74
|
+
token ||= get_token(facade)
|
75
|
+
|
76
|
+
# Verb-specific logic
|
77
|
+
case verb.upcase
|
78
|
+
when "GET"
|
79
|
+
urlpath = '/' + path + '?nonce=' + KeyUtils.nonce + '&token=' + token
|
80
|
+
request = Net::HTTP::Get.new urlpath
|
81
|
+
request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath, @priv_key)
|
82
|
+
|
83
|
+
when "PUT"
|
84
|
+
|
85
|
+
when "POST" # Requires a GUID
|
86
|
+
|
87
|
+
urlpath = '/' + path
|
88
|
+
request = Net::HTTP::Post.new urlpath
|
89
|
+
params[:token] = token
|
90
|
+
params[:nonce] = KeyUtils.nonce
|
91
|
+
params[:guid] = SecureRandom.uuid
|
92
|
+
params[:id] = @client_id
|
93
|
+
request.body = params.to_json
|
94
|
+
request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath + request.body, @priv_key)
|
95
|
+
|
96
|
+
when "DELETE"
|
97
|
+
|
98
|
+
raise(BitPayError, "Invalid HTTP verb: #{verb.upcase}")
|
99
|
+
end
|
100
|
+
|
101
|
+
# Build request headers and submit
|
102
|
+
request['X-Identity'] = @pub_key
|
103
|
+
|
104
|
+
response = process_request(request)
|
105
|
+
end
|
106
|
+
|
107
|
+
##### PRIVATE METHODS #####
|
108
|
+
private
|
109
|
+
|
110
|
+
## Processes HTTP Request and returns parsed response
|
111
|
+
# Otherwise throws error
|
112
|
+
#
|
113
|
+
def process_request(request)
|
114
|
+
|
115
|
+
request['User-Agent'] = @user_agent
|
116
|
+
request['Content-Type'] = 'application/json'
|
117
|
+
request['X-BitPay-Plugin-Info'] = 'Rubylib' + VERSION
|
118
|
+
|
119
|
+
begin
|
120
|
+
response = @https.request request
|
121
|
+
rescue => error
|
122
|
+
raise BitPay::ConnectionError, "#{error.message}"
|
123
|
+
end
|
124
|
+
|
125
|
+
if response.kind_of? Net::HTTPSuccess
|
126
|
+
return JSON.parse(response.body)
|
127
|
+
elsif JSON.parse(response.body)["error"]
|
128
|
+
raise(BitPayError, "#{response.code}: #{JSON.parse(response.body)['error']}")
|
129
|
+
else
|
130
|
+
raise BitPayError, "#{response.code}: #{JSON.parse(response.body)}"
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
## Requests token by appending nonce and signing URL
|
136
|
+
# Returns a hash of available tokens
|
137
|
+
#
|
138
|
+
def load_tokens
|
139
|
+
|
140
|
+
urlpath = '/tokens?nonce=' + KeyUtils.nonce
|
141
|
+
|
142
|
+
request = Net::HTTP::Get.new(urlpath)
|
143
|
+
request['x-identity'] = @pub_key
|
144
|
+
request['x-signature'] = KeyUtils.sign(@uri.to_s + urlpath, @priv_key)
|
145
|
+
|
146
|
+
response = process_request(request)
|
147
|
+
|
148
|
+
token_array = response["data"] || {}
|
149
|
+
|
150
|
+
tokens = {}
|
151
|
+
token_array.each do |t|
|
152
|
+
tokens[t.keys.first] = t.values.first
|
153
|
+
end
|
154
|
+
|
155
|
+
@tokens = tokens
|
156
|
+
return tokens
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
## Retrieves specified token from hash, otherwise tries to refresh @tokens and retry
|
161
|
+
def set_pos_token(claim_code)
|
162
|
+
params = {pairingCode: claim_code}
|
163
|
+
urlpath = '/tokens'
|
164
|
+
request = Net::HTTP::Post.new urlpath
|
165
|
+
params[:guid] = SecureRandom.uuid
|
166
|
+
params[:id] = @client_id
|
167
|
+
request.body = params.to_json
|
168
|
+
process_request(request)
|
169
|
+
end
|
170
|
+
|
171
|
+
def get_token(facade)
|
172
|
+
token = @tokens[facade] || load_tokens[facade] || raise(BitPayError, "Not authorized for facade: #{facade}")
|
173
|
+
end
|
174
|
+
|
175
|
+
def verify_claim_code(claim_code)
|
176
|
+
regex = /^[[:alnum:]]{7}$/
|
177
|
+
matches = regex.match(claim_code)
|
178
|
+
!(matches.nil?)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# license Copyright 2011-2014 BitPay, Inc., MIT License
|
2
|
+
# see http://opensource.org/licenses/MIT
|
3
|
+
# or https://github.com/bitpay/php-bitpay-client/blob/master/LICENSE
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
require 'net/https'
|
7
|
+
require 'json'
|
8
|
+
require 'openssl'
|
9
|
+
require 'ecdsa'
|
10
|
+
require 'securerandom'
|
11
|
+
require 'digest/sha2'
|
12
|
+
require 'cgi'
|
13
|
+
|
14
|
+
module BitPay
|
15
|
+
class KeyUtils
|
16
|
+
class << self
|
17
|
+
def nonce
|
18
|
+
Time.now.utc.strftime('%Y%m%d%H%M%S%L')
|
19
|
+
end
|
20
|
+
|
21
|
+
## Generates a new private key
|
22
|
+
#
|
23
|
+
|
24
|
+
def generate_pem
|
25
|
+
key = OpenSSL::PKey::EC.new("secp256k1")
|
26
|
+
key.generate_key
|
27
|
+
key.to_pem
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_key pem
|
31
|
+
OpenSSL::PKey::EC.new(pem)
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_new_key
|
35
|
+
key = OpenSSL::PKey::EC.new("secp256k1")
|
36
|
+
key.generate_key
|
37
|
+
key
|
38
|
+
end
|
39
|
+
|
40
|
+
## Gets private key from ENV variable or local FS
|
41
|
+
#
|
42
|
+
def get_local_pem_file
|
43
|
+
ENV['BITPAY_PEM'] || (raise BitPayError, MISSING_KEY)
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_private_key key
|
47
|
+
key.private_key.to_int.to_s(16)
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_public_key key
|
51
|
+
key.public_key.group.point_conversion_form = :compressed
|
52
|
+
key.public_key.to_bn.to_s(16).downcase
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_private_key_from_pem pem
|
56
|
+
raise BitPayError, "Missing key" unless pem
|
57
|
+
key = OpenSSL::PKey::EC.new(pem)
|
58
|
+
get_private_key key
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_public_key_from_pem pem
|
62
|
+
raise BitPayError, MISSING_KEY unless pem
|
63
|
+
key = OpenSSL::PKey::EC.new(pem)
|
64
|
+
get_public_key key
|
65
|
+
end
|
66
|
+
|
67
|
+
def generate_sin_from_pem(pem = nil)
|
68
|
+
#http://blog.bitpay.com/2014/07/01/bitauth-for-decentralized-authentication.html
|
69
|
+
#https://en.bitcoin.it/wiki/Identity_protocol_v1
|
70
|
+
|
71
|
+
# NOTE: All Digests are calculated against the binary representation,
|
72
|
+
# hence the requirement to use [].pack("H*") to convert to binary for each step
|
73
|
+
|
74
|
+
#Generate Private Key
|
75
|
+
key = OpenSSL::PKey::EC.new(pem ||= get_local_pem_file)
|
76
|
+
key.public_key.group.point_conversion_form = :compressed
|
77
|
+
public_key = key.public_key.to_bn.to_s(2)
|
78
|
+
step_one = Digest::SHA256.hexdigest(public_key)
|
79
|
+
step_two = Digest::RMD160.hexdigest([step_one].pack("H*"))
|
80
|
+
step_three = "0F02" + step_two
|
81
|
+
step_four_a = Digest::SHA256.hexdigest([step_three].pack("H*"))
|
82
|
+
step_four = Digest::SHA256.hexdigest([step_four_a].pack("H*"))
|
83
|
+
step_five = step_four[0..7]
|
84
|
+
step_six = step_three + step_five
|
85
|
+
encode_base58(step_six)
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
## Generate ECDSA signature
|
90
|
+
# This is the last method that requires the ecdsa gem, which we would like to replace
|
91
|
+
|
92
|
+
def sign(message, privkey)
|
93
|
+
group = ECDSA::Group::Secp256k1
|
94
|
+
digest = Digest::SHA256.digest(message)
|
95
|
+
signature = nil
|
96
|
+
while signature.nil?
|
97
|
+
temp_key = 1 + SecureRandom.random_number(group.order - 1)
|
98
|
+
signature = ECDSA.sign(group, privkey.to_i(16), digest, temp_key)
|
99
|
+
return ECDSA::Format::SignatureDerString.encode(signature).unpack("H*").first
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
########## Private Class Methods ################
|
104
|
+
|
105
|
+
## Base58 Encoding Method
|
106
|
+
#
|
107
|
+
private
|
108
|
+
def encode_base58 (data)
|
109
|
+
code_string = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
110
|
+
base = 58
|
111
|
+
x = data.hex
|
112
|
+
output_string = ""
|
113
|
+
|
114
|
+
while x > 0 do
|
115
|
+
remainder = x % base
|
116
|
+
x = x / base
|
117
|
+
output_string << code_string[remainder]
|
118
|
+
end
|
119
|
+
|
120
|
+
pos = 0
|
121
|
+
while data[pos,2] == "00" do
|
122
|
+
output_string << code_string[0]
|
123
|
+
pos += 2
|
124
|
+
end
|
125
|
+
|
126
|
+
output_string.reverse()
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/lib/harness.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# license Copyright 2011-2014 BitPay, Inc., MIT License
|
2
|
+
# see http://opensource.org/licenses/MIT
|
3
|
+
# or https://github.com/bitpay/php-bitpay-client/blob/master/LICENSE
|
4
|
+
|
5
|
+
require_relative 'bitpay.rb'
|
6
|
+
require_relative 'bitpay/key_utils.rb'
|
7
|
+
|
8
|
+
# Test SIN Generation class methods
|
9
|
+
|
10
|
+
# Generate SIN
|
11
|
+
ENV["PRIV_KEY"] = "16d7c3508ec59773e71ae728d29f41fcf5d1f380c379b99d68fa9f552ce3ebc3"
|
12
|
+
puts "privkey: #{ENV['PRIV_KEY']}"
|
13
|
+
puts "target SIN: TfFVQhy2hQvchv4VVG4c7j4XPa2viJ9HrR8"
|
14
|
+
puts "Derived SIN: #{BitPay::KeyUtils.get_client_id}"
|
15
|
+
|
16
|
+
puts "\n\n------------------\n\n"
|
17
|
+
|
18
|
+
uri = "https://localhost:8088"
|
19
|
+
#name = "Ridonculous.label That shouldn't work really"
|
20
|
+
name = "somethinginnocuous"
|
21
|
+
facade = "pos"
|
22
|
+
client_id = BitPay::KeyUtils.get_client_id
|
23
|
+
|
24
|
+
BitPay::KeyUtils.generate_registration_url(uri,name,facade,client_id)
|
25
|
+
|
26
|
+
puts "\n\n------------------\n\n"
|
27
|
+
|
28
|
+
#### Test Invoice Creation using directly assigned keys
|
29
|
+
## (Ultimately pubkey and SIN should be derived)
|
30
|
+
|
31
|
+
ENV["PRIV_KEY"] = "16d7c3508ec59773e71ae728d29f41fcf5d1f380c379b99d68fa9f552ce3ebc3"
|
32
|
+
#ENV["pub_key"] = "0353a036fb495c5846f26a3727a28198da8336ae4f5aaa09e24c14a4126b5d969d"
|
33
|
+
#ENV['SIN'] = "TfFVQhy2hQvchv4VVG4c7j4XPa2viJ9HrR8"
|
34
|
+
|
35
|
+
client = BitPay::Client.new({insecure: true, debug: false})
|
36
|
+
|
37
|
+
invoice = client.post 'invoices', {:price => 10.00, :currency => 'USD'}
|
38
|
+
|
39
|
+
puts "Here's the invoice: \n" + JSON.pretty_generate(invoice)
|
40
|
+
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
def tokens
|
4
|
+
{"data" =>
|
5
|
+
[{"merchant" => "MERCHANTTOKEN"},
|
6
|
+
{"pos" =>"POSTOKEN"},
|
7
|
+
{"merchant/invoice" => "9kv7gGqZLoQ2fxbKEgfgndLoxwjp5na6VtGSH3sN7buX"}
|
8
|
+
]
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
describe BitPay::Client do
|
13
|
+
let(:bitpay_client) { BitPay::Client.new({api_uri: BitPay::TEST_API_URI}) }
|
14
|
+
let(:claim_code) { "a12bc3d" }
|
15
|
+
|
16
|
+
before do
|
17
|
+
allow(BitPay::KeyUtils).to receive(:nonce).and_return('1')
|
18
|
+
stub_request(:get, /#{BitPay::TEST_API_URI}\/tokens.*/).to_return(:status => 200, :body => tokens.to_json, :headers => {})
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#initialize" do
|
22
|
+
|
23
|
+
it 'should be able to get pem file from the env' do
|
24
|
+
stub_const('ENV', {'BITPAY_PEM' => PEM})
|
25
|
+
expect {bitpay_client}.to_not raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#send_request" do
|
31
|
+
before do
|
32
|
+
stub_const('ENV', {'BITPAY_PEM' => PEM})
|
33
|
+
end
|
34
|
+
|
35
|
+
context "GET" do
|
36
|
+
it 'should generate a get request' do
|
37
|
+
stub_request(:get, /#{BitPay::TEST_API_URI}\/whatever.*/).to_return(:body => '{"awesome": "json"}')
|
38
|
+
bitpay_client.send_request("GET", "whatever", facade: "merchant")
|
39
|
+
expect(WebMock).to have_requested(:get, "#{BitPay::TEST_API_URI}/whatever?nonce=1&token=MERCHANTTOKEN")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "POST" do
|
44
|
+
it 'should generate a post request' do
|
45
|
+
stub_request(:post, /#{BitPay::TEST_API_URI}.*/).to_return(:body => '{"awesome": "json"}')
|
46
|
+
bitpay_client.send_request("POST", "whatever", facade: "merchant")
|
47
|
+
expect(WebMock).to have_requested(:post, "#{BitPay::TEST_API_URI}/whatever")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#pair_pos_client" do
|
54
|
+
before do
|
55
|
+
stub_const('ENV', {'BITPAY_PEM' => PEM})
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'throws a BitPayError with the error message if the token setting fails' do
|
59
|
+
stub_request(:any, /#{BitPay::TEST_API_URI}.*/).to_return(status: 500, body: "{\n \"error\": \"Unable to create token\"\n}")
|
60
|
+
expect { bitpay_client.pair_pos_client(claim_code) }.to raise_error(BitPay::BitPayError, '500: Unable to create token')
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'gracefully handles 4xx errors' do
|
64
|
+
stub_request(:any, /#{BitPay::TEST_API_URI}.*/).to_return(status: 403, body: "{\n \"error\": \"this is a 403 error\"\n}")
|
65
|
+
expect { bitpay_client.pair_pos_client(claim_code) }.to raise_error(BitPay::BitPayError, '403: this is a 403 error')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'short circuits on invalid pairing codes' do
|
69
|
+
100.times do
|
70
|
+
claim_code = an_illegal_claim_code
|
71
|
+
expect{bitpay_client.pair_pos_client(claim_code)}.to raise_error BitPay::ArgumentError, "pairing code is not legal"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#create_invoice" do
|
77
|
+
subject { bitpay_client }
|
78
|
+
before {stub_const('ENV', {'BITPAY_PEM' => PEM})}
|
79
|
+
it { is_expected.to respond_to(:create_invoice) }
|
80
|
+
|
81
|
+
describe "should make the call to the server to create an invoice" do
|
82
|
+
it 'allows numeric input for the price' do
|
83
|
+
stub_request(:post, /#{BitPay::TEST_API_URI}\/invoices.*/).to_return(:body => '{"data": "awesome"}')
|
84
|
+
bitpay_client.create_invoice(price: 20.00, currency: "USD")
|
85
|
+
assert_requested :post, "#{BitPay::TEST_API_URI}/invoices"
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'allows string input for the price' do
|
89
|
+
stub_request(:post, /#{BitPay::TEST_API_URI}\/invoices.*/).to_return(:body => '{"data": "awesome"}')
|
90
|
+
bitpay_client.create_invoice(price: "20.00", currency: "USD")
|
91
|
+
assert_requested :post, "#{BitPay::TEST_API_URI}/invoices"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should pass through the API error message from load_tokens' do
|
96
|
+
stub_request(:get, /#{BitPay::TEST_API_URI}\/tokens.*/).to_return(status: 500, body: '{"error": "load_tokens_error"}')
|
97
|
+
expect { bitpay_client.create_invoice(price: 20, currency: "USD") }.to raise_error(BitPay::BitPayError, '500: load_tokens_error')
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'verifies the validity of the price argument' do
|
101
|
+
expect { bitpay_client.create_invoice(price: "3,999", currency: "USD") }.to raise_error(BitPay::ArgumentError, 'Illegal Argument: Price must be formatted as a float')
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'verifies the validity of the currency argument' do
|
105
|
+
expect { bitpay_client.create_invoice(price: "3999", currency: "UASD") }.to raise_error(BitPay::ArgumentError, 'Illegal Argument: Currency is invalid.')
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#set_token' do
|
110
|
+
subject { bitpay_client }
|
111
|
+
before {stub_const('ENV', {'BITPAY_PEM' => PEM})}
|
112
|
+
it { is_expected.to respond_to(:set_token) }
|
113
|
+
it 'sets a token in the client' do
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "#verify_token" do
|
119
|
+
subject { bitpay_client }
|
120
|
+
before {stub_const('ENV', {'BITPAY_PEM' => PEM})}
|
121
|
+
it { is_expected.to respond_to(:verify_token) }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|