Compare commits
4 Commits
master
...
bug-issue-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8defe21d3 | ||
|
|
25d6ebbcec | ||
|
|
48d3c753ee | ||
|
|
72487c9591 |
11
CHANGELOG.md
Normal file
11
CHANGELOG.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Change Log
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [2.3.3] - 2015-03-12
|
||||||
|
### Changed
|
||||||
|
- Pull refund functionality from 2.3 spec
|
||||||
|
- Bump gem version
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix Bug: GitHub issue #40 - error on return from endpoints that do not have a "data" field
|
||||||
@ -11,9 +11,9 @@ TEST_PASS = ENV['RCTESTPASSWORD']
|
|||||||
DASHBOARD_URL = "#{ROOT_ADDRESS}/dashboard/merchant/home"
|
DASHBOARD_URL = "#{ROOT_ADDRESS}/dashboard/merchant/home"
|
||||||
|
|
||||||
unless
|
unless
|
||||||
ROOT_ADDRESS &&
|
ROOT_ADDRESS &&
|
||||||
TEST_USER &&
|
TEST_USER &&
|
||||||
TEST_PASS
|
TEST_PASS
|
||||||
then
|
then
|
||||||
raise "Missing configuration options - see constants.rb"
|
raise "Missing configuration options - see constants.rb"
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
@invoices
|
||||||
Feature: creating an invoice
|
Feature: creating an invoice
|
||||||
The user won't get any money
|
The user won't get any money
|
||||||
If they can't
|
If they can't
|
||||||
@ -10,9 +11,10 @@ Feature: creating an invoice
|
|||||||
When the user creates an invoice for <price> <currency>
|
When the user creates an invoice for <price> <currency>
|
||||||
Then they should recieve an invoice in response for <price> <currency>
|
Then they should recieve an invoice in response for <price> <currency>
|
||||||
Examples:
|
Examples:
|
||||||
| price | currency |
|
| price | currency |
|
||||||
| "5.23" | "USD" |
|
| "5.23" | "USD" |
|
||||||
| "10.21" | "EUR" |
|
| "10.21" | "EUR" |
|
||||||
|
| "0.225" | "BTC" |
|
||||||
|
|
||||||
Scenario Outline: The invoice contains illegal characters
|
Scenario Outline: The invoice contains illegal characters
|
||||||
When the user creates an invoice for <price> <currency>
|
When the user creates an invoice for <price> <currency>
|
||||||
@ -20,7 +22,7 @@ Feature: creating an invoice
|
|||||||
Examples:
|
Examples:
|
||||||
| price | currency | message |
|
| price | currency | message |
|
||||||
| "5,023" | "USD" | "Price must be formatted as a float" |
|
| "5,023" | "USD" | "Price must be formatted as a float" |
|
||||||
| "3.21" | "EaUR" | "Currency is invalid." |
|
| "3.21" | "EaUR" | "Currency is invalid." |
|
||||||
| "" | "USD" | "Price must be formatted as a float" |
|
| "" | "USD" | "Price must be formatted as a float" |
|
||||||
| "Ten" | "USD" | "Price must be formatted as a float" |
|
| "Ten" | "USD" | "Price must be formatted as a float" |
|
||||||
| "10" | "" | "Currency is invalid." |
|
| "10" | "" | "Currency is invalid." |
|
||||||
|
|||||||
@ -61,5 +61,5 @@ Given(/^the user requests a client\-side pairing$/) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
Then(/^they will receive a claim code$/) do
|
Then(/^they will receive a claim code$/) do
|
||||||
expect(@response["data"].first["pairingCode"] ).not_to be_empty
|
expect(@response.first["pairingCode"] ).not_to be_empty
|
||||||
end
|
end
|
||||||
|
|||||||
@ -66,7 +66,7 @@ def new_client_from_stored_values
|
|||||||
end
|
end
|
||||||
|
|
||||||
def get_token_from_file
|
def get_token_from_file
|
||||||
token = JSON.parse(File.read(BitPay::TOKEN_FILE_PATH))['data'][0]
|
token = JSON.parse(File.read(BitPay::TOKEN_FILE_PATH))[0]
|
||||||
{token['facade'] => token['token']}
|
{token['facade'] => token['token']}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# license Copyright 2011-2014 BitPay, Inc., MIT License
|
# license Copyright 2011-2015 BitPay, Inc., MIT License
|
||||||
# see http://opensource.org/licenses/MIT
|
# see http://opensource.org/licenses/MIT
|
||||||
# or https://github.com/bitpay/php-bitpay-client/blob/master/LICENSE
|
# or https://github.com/bitpay/php-bitpay-client/blob/master/LICENSE
|
||||||
|
|
||||||
@ -7,13 +7,14 @@ require 'net/https'
|
|||||||
require 'json'
|
require 'json'
|
||||||
|
|
||||||
require_relative 'key_utils'
|
require_relative 'key_utils'
|
||||||
|
require_relative 'rest_connector'
|
||||||
|
|
||||||
module BitPay
|
module BitPay
|
||||||
# This class is used to instantiate a BitPay Client object. It is expected to be thread safe.
|
# This class is used to instantiate a BitPay Client object. It is expected to be thread safe.
|
||||||
#
|
#
|
||||||
module SDK
|
module SDK
|
||||||
class Client
|
class Client
|
||||||
|
include BitPay::RestConnector
|
||||||
# @return [Client]
|
# @return [Client]
|
||||||
# @example
|
# @example
|
||||||
# # Create a client with a pem file created by the bitpay client:
|
# # Create a client with a pem file created by the bitpay client:
|
||||||
@ -46,7 +47,8 @@ module BitPay
|
|||||||
# => Pass {pairingCode: 'WfD01d2'} to claim a server-initiated pairing code
|
# => Pass {pairingCode: 'WfD01d2'} to claim a server-initiated pairing code
|
||||||
#
|
#
|
||||||
def pair_client(params={})
|
def pair_client(params={})
|
||||||
pairing_request(params)
|
tokens = post(path: 'tokens', params: params)
|
||||||
|
return tokens["data"]
|
||||||
end
|
end
|
||||||
|
|
||||||
## Compatibility method for pos pairing
|
## Compatibility method for pos pairing
|
||||||
@ -61,27 +63,22 @@ module BitPay
|
|||||||
# Defaults to pos facade, also works with merchant facade
|
# Defaults to pos facade, also works with merchant facade
|
||||||
#
|
#
|
||||||
def create_invoice(price:, currency:, facade: 'pos', params:{})
|
def create_invoice(price:, currency:, facade: 'pos', params:{})
|
||||||
raise BitPay::ArgumentError, "Illegal Argument: Price must be formatted as a float" unless ( price.is_a?(Numeric) || /^[[:digit:]]+(\.[[:digit:]]{2})?$/.match(price) )
|
raise BitPay::ArgumentError, "Illegal Argument: Price must be formatted as a float" unless
|
||||||
|
price.is_a?(Numeric) ||
|
||||||
|
/^[[:digit:]]+(\.[[:digit:]]{2})?$/.match(price) ||
|
||||||
|
currency == 'BTC' && /^[[:digit:]]+(\.[[:digit:]]{1,6})?$/.match(price)
|
||||||
raise BitPay::ArgumentError, "Illegal Argument: Currency is invalid." unless /^[[:upper:]]{3}$/.match(currency)
|
raise BitPay::ArgumentError, "Illegal Argument: Currency is invalid." unless /^[[:upper:]]{3}$/.match(currency)
|
||||||
params.merge!({price: price, currency: currency})
|
params.merge!({price: price, currency: currency})
|
||||||
response = send_request("POST", "invoices", facade: facade, params: params)
|
token = get_token(facade)
|
||||||
response["data"]
|
invoice = post(path: "invoices", token: token, params: params)
|
||||||
|
invoice["data"]
|
||||||
end
|
end
|
||||||
|
|
||||||
## Gets the privileged merchant-version of the invoice
|
|
||||||
# Requires merchant facade token
|
|
||||||
#
|
|
||||||
def get_invoice(id:)
|
|
||||||
response = send_request("GET", "invoices/#{id}", facade: 'merchant')
|
|
||||||
response["data"]
|
|
||||||
end
|
|
||||||
|
|
||||||
## Gets the public version of the invoice
|
## Gets the public version of the invoice
|
||||||
#
|
#
|
||||||
def get_public_invoice(id:)
|
def get_public_invoice(id:)
|
||||||
request = Net::HTTP::Get.new("/invoices/#{id}")
|
invoice = get(path: "invoices/#{id}", public: true)
|
||||||
response = process_request(request)
|
invoice["data"]
|
||||||
response["data"]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
## Checks that the passed tokens are valid by
|
## Checks that the passed tokens are valid by
|
||||||
@ -95,116 +92,12 @@ module BitPay
|
|||||||
tokens.each{|key, value| return false if server_tokens[key] != value}
|
tokens.each{|key, value| return false if server_tokens[key] != value}
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
## Generates REST request to api endpoint
|
|
||||||
# => Defaults to merchant facade unless token or facade is explicitly provided
|
|
||||||
#
|
|
||||||
def send_request(verb, path, facade: 'merchant', params: {}, token: nil)
|
|
||||||
token ||= get_token(facade)
|
|
||||||
|
|
||||||
# Verb-specific logic
|
|
||||||
case verb.upcase
|
|
||||||
when "GET"
|
|
||||||
urlpath = '/' + path + '?token=' + token
|
|
||||||
request = Net::HTTP::Get.new urlpath
|
|
||||||
request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath, @priv_key)
|
|
||||||
|
|
||||||
when "PUT"
|
|
||||||
|
|
||||||
when "POST" # Requires a GUID
|
|
||||||
|
|
||||||
urlpath = '/' + path
|
|
||||||
request = Net::HTTP::Post.new urlpath
|
|
||||||
params[:token] = token
|
|
||||||
params[:guid] = SecureRandom.uuid
|
|
||||||
params[:id] = @client_id
|
|
||||||
request.body = params.to_json
|
|
||||||
request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath + request.body, @priv_key)
|
|
||||||
|
|
||||||
when "DELETE"
|
|
||||||
|
|
||||||
raise(BitPayError, "Invalid HTTP verb: #{verb.upcase}")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Build request headers and submit
|
|
||||||
request['X-Identity'] = @pub_key
|
|
||||||
|
|
||||||
response = process_request(request)
|
|
||||||
end
|
|
||||||
|
|
||||||
##### PRIVATE METHODS #####
|
|
||||||
private
|
private
|
||||||
|
|
||||||
## Processes HTTP Request and returns parsed response
|
|
||||||
# Otherwise throws error
|
|
||||||
#
|
|
||||||
def process_request(request)
|
|
||||||
|
|
||||||
request['User-Agent'] = @user_agent
|
|
||||||
request['Content-Type'] = 'application/json'
|
|
||||||
request['X-BitPay-Plugin-Info'] = 'Rubylib' + VERSION
|
|
||||||
|
|
||||||
begin
|
|
||||||
response = @https.request request
|
|
||||||
rescue => error
|
|
||||||
raise BitPay::ConnectionError, "#{error.message}"
|
|
||||||
end
|
|
||||||
|
|
||||||
if response.kind_of? Net::HTTPSuccess
|
|
||||||
return JSON.parse(response.body)
|
|
||||||
elsif JSON.parse(response.body)["error"]
|
|
||||||
raise(BitPayError, "#{response.code}: #{JSON.parse(response.body)['error']}")
|
|
||||||
else
|
|
||||||
raise BitPayError, "#{response.code}: #{JSON.parse(response.body)}"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
## Fetches the tokens hash from the server and
|
|
||||||
# updates @tokens
|
|
||||||
#
|
|
||||||
def refresh_tokens
|
|
||||||
urlpath = '/tokens'
|
|
||||||
|
|
||||||
request = Net::HTTP::Get.new(urlpath)
|
|
||||||
request['X-Identity'] = @pub_key
|
|
||||||
request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath, @priv_key)
|
|
||||||
|
|
||||||
response = process_request(request)
|
|
||||||
token_array = response["data"] || {}
|
|
||||||
|
|
||||||
tokens = {}
|
|
||||||
token_array.each do |t|
|
|
||||||
tokens[t.keys.first] = t.values.first
|
|
||||||
end
|
|
||||||
|
|
||||||
@tokens = tokens
|
|
||||||
return tokens
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
## Makes a request to /tokens for pairing
|
|
||||||
# Adds passed params as post parameters
|
|
||||||
# If empty params, retrieves server-generated pairing code
|
|
||||||
# If pairingCode key/value is passed, will pair client ID to this account
|
|
||||||
# Returns response hash
|
|
||||||
#
|
|
||||||
def pairing_request(params)
|
|
||||||
urlpath = '/tokens'
|
|
||||||
request = Net::HTTP::Post.new urlpath
|
|
||||||
params[:guid] = SecureRandom.uuid
|
|
||||||
params[:id] = @client_id
|
|
||||||
request.body = params.to_json
|
|
||||||
process_request(request)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_token(facade)
|
|
||||||
token = @tokens[facade] || refresh_tokens[facade] || raise(BitPayError, "Not authorized for facade: #{facade}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def verify_claim_code(claim_code)
|
def verify_claim_code(claim_code)
|
||||||
regex = /^[[:alnum:]]{7}$/
|
regex = /^[[:alnum:]]{7}$/
|
||||||
matches = regex.match(claim_code)
|
matches = regex.match(claim_code)
|
||||||
!(matches.nil?)
|
!(matches.nil?)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
96
lib/bitpay/rest_connector.rb
Normal file
96
lib/bitpay/rest_connector.rb
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# license Copyright 2011-2015 BitPay, Inc., MIT License
|
||||||
|
# see http://opensource.org/licenses/MIT
|
||||||
|
# or https://github.com/bitpay/php-bitpay-client/blob/master/LICENSE
|
||||||
|
|
||||||
|
module BitPay
|
||||||
|
module RestConnector
|
||||||
|
def send_request(verb, path, facade: 'merchant', params: {}, token: nil)
|
||||||
|
token ||= get_token(facade)
|
||||||
|
case verb.upcase
|
||||||
|
when "GET"
|
||||||
|
return get(path: path, token:token)
|
||||||
|
when "POST"
|
||||||
|
return post(path: path, token: token, params: params)
|
||||||
|
else
|
||||||
|
raise(BitPayError, "Invalid HTTP verb: #{verb.upcase}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(path:, token: nil, public: false)
|
||||||
|
urlpath = '/' + path
|
||||||
|
urlpath = urlpath + '?token=' + token if token
|
||||||
|
request = Net::HTTP::Get.new urlpath
|
||||||
|
unless public
|
||||||
|
request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath, @priv_key)
|
||||||
|
request['X-Identity'] = @pub_key
|
||||||
|
end
|
||||||
|
process_request(request)
|
||||||
|
end
|
||||||
|
|
||||||
|
def post(path:, token: nil, params:)
|
||||||
|
urlpath = '/' + path
|
||||||
|
request = Net::HTTP::Post.new urlpath
|
||||||
|
params[:token] = token if token
|
||||||
|
params[:guid] = SecureRandom.uuid
|
||||||
|
params[:id] = @client_id
|
||||||
|
request.body = params.to_json
|
||||||
|
if token
|
||||||
|
request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath + request.body, @priv_key)
|
||||||
|
request['X-Identity'] = @pub_key
|
||||||
|
end
|
||||||
|
process_request(request)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
## Processes HTTP Request and returns parsed response
|
||||||
|
# Otherwise throws error
|
||||||
|
#
|
||||||
|
def process_request(request)
|
||||||
|
request['User-Agent'] = @user_agent
|
||||||
|
request['Content-Type'] = 'application/json'
|
||||||
|
request['X-BitPay-Plugin-Info'] = 'Rubylib' + VERSION
|
||||||
|
|
||||||
|
begin
|
||||||
|
response = @https.request request
|
||||||
|
rescue => error
|
||||||
|
raise BitPay::ConnectionError, "#{error.message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if response.kind_of? Net::HTTPSuccess
|
||||||
|
return JSON.parse(response.body)
|
||||||
|
elsif JSON.parse(response.body)["error"]
|
||||||
|
raise(BitPayError, "#{response.code}: #{JSON.parse(response.body)['error']}")
|
||||||
|
else
|
||||||
|
raise BitPayError, "#{response.code}: #{JSON.parse(response.body)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
## Fetches the tokens hash from the server and
|
||||||
|
# updates @tokens
|
||||||
|
#
|
||||||
|
def refresh_tokens
|
||||||
|
response = get(path: 'tokens')["data"]
|
||||||
|
token_array = response || {}
|
||||||
|
tokens = {}
|
||||||
|
token_array.each do |t|
|
||||||
|
tokens[t.keys.first] = t.values.first
|
||||||
|
end
|
||||||
|
@tokens = tokens
|
||||||
|
return tokens
|
||||||
|
end
|
||||||
|
|
||||||
|
## Makes a request to /tokens for pairing
|
||||||
|
# Adds passed params as post parameters
|
||||||
|
# If empty params, retrieves server-generated pairing code
|
||||||
|
# If pairingCode key/value is passed, will pair client ID to this account
|
||||||
|
# Returns response hash
|
||||||
|
#
|
||||||
|
|
||||||
|
def get_token(facade)
|
||||||
|
token = @tokens[facade] || refresh_tokens[facade] || raise(BitPayError, "Not authorized for facade: #{facade}")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -3,5 +3,5 @@
|
|||||||
# or https://github.com/bitpay/php-bitpay-client/blob/master/LICENSE
|
# or https://github.com/bitpay/php-bitpay-client/blob/master/LICENSE
|
||||||
|
|
||||||
module BitPay
|
module BitPay
|
||||||
VERSION = '2.3.1'
|
VERSION = '2.3.3'
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user