bitpay-client 2.0.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/Rakefile +38 -16
- data/bin/bitpay +1 -1
- data/bitpay-client.gemspec +15 -13
- data/config/constants.rb +2 -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 +73 -0
- data/lib/bitpay/cli.rb +1 -0
- data/lib/bitpay/cli_client.rb +24 -0
- data/lib/bitpay/cli_key_utils.rb +40 -0
- data/lib/bitpay/version.rb +1 -1
- data/lib/bitpay.rb +6 -19
- data/spec/client_spec.rb +48 -22
- data/spec/features/pair_spec.rb +11 -9
- data/spec/features/pos_spec.rb +1 -1
- data/spec/key_utils_spec.rb +0 -61
- data/spec/spec_helper.rb +14 -3
- metadata +90 -58
- data/lib/bitpay/client.rb +0 -160
- data/lib/bitpay/key_utils.rb +0 -143
- data/lib/harness.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4dc42cd2a385367410f051b9a7def43a3ff3021
|
4
|
+
data.tar.gz: c6bd1df8c6e5d47deb49afae0ca2caf0c376f6d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6cb61d057cae19d839dc050028c196256c0837e0f64424985aef98df2dd5d3dd6563c3222b5dd7e0310b1931359916c9ce6c178cab0c30f692fdeac72d82ef6b
|
7
|
+
data.tar.gz: 0e326cd00defdeeb8bae777cc42c5189155683aac1be108dcf05801b84dc5f4bab47d8554da6103ecb35514ec51f2c6033c0bc6c06a15a8aadcc75dcbe410aaa
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# BitPay Library for Ruby [![](https://
|
1
|
+
# BitPay Library for Ruby [![](https://travis-ci.org/bitpay/ruby-cli.svg)](http://travis-ci.org/bitpay/ruby-cli)
|
2
2
|
Powerful, flexible, lightweight interface to the BitPay Bitcoin Payment Gateway API.
|
3
3
|
|
4
4
|
## Installation
|
data/Rakefile
CHANGED
@@ -3,6 +3,8 @@ require 'rspec/core/rake_task'
|
|
3
3
|
require 'capybara'
|
4
4
|
require 'capybara/poltergeist'
|
5
5
|
require 'mongo'
|
6
|
+
require 'cucumber'
|
7
|
+
require 'cucumber/rake/task'
|
6
8
|
|
7
9
|
require_relative 'config/constants.rb'
|
8
10
|
require_relative 'config/capybara.rb'
|
@@ -11,32 +13,24 @@ RSpec::Core::RakeTask.new(:spec)
|
|
11
13
|
|
12
14
|
task :default => :spec
|
13
15
|
|
16
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
17
|
+
t.cucumber_opts = "features --format pretty"
|
18
|
+
end
|
19
|
+
|
14
20
|
desc "Bitpay Tasks"
|
15
21
|
namespace :bitpay do
|
16
22
|
|
17
23
|
desc "Clear all claim codes from the test server."
|
18
24
|
task :clear_claim_codes do
|
19
25
|
puts "clearing claim codes"
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
Capybara.click_button('loginButton')
|
25
|
-
Capybara.click_link "My Account"
|
26
|
-
Capybara.click_link "API Tokens", match: :first
|
27
|
-
while Capybara.page.has_selector?(".token-claimcode") || Capybara.page.has_selector?(".token-requiredsins-key") do
|
28
|
-
Capybara.page.find(".api-manager-actions-edit", match: :first).click
|
29
|
-
Capybara.page.find(".api-manager-actions-revoke", match: :first).click
|
30
|
-
Capybara.click_button("Confirm Revoke")
|
31
|
-
# this back and forth bit is here because no other reload mechanism worked, and without it the task errors out: either because it can't find the revoke button or it finds multiple elements at each click point
|
32
|
-
Capybara.page.go_back
|
33
|
-
Capybara.click_link "API Tokens", match: :first
|
34
|
-
end
|
26
|
+
client = Mongo::MongoClient.new
|
27
|
+
db = client['bitpay-dev']
|
28
|
+
coll = db['tokenaccesses']
|
29
|
+
coll.remove()
|
35
30
|
puts "claim codes cleared"
|
36
31
|
end
|
37
32
|
|
38
33
|
desc "Clear rate limiters from local mongo host"
|
39
|
-
|
40
34
|
task :clear_rate_limiters do
|
41
35
|
puts "clearing rate limiters"
|
42
36
|
client = Mongo::MongoClient.new
|
@@ -46,7 +40,35 @@ namespace :bitpay do
|
|
46
40
|
puts "rate limiters cleared"
|
47
41
|
end
|
48
42
|
|
43
|
+
desc "Clear local pem and token file"
|
44
|
+
task :clear_local_files do
|
45
|
+
puts "clearing local files"
|
46
|
+
HOME_DIR = File.join(Dir.home, '.bitpay')
|
47
|
+
KEY_FILE = File.join(HOME_DIR, 'bitpay.pem')
|
48
|
+
TOKEN_FILE = File.join(HOME_DIR, 'tokens.json')
|
49
|
+
File.delete(KEY_FILE) if File.file?(KEY_FILE)
|
50
|
+
File.delete(TOKEN_FILE) if File.file?(TOKEN_FILE)
|
51
|
+
puts "local files cleared"
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Clear tokens, rate limiters, and local files."
|
55
|
+
task :clear do
|
56
|
+
["bitpay:clear_local_files", "bitpay:clear_rate_limiters", "bitpay:clear_claim_codes"].each{|task| Rake::Task[task].reenable}
|
57
|
+
["bitpay:clear_local_files", "bitpay:clear_rate_limiters", "bitpay:clear_claim_codes"].each{|task| Rake::Task[task].invoke}
|
58
|
+
end
|
59
|
+
|
49
60
|
desc "Run specs and clear claim codes and rate_limiters."
|
50
61
|
task :spec_clear => ['spec', 'clear_claim_codes', 'clear_rate_limiters']
|
51
62
|
|
63
|
+
desc "Run specs, clear data, run cukes, clear data"
|
64
|
+
task :tests_clear do
|
65
|
+
Rake::Task["bitpay:clear"].invoke
|
66
|
+
Rake::Task["spec"].invoke
|
67
|
+
Rake::Task["bitpay:clear"].reenable
|
68
|
+
Rake::Task["bitpay:clear"].invoke
|
69
|
+
Rake::Task["features"].invoke
|
70
|
+
Rake::Task["bitpay:clear"].reenable
|
71
|
+
Rake::Task["bitpay:clear"].invoke
|
72
|
+
end
|
73
|
+
|
52
74
|
end
|
data/bin/bitpay
CHANGED
data/bitpay-client.gemspec
CHANGED
@@ -17,19 +17,21 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.bindir = 'bin'
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
|
20
|
-
s.add_dependency '
|
21
|
-
s.add_dependency '
|
22
|
-
s.add_dependency '
|
20
|
+
s.add_dependency 'bitpay-sdk', '~>2.1'
|
21
|
+
s.add_dependency 'json', '~> 1.8.1'
|
22
|
+
s.add_dependency 'rack', '~> 1.5.2'
|
23
|
+
s.add_dependency 'ecdsa', '~> 1.2.0'
|
23
24
|
s.add_dependency 'commander'
|
24
25
|
|
25
|
-
s.add_development_dependency 'rake'
|
26
|
-
s.add_development_dependency 'webmock'
|
27
|
-
s.add_development_dependency 'pry'
|
28
|
-
s.add_development_dependency 'pry-byebug'
|
29
|
-
s.add_development_dependency 'pry-rescue'
|
30
|
-
s.add_development_dependency 'capybara'
|
31
|
-
s.add_development_dependency '
|
32
|
-
s.add_development_dependency '
|
33
|
-
s.add_development_dependency '
|
34
|
-
s.add_development_dependency '
|
26
|
+
s.add_development_dependency 'rake', '~> 10.3.2'
|
27
|
+
s.add_development_dependency 'webmock', '~> 1.18.0'
|
28
|
+
s.add_development_dependency 'pry', '~> 0.10.1'
|
29
|
+
s.add_development_dependency 'pry-byebug', '~> 2.0.0'
|
30
|
+
s.add_development_dependency 'pry-rescue', '~> 1.4.1'
|
31
|
+
s.add_development_dependency 'capybara', '~> 2.4.3'
|
32
|
+
s.add_development_dependency 'cucumber', '~> 1.3.17'
|
33
|
+
s.add_development_dependency 'poltergeist', '~> 1.5.1'
|
34
|
+
s.add_development_dependency 'airborne', '~> 0.0.20'
|
35
|
+
s.add_development_dependency 'rspec', '~> 3.1.0'
|
36
|
+
s.add_development_dependency 'mongo', '~> 1.11.1'
|
35
37
|
end
|
data/config/constants.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
Feature: creating an invoice
|
2
|
+
The user won't get any money
|
3
|
+
If they can't
|
4
|
+
Create Invoices
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given the user is authenticated with BitPay
|
8
|
+
|
9
|
+
Scenario Outline: The request is correct
|
10
|
+
When the user creates an invoice for <price> <currency>
|
11
|
+
Then they should recieve an invoice in response for <price> <currency>
|
12
|
+
Examples:
|
13
|
+
| price | currency |
|
14
|
+
| "500.23" | "USD" |
|
15
|
+
| "300.21" | "EUR" |
|
16
|
+
|
17
|
+
Scenario Outline: The invoice contains illegal characters
|
18
|
+
When the user creates an invoice for <price> <currency>
|
19
|
+
Then they will receive a BitPay::ArgumentError matching <message>
|
20
|
+
Examples:
|
21
|
+
| price | currency | message |
|
22
|
+
| "50,023" | "USD" | "Price must be formatted as a float" |
|
23
|
+
| "300.21" | "EaUR" | "Currency is invalid." |
|
24
|
+
| "" | "USD" | "Price must be formatted as a float" |
|
25
|
+
| "Ten" | "USD" | "Price must be formatted as a float" |
|
26
|
+
| "100" | "" | "Currency is invalid." |
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Feature: pairing with bitpay
|
2
|
+
In order to access bitpay
|
3
|
+
It is required that the library
|
4
|
+
Is able to pair successfully
|
5
|
+
|
6
|
+
Scenario: the client has a correct pairing code
|
7
|
+
Given the user pairs with BitPay with a valid pairing code
|
8
|
+
Then the user is paired with BitPay
|
9
|
+
|
10
|
+
Scenario Outline: the client has a bad pairing code
|
11
|
+
Given the user fails to pair with a semantically <valid> code <code>
|
12
|
+
Then they will receive a <error> matching <message>
|
13
|
+
Examples:
|
14
|
+
| valid | code | error | message |
|
15
|
+
| valid | "a1b2c3d" | BitPay::BitPayError | "500: Unable to create token" |
|
16
|
+
| invalid | "a1b2c3d4" | BitPay::ArgumentError | "pairing code is not legal" |
|
17
|
+
|
18
|
+
Scenario: the client has a bad port configuration to a closed port
|
19
|
+
When the fails to pair with BitPay because of an incorrect port
|
20
|
+
Then they will receive a BitPay::ConnectionError matching "Connection refused"
|
21
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
When(/^the user (?:tries to |)creates? an invoice (?:for|without) "(.*?)" (?:or |and |)"(.*?)"$/) do |price, currency|
|
2
|
+
begin
|
3
|
+
@response = @client.create_invoice(price: price, currency: currency)
|
4
|
+
rescue => error
|
5
|
+
@error = error
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Then(/^they should recieve an invoice in response for "(.*?)" "(.*?)"$/) do |price, currency|
|
10
|
+
raise "#{@response['price']} != #{price} or #{@response['currency']} != #{currency}" unless (price == @response['price'].to_s && currency == @response['currency'])
|
11
|
+
end
|
12
|
+
|
13
|
+
Given(/^there is an invalid token$/) do
|
14
|
+
pending # express the regexp above with the code you wish you had
|
15
|
+
end
|
16
|
+
|
17
|
+
Given(/^that a user knows an invoice id$/) do
|
18
|
+
client = new_paired_client
|
19
|
+
@id = (client.create_invoice(price: 100, currency: "USD" ))['id']
|
20
|
+
end
|
21
|
+
|
22
|
+
Then(/^they can retrieve that invoice$/) do
|
23
|
+
invoice = BitPay::Client.new(api_uri: ROOT_ADDRESS, insecure: true).get_public_invoice(id: @id)
|
24
|
+
raise "That's the wrong invoice" unless invoice['id'] == @id
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
@token = nil
|
2
|
+
@error = nil
|
3
|
+
|
4
|
+
When(/^the user pairs with BitPay(?: with a valid pairing code|)$/) do
|
5
|
+
claim_code = get_claim_code_from_server
|
6
|
+
pem = BitPay::KeyUtils.generate_pem
|
7
|
+
@client = BitPay::Client.new(api_uri: ROOT_ADDRESS, pem: pem, insecure: true)
|
8
|
+
@token = @client.pair_pos_client(claim_code)
|
9
|
+
end
|
10
|
+
|
11
|
+
When(/^the fails to pair with BitPay because of an incorrect port$/) do
|
12
|
+
pem = BitPay::KeyUtils.generate_pem
|
13
|
+
address = ROOT_ADDRESS.split(':').slice(0,2).join(':') + ":999"
|
14
|
+
client = BitPay::Client.new(api_uri: address, pem: pem, insecure: true)
|
15
|
+
begin
|
16
|
+
client.pair_pos_client("1ab2c34")
|
17
|
+
raise "pairing unexpectedly worked"
|
18
|
+
rescue => error
|
19
|
+
@error = error
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Given(/^the user is authenticated with BitPay$/) do
|
25
|
+
@client = new_client_from_stored_values
|
26
|
+
end
|
27
|
+
|
28
|
+
Given(/^the user is paired with BitPay$/) do
|
29
|
+
raise "Client is not paired" unless @client.verify_token
|
30
|
+
end
|
31
|
+
|
32
|
+
Given(/^the user has a bad pairing_code "(.*?)"$/) do |arg1|
|
33
|
+
# This is a no-op, pairing codes are transient and never actually saved
|
34
|
+
end
|
35
|
+
|
36
|
+
Then(/^the user fails to pair with a semantically (?:in|)valid code "(.*?)"$/) do |code|
|
37
|
+
pem = BitPay::KeyUtils.generate_pem
|
38
|
+
client = BitPay::Client.new(api_uri: ROOT_ADDRESS, pem: pem, insecure: true)
|
39
|
+
begin
|
40
|
+
client.pair_pos_client(code)
|
41
|
+
raise "pairing unexpectedly worked"
|
42
|
+
rescue => error
|
43
|
+
@error = error
|
44
|
+
true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Then(/^they will receive an? (.*?) matching "(.*?)"$/) do |error_class, error_message|
|
49
|
+
raise "Error: #{@error.class}, message: #{@error.message}" unless Object.const_get(error_class) == @error.class && @error.message.include?(error_message)
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'capybara/poltergeist'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
require File.join File.dirname(__FILE__), '..', '..', 'lib', 'bitpay.rb'
|
5
|
+
require_relative '../../config/constants.rb'
|
6
|
+
require_relative '../../config/capybara.rb'
|
7
|
+
|
8
|
+
#
|
9
|
+
## Test Variables
|
10
|
+
#
|
11
|
+
#PEM = "-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEICg7E4NN53YkaWuAwpoqjfAofjzKI7Jq1f532dX+0O6QoAcGBSuBBAAK\noUQDQgAEjZcNa6Kdz6GQwXcUD9iJ+t1tJZCx7hpqBuJV2/IrQBfue8jh8H7Q/4vX\nfAArmNMaGotTpjdnymWlMfszzXJhlw==\n-----END EC PRIVATE KEY-----\n"
|
12
|
+
#
|
13
|
+
#PUB_KEY = '038d970d6ba29dcfa190c177140fd889fadd6d2590b1ee1a6a06e255dbf22b4017'
|
14
|
+
#CLIENT_ID = "TeyN4LPrXiG5t2yuSamKqP3ynVk3F52iHrX"
|
15
|
+
module BitPay
|
16
|
+
# Location for API Credentials
|
17
|
+
BITPAY_CREDENTIALS_DIR = File.join(Dir.home, ".bitpay")
|
18
|
+
PRIVATE_KEY_FILE = 'bitpay.pem'
|
19
|
+
PRIVATE_KEY_PATH = File.join(BITPAY_CREDENTIALS_DIR, PRIVATE_KEY_FILE)
|
20
|
+
TOKEN_FILE = 'tokens.json'
|
21
|
+
TOKEN_FILE_PATH = File.join(BITPAY_CREDENTIALS_DIR, TOKEN_FILE)
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_claim_code_from_server
|
25
|
+
Capybara::visit ROOT_ADDRESS
|
26
|
+
if logged_in
|
27
|
+
Capybara::visit "#{ROOT_ADDRESS}/home"
|
28
|
+
else
|
29
|
+
log_in
|
30
|
+
end
|
31
|
+
Capybara::click_link "My Account"
|
32
|
+
Capybara::click_link "API Tokens", match: :first
|
33
|
+
Capybara::find(".token-access-new-button").find(".btn").find(".icon-plus").click
|
34
|
+
sleep 0.25
|
35
|
+
Capybara::find_button("Add Token", match: :first).click
|
36
|
+
Capybara::find(".token-claimcode", match: :first).text
|
37
|
+
end
|
38
|
+
|
39
|
+
def log_in
|
40
|
+
Capybara::click_link('Login')
|
41
|
+
Capybara::fill_in 'email', :with => TEST_USER
|
42
|
+
Capybara::fill_in 'password', :with => TEST_PASS
|
43
|
+
Capybara::click_button('loginButton')
|
44
|
+
end
|
45
|
+
|
46
|
+
def new_paired_client
|
47
|
+
claim_code = get_claim_code_from_server
|
48
|
+
pem = BitPay::KeyUtils.generate_pem
|
49
|
+
client = BitPay::Client.new(api_uri: ROOT_ADDRESS, pem: pem, insecure: true)
|
50
|
+
client.pair_pos_client(claim_code)
|
51
|
+
client
|
52
|
+
end
|
53
|
+
|
54
|
+
def new_client_from_stored_values
|
55
|
+
if File.file?(BitPay::PRIVATE_KEY_PATH) && File.file?(BitPay::TOKEN_FILE_PATH)
|
56
|
+
token = get_token_from_file
|
57
|
+
pem = File.read(BitPay::PRIVATE_KEY_PATH)
|
58
|
+
BitPay::Client.new(pem: pem, tokens: token, insecure: true, api_uri: ROOT_ADDRESS )
|
59
|
+
else
|
60
|
+
claim_code = get_claim_code_from_server
|
61
|
+
client = BitPay::Client.new(api_uri: ROOT_ADDRESS, insecure: true)
|
62
|
+
client.pair_pos_client(claim_code)
|
63
|
+
client
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_token_from_file
|
68
|
+
token = JSON.parse(File.read(BitPay::TOKEN_FILE_PATH))
|
69
|
+
end
|
70
|
+
|
71
|
+
def logged_in
|
72
|
+
Capybara::has_link?('Dashboard')
|
73
|
+
end
|
data/lib/bitpay/cli.rb
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
module BitPay
|
2
|
+
class Client < BitPay::SDK::Client
|
3
|
+
def initialize opts={}
|
4
|
+
pem = File.read(PRIVATE_KEY_PATH) if File.exists?(PRIVATE_KEY_PATH)
|
5
|
+
pem = BitPay::KeyUtils.generate_pem unless File.exists?(PRIVATE_KEY_PATH)
|
6
|
+
pem = opts[:pem] if opts[:pem]
|
7
|
+
File.write(PRIVATE_KEY_PATH, pem)
|
8
|
+
opts[:pem] = pem
|
9
|
+
super opts
|
10
|
+
end
|
11
|
+
|
12
|
+
def pair_pos_client claim_code
|
13
|
+
response = super
|
14
|
+
token = get_token_from_response response
|
15
|
+
File.write(TOKEN_FILE_PATH, token)
|
16
|
+
return response
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def get_token_from_response response
|
21
|
+
({response["data"][0]["facade"] => response["data"][0]["token"]}).to_json
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -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 '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
|
+
## Generates a new private key and writes to local FS
|
18
|
+
#
|
19
|
+
def retrieve_or_generate_pem
|
20
|
+
begin
|
21
|
+
pem = get_local_pem_file
|
22
|
+
rescue
|
23
|
+
pem = generate_pem
|
24
|
+
write_pem_file pem
|
25
|
+
end
|
26
|
+
pem
|
27
|
+
end
|
28
|
+
|
29
|
+
def write_pem_file pem
|
30
|
+
FileUtils.mkdir_p(BITPAY_CREDENTIALS_DIR)
|
31
|
+
File.open(PRIVATE_KEY_PATH, 'w') { |file| file.write(pem) }
|
32
|
+
end
|
33
|
+
## Gets private key from ENV variable or local FS
|
34
|
+
#
|
35
|
+
def get_local_pem_file
|
36
|
+
File.read(PRIVATE_KEY_PATH) || (raise BitPayError, MISSING_KEY)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/bitpay/version.rb
CHANGED
data/lib/bitpay.rb
CHANGED
@@ -4,31 +4,18 @@
|
|
4
4
|
|
5
5
|
libdir = File.dirname(__FILE__)
|
6
6
|
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
7
|
-
require '
|
7
|
+
require 'bitpay_sdk'
|
8
|
+
require 'bitpay/cli_client'
|
9
|
+
require 'bitpay/cli_key_utils'
|
8
10
|
require 'bitpay/version'
|
9
11
|
|
10
12
|
module BitPay
|
11
13
|
|
12
|
-
|
13
|
-
# As sourced from http://curl.haxx.se/ca/cacert.pem
|
14
|
-
CA_FILE = File.join File.dirname(__FILE__), 'bitpay','cacert.pem'
|
15
|
-
|
16
|
-
# Location of API
|
17
|
-
API_URI = 'https://bitpay.com'
|
18
|
-
TEST_API_URI = 'https://test.bitpay.com'
|
19
|
-
CLIENT_REGISTRATION_PATH = '/api-access-request'
|
20
|
-
|
21
|
-
# Location for API Credentials
|
22
|
-
BITPAY_CREDENTIALS_DIR = File.join(Dir.home, ".bitpay")
|
14
|
+
BITPAY_CREDENTIALS_DIR = ENV['RCBITPAYROOT'] || File.expand_path("~") + "/.bitpay/"
|
23
15
|
PRIVATE_KEY_FILE = 'bitpay.pem'
|
24
16
|
PRIVATE_KEY_PATH = File.join(BITPAY_CREDENTIALS_DIR, PRIVATE_KEY_FILE)
|
17
|
+
TOKEN_FILE = 'tokens.json'
|
18
|
+
TOKEN_FILE_PATH = File.join(BITPAY_CREDENTIALS_DIR, TOKEN_FILE)
|
25
19
|
|
26
|
-
# User agent reported to API
|
27
|
-
USER_AGENT = 'ruby-bitpay-client '+VERSION
|
28
|
-
|
29
20
|
MISSING_KEY = 'No Private Key specified. Pass priv_key or set ENV variable PRIV_KEY'
|
30
|
-
MISSING_PEM = 'No pem file specified. Pass pem or set ENV variable BITPAY_PEM'
|
31
|
-
|
32
|
-
class BitPayError < StandardError; end
|
33
|
-
|
34
21
|
end
|
data/spec/client_spec.rb
CHANGED
@@ -11,25 +11,15 @@ end
|
|
11
11
|
|
12
12
|
describe BitPay::Client do
|
13
13
|
let(:bitpay_client) { BitPay::Client.new({api_uri: BitPay::TEST_API_URI}) }
|
14
|
+
let(:claim_code) { "a12bc3d" }
|
14
15
|
|
15
16
|
before do
|
16
17
|
allow(BitPay::KeyUtils).to receive(:nonce).and_return('1')
|
17
18
|
stub_request(:get, /#{BitPay::TEST_API_URI}\/tokens.*/).to_return(:status => 200, :body => tokens.to_json, :headers => {})
|
18
19
|
end
|
19
20
|
|
20
|
-
describe "#initialize" do
|
21
|
-
|
22
|
-
it 'should be able to get pem file from the env' do
|
23
|
-
stub_const('ENV', {'BITPAY_PEM' => PEM})
|
24
|
-
expect {bitpay_client}.to_not raise_error
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
21
|
|
29
22
|
describe "#send_request" do
|
30
|
-
before do
|
31
|
-
stub_const('ENV', {'BITPAY_PEM' => PEM})
|
32
|
-
end
|
33
23
|
|
34
24
|
context "GET" do
|
35
25
|
it 'should generate a get request' do
|
@@ -50,34 +40,70 @@ describe BitPay::Client do
|
|
50
40
|
end
|
51
41
|
|
52
42
|
describe "#pair_pos_client" do
|
53
|
-
|
43
|
+
before do
|
54
44
|
stub_const('ENV', {'BITPAY_PEM' => PEM})
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'throws a BitPayError with the error message if the token setting fails' do
|
55
48
|
stub_request(:any, /#{BitPay::TEST_API_URI}.*/).to_return(status: 500, body: "{\n \"error\": \"Unable to create token\"\n}")
|
56
|
-
expect { bitpay_client.pair_pos_client(
|
49
|
+
expect { bitpay_client.pair_pos_client(claim_code) }.to raise_error(BitPay::BitPayError, '500: Unable to create token')
|
57
50
|
end
|
58
51
|
|
59
52
|
it 'gracefully handles 4xx errors' do
|
60
|
-
stub_const('ENV', {'BITPAY_PEM' => PEM})
|
61
53
|
stub_request(:any, /#{BitPay::TEST_API_URI}.*/).to_return(status: 403, body: "{\n \"error\": \"this is a 403 error\"\n}")
|
62
|
-
expect { bitpay_client.pair_pos_client(
|
54
|
+
expect { bitpay_client.pair_pos_client(claim_code) }.to raise_error(BitPay::BitPayError, '403: this is a 403 error')
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'short circuits on invalid pairing codes' do
|
58
|
+
100.times do
|
59
|
+
claim_code = an_illegal_claim_code
|
60
|
+
expect{bitpay_client.pair_pos_client(claim_code)}.to raise_error BitPay::ArgumentError, "pairing code is not legal"
|
61
|
+
end
|
63
62
|
end
|
64
63
|
end
|
65
64
|
|
66
65
|
describe "#create_invoice" do
|
67
66
|
subject { bitpay_client }
|
68
|
-
before {stub_const('ENV', {'BITPAY_PEM' => PEM})}
|
69
67
|
it { is_expected.to respond_to(:create_invoice) }
|
70
68
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
describe "should make the call to the server to create an invoice" do
|
70
|
+
it 'allows numeric input for the price' do
|
71
|
+
stub_request(:post, /#{BitPay::TEST_API_URI}\/invoices.*/).to_return(:body => '{"data": "awesome"}')
|
72
|
+
bitpay_client.create_invoice(price: 20.00, currency: "USD")
|
73
|
+
assert_requested :post, "#{BitPay::TEST_API_URI}/invoices"
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'allows string input for the price' do
|
77
|
+
stub_request(:post, /#{BitPay::TEST_API_URI}\/invoices.*/).to_return(:body => '{"data": "awesome"}')
|
78
|
+
bitpay_client.create_invoice(price: "20.00", currency: "USD")
|
79
|
+
assert_requested :post, "#{BitPay::TEST_API_URI}/invoices"
|
80
|
+
end
|
75
81
|
end
|
76
82
|
|
77
83
|
it 'should pass through the API error message from load_tokens' do
|
78
84
|
stub_request(:get, /#{BitPay::TEST_API_URI}\/tokens.*/).to_return(status: 500, body: '{"error": "load_tokens_error"}')
|
79
|
-
expect { bitpay_client.create_invoice(
|
85
|
+
expect { bitpay_client.create_invoice(price: 20, currency: "USD") }.to raise_error(BitPay::BitPayError, '500: load_tokens_error')
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'verifies the validity of the price argument' do
|
89
|
+
expect { bitpay_client.create_invoice(price: "3,999", currency: "USD") }.to raise_error(BitPay::ArgumentError, 'Illegal Argument: Price must be formatted as a float')
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'verifies the validity of the currency argument' do
|
93
|
+
expect { bitpay_client.create_invoice(price: "3999", currency: "UASD") }.to raise_error(BitPay::ArgumentError, 'Illegal Argument: Currency is invalid.')
|
80
94
|
end
|
81
95
|
end
|
82
|
-
end
|
83
96
|
|
97
|
+
describe '#set_token' do
|
98
|
+
subject { bitpay_client }
|
99
|
+
it { is_expected.to respond_to(:set_token) }
|
100
|
+
it 'sets a token in the client' do
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#verify_token" do
|
106
|
+
subject { bitpay_client }
|
107
|
+
it { is_expected.to respond_to(:verify_token) }
|
108
|
+
end
|
109
|
+
end
|
data/spec/features/pair_spec.rb
CHANGED
@@ -14,17 +14,19 @@ describe "pairing a token", javascript: true, type: :feature do
|
|
14
14
|
click_button("Add Token")
|
15
15
|
find(".token-claimcode", match: :first).text
|
16
16
|
end
|
17
|
-
|
18
|
-
|
17
|
+
context "when no pem file exists" do
|
18
|
+
before do
|
19
|
+
File.delete(BitPay::PRIVATE_KEY_PATH) if File.exists?(BitPay::PRIVATE_KEY_PATH)
|
20
|
+
File.delete(BitPay::TOKEN_FILE) if File.exists?(BitPay::TOKEN_FILE)
|
21
|
+
`./bin/bitpay pair #{claimCode} --insecure #{ROOT_ADDRESS}`
|
22
|
+
end
|
19
23
|
|
20
|
-
|
21
|
-
|
22
|
-
expect(client.instance_variable_get(:@tokens)).to be_empty
|
24
|
+
it "should save a pem file when pairing" do
|
25
|
+
expect(File.exists?(BitPay::PRIVATE_KEY_PATH)).to eq true
|
23
26
|
end
|
24
|
-
|
25
|
-
|
26
|
-
expect(
|
27
|
+
|
28
|
+
it "should save a token when pairing" do
|
29
|
+
expect(JSON.parse(File.read(BitPay::TOKEN_FILE_PATH))["pos"]).to_not be_nil
|
27
30
|
end
|
28
31
|
end
|
29
|
-
|
30
32
|
end
|
data/spec/features/pos_spec.rb
CHANGED
@@ -24,7 +24,7 @@ describe "create an invoice", javascript: true, type: :feature do
|
|
24
24
|
@client ||= set_client.call
|
25
25
|
@invoice_id ||= SecureRandom.uuid
|
26
26
|
@price ||= (100..150).to_a.sample
|
27
|
-
@invoice = @client.create_invoice(
|
27
|
+
@invoice = @client.create_invoice(currency: "USD", price: @price)
|
28
28
|
end
|
29
29
|
|
30
30
|
it "should create an invoice" do
|