ruby-client/lib/bitpay/client.rb
Paul Daigle ad3a68e009 Change namespace of Client to SDK::Client
And bumps gem version, deletes redundant files, removes redundant
requires.
2015-01-07 10:46:46 -05:00

184 lines
5.9 KiB
Ruby

# license Copyright 2011-2014 BitPay, Inc., MIT License
# see http://opensource.org/licenses/MIT
# or https://github.com/bitpay/php-bitpay-client/blob/master/LICENSE
require 'uri'
require 'net/https'
require 'json'
require_relative 'key_utils'
module BitPay
# This class is used to instantiate a BitPay Client object. It is expected to be thread safe.
#
module SDK
class Client
# @return [Client]
# @example
# # Create a client with a pem file created by the bitpay client:
# client = BitPay::Client.new
def initialize(opts={})
@pem = opts[:pem] || ENV['BITPAY_PEM'] || KeyUtils.generate_pem
@key = KeyUtils.create_key @pem
@priv_key = KeyUtils.get_private_key @key
@pub_key = KeyUtils.get_public_key @key
@client_id = KeyUtils.generate_sin_from_pem @pem
@uri = URI.parse opts[:api_uri] || API_URI
@user_agent = opts[:user_agent] || USER_AGENT
@https = Net::HTTP.new @uri.host, @uri.port
@https.use_ssl = true
@https.ca_file = CA_FILE
@tokens = opts[:tokens] || {}
# Option to disable certificate validation in extraordinary circumstance. NOT recommended for production use
@https.verify_mode = opts[:insecure] == true ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
# Option to enable http request debugging
@https.set_debug_output($stdout) if opts[:debug] == true
end
def pair_pos_client(claimCode)
raise BitPay::ArgumentError, "pairing code is not legal" unless verify_claim_code(claimCode)
response = set_pos_token(claimCode)
get_token 'pos'
response
end
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: Currency is invalid." unless /^[[:upper:]]{3}$/.match(currency)
params.merge!({price: price, currency: currency})
response = send_request("POST", "invoices", facade: facade, params: params)
response["data"]
end
def get_public_invoice(id:)
request = Net::HTTP::Get.new("/invoices/#{id}")
response = process_request(request)
response["data"]
end
def set_token
end
def verify_token
server_tokens = load_tokens
@tokens.each{|key, value| return false if server_tokens[key] != value}
return true
end
## Generates REST request to api endpoint
def send_request(verb, path, facade: 'merchant', params: {}, token: nil)
token ||= get_token(facade)
# Verb-specific logic
case verb.upcase
when "GET"
urlpath = '/' + path + '?nonce=' + KeyUtils.nonce + '&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[:nonce] = KeyUtils.nonce
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
## 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
## Requests token by appending nonce and signing URL
# Returns a hash of available tokens
#
def load_tokens
urlpath = '/tokens?nonce=' + KeyUtils.nonce
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
## Retrieves specified token from hash, otherwise tries to refresh @tokens and retry
def set_pos_token(claim_code)
params = {pairingCode: claim_code}
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] || load_tokens[facade] || raise(BitPayError, "Not authorized for facade: #{facade}")
end
def verify_claim_code(claim_code)
regex = /^[[:alnum:]]{7}$/
matches = regex.match(claim_code)
!(matches.nil?)
end
end
end
end