bitpay-client 2.4.0 → 2.5.1905

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.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -1
  3. data/.travis.yml +6 -1
  4. data/CHANGELOG.md +24 -0
  5. data/GUIDE.md +233 -0
  6. data/Gemfile +0 -4
  7. data/LICENSE.md +2 -2
  8. data/README.md +8 -79
  9. data/Rakefile +57 -2
  10. data/bitpay-client.gemspec +15 -16
  11. data/config/constants.rb +12 -1
  12. data/features/creating_invoices.feature +28 -0
  13. data/features/pairing.feature +19 -0
  14. data/features/refunds.feature +23 -0
  15. data/features/retrieving_invoices.feature +11 -0
  16. data/features/step_definitions/invoice_steps.rb +31 -0
  17. data/features/step_definitions/keygen_steps.rb +56 -0
  18. data/features/step_definitions/refund_steps.rb +37 -0
  19. data/features/step_definitions/step_helpers.rb +31 -0
  20. data/lib/bitpay/cacert.pem +1295 -1760
  21. data/lib/bitpay/client.rb +175 -0
  22. data/lib/bitpay/rest_connector.rb +106 -0
  23. data/lib/bitpay/version.rb +2 -2
  24. data/lib/bitpay_sdk.rb +28 -0
  25. data/spec/client_spec.rb +153 -6
  26. data/spec/fixtures/invoices-POST.json +29 -0
  27. data/spec/fixtures/invoices_{id}-GET.json +35 -0
  28. data/spec/fixtures/invoices_{id}_refunds-GET.json +17 -0
  29. data/spec/fixtures/invoices_{id}_refunds-POST.json +9 -0
  30. data/spec/fixtures/invoices_{id}_refunds_{refund_id}-GET.json +11 -0
  31. data/spec/fixtures/response-nodata.json +10 -0
  32. data/spec/spec_helper.rb +16 -18
  33. metadata +77 -82
  34. data/bin/bitpay +0 -3
  35. data/lib/bitpay.rb +0 -21
  36. data/lib/bitpay/cli.rb +0 -64
  37. data/lib/bitpay/cli_client.rb +0 -24
  38. data/lib/bitpay/cli_key_utils.rb +0 -40
  39. data/spec/features/pair_spec.rb +0 -25
  40. data/spec/key_utils_spec.rb +0 -26
  41. data/spec/set_constants.sh +0 -4
@@ -0,0 +1,175 @@
1
+ # license Copyright 2011-2019 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 'bitpay_key_utils'
10
+ require_relative 'rest_connector'
11
+
12
+ module BitPay
13
+ # This class is used to instantiate a BitPay Client object. It is expected to be thread safe.
14
+ #
15
+ module SDK
16
+ class Client
17
+ include BitPay::RestConnector
18
+ # @return [Client]
19
+ # @example
20
+ # # Create a client with a pem file created by the bitpay client:
21
+ # client = BitPay::SDK::Client.new
22
+ def initialize(opts={})
23
+ @pem = opts[:pem] || ENV['BITPAY_PEM'] || KeyUtils.generate_pem
24
+ @key = KeyUtils.create_key @pem
25
+ @priv_key = KeyUtils.get_private_key @key
26
+ @pub_key = KeyUtils.get_public_key @key
27
+ @client_id = KeyUtils.generate_sin_from_pem @pem
28
+ @uri = URI.parse opts[:api_uri] || API_URI
29
+ @user_agent = opts[:user_agent] || USER_AGENT
30
+ @https = Net::HTTP.new @uri.host, @uri.port
31
+ @https.use_ssl = true
32
+ @https.open_timeout = 10
33
+ @https.read_timeout = 10
34
+
35
+ @https.ca_file = CA_FILE
36
+ @tokens = opts[:tokens] || {}
37
+
38
+ # Option to disable certificate validation in extraordinary circumstance. NOT recommended for production use
39
+ @https.verify_mode = opts[:insecure] == true ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
40
+
41
+ # Option to enable http request debugging
42
+ @https.set_debug_output($stdout) if opts[:debug] == true
43
+ end
44
+
45
+ ## Pair client with BitPay service
46
+ # => Pass empty hash {} to retreive client-initiated pairing code
47
+ # => Pass {pairingCode: 'WfD01d2'} to claim a server-initiated pairing code
48
+ #
49
+ def pair_client(params={})
50
+ tokens = post(path: 'tokens', params: params)
51
+ return tokens["data"]
52
+ end
53
+
54
+ ## Compatibility method for pos pairing
55
+ #
56
+ def pair_pos_client(claimCode)
57
+ raise BitPay::ArgumentError, "pairing code is not legal" unless verify_claim_code(claimCode)
58
+ pair_client({pairingCode: claimCode})
59
+ end
60
+
61
+ ## Create bitcoin invoice
62
+ #
63
+ # Defaults to pos facade, also works with merchant facade
64
+ #
65
+ def create_invoice(price:, currency:, facade: 'pos', params:{})
66
+ raise BitPay::ArgumentError, "Illegal Argument: Price must be formatted as a float" unless
67
+ price.is_a?(Numeric) ||
68
+ /^[[:digit:]]+(\.[[:digit:]]{2})?$/.match(price) ||
69
+ currency == 'BTC' && /^[[:digit:]]+(\.[[:digit:]]{1,6})?$/.match(price)
70
+ raise BitPay::ArgumentError, "Illegal Argument: Currency is invalid." unless /^[[:upper:]]{3}$/.match(currency)
71
+ params.merge!({price: price, currency: currency})
72
+ token = get_token(facade)
73
+ invoice = post(path: "invoices", token: token, params: params)
74
+ invoice["data"]
75
+ end
76
+
77
+ ## Gets the privileged merchant-version of the invoice
78
+ # Requires merchant facade token
79
+ #
80
+ def get_invoice(id:)
81
+ token = get_token('merchant')
82
+ invoice = get(path: "invoices/#{id}", token: token)
83
+ invoice["data"]
84
+ end
85
+
86
+ ## Gets the public version of the invoice
87
+ #
88
+ def get_public_invoice(id:)
89
+ invoice = get(path: "invoices/#{id}", public: true)
90
+ invoice["data"]
91
+ end
92
+
93
+
94
+ ## Refund paid BitPay invoice
95
+ #
96
+ # If invoice["data"]["flags"]["refundable"] == true the a refund address was
97
+ # provided with the payment and the refund_address parameter is an optional override
98
+ #
99
+ # Amount and Currency are required fields for fully paid invoices but optional
100
+ # for under or overpaid invoices which will otherwise be completely refunded
101
+ #
102
+ # Requires merchant facade token
103
+ #
104
+ # @example
105
+ # client.refund_invoice(id: 'JB49z2MsDH7FunczeyDS8j', params: {amount: 10, currency: 'USD', bitcoinAddress: '1Jtcygf8W3cEmtGgepggtjCxtmFFjrZwRV'})
106
+ #
107
+ def refund_invoice(id:, params:{})
108
+ invoice = get_invoice(id: id)
109
+ refund = post(path: "invoices/#{id}/refunds", token: invoice["token"], params: params)
110
+ refund["data"]
111
+ end
112
+
113
+ ## Get All Refunds for Invoice
114
+ # Returns an array of all refund requests for a specific invoice,
115
+ #
116
+ # Requires merchant facade token
117
+ #
118
+ # @example:
119
+ # client.get_all_refunds_for_invoice(id: 'JB49z2MsDH7FunczeyDS8j')
120
+ #
121
+ def get_all_refunds_for_invoice(id:)
122
+ urlpath = "invoices/#{id}/refunds"
123
+ invoice = get_invoice(id: id)
124
+ refunds = get(path: urlpath, token: invoice["token"])
125
+ refunds["data"]
126
+ end
127
+
128
+ ## Get Refund
129
+ # Requires merchant facade token
130
+ #
131
+ # @example:
132
+ # client.get_refund(id: 'JB49z2MsDH7FunczeyDS8j', request_id: '4evCrXq4EDXk4oqDXdWQhX')
133
+ #
134
+ def get_refund(invoice_id:, request_id:)
135
+ urlpath = "invoices/#{invoice_id}/refunds/#{request_id}"
136
+ invoice = get_invoice(id: invoice_id)
137
+ refund = get(path: urlpath, token: invoice["token"])
138
+ refund["data"]
139
+ end
140
+
141
+ ## Cancel Refund
142
+ # Requires merchant facade token
143
+ #
144
+ # @example:
145
+ # client.cancel_refund(id: 'JB49z2MsDH7FunczeyDS8j', request_id: '4evCrXq4EDXk4oqDXdWQhX')
146
+ #
147
+ def cancel_refund(invoice_id:, request_id:)
148
+ urlpath = "invoices/#{invoice_id}/refunds/#{request_id}"
149
+ refund = get_refund(invoice_id: invoice_id, request_id: request_id)
150
+ deletion = delete(path: urlpath, token: refund["token"])
151
+ deletion["data"]
152
+ end
153
+
154
+ ## Checks that the passed tokens are valid by
155
+ # comparing them to those that are authorized by the server
156
+ #
157
+ # Uses local @tokens variable if no tokens are passed
158
+ # in order to validate the connector is properly paired
159
+ #
160
+ def verify_tokens(tokens: @tokens)
161
+ server_tokens = refresh_tokens
162
+ tokens.each{|key, value| return false if server_tokens[key] != value}
163
+ return true
164
+ end
165
+
166
+ private
167
+
168
+ def verify_claim_code(claim_code)
169
+ regex = /^[[:alnum:]]{7}$/
170
+ matches = regex.match(claim_code)
171
+ !(matches.nil?)
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,106 @@
1
+ # license Copyright 2011-2019 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
+ module BitPay
6
+ module RestConnector
7
+ def send_request(verb, path, facade: 'merchant', params: {}, token: nil)
8
+ token ||= get_token(facade)
9
+ case verb.upcase
10
+ when "GET"
11
+ return get(path: path, token: token)
12
+ when "POST"
13
+ return post(path: path, token: token, params: params)
14
+ else
15
+ raise(BitPayError, "Invalid HTTP verb: #{verb.upcase}")
16
+ end
17
+ end
18
+
19
+ def get(path:, token: nil, public: false)
20
+ urlpath = '/' + path
21
+ token_prefix = if urlpath.include? '?' then '&token=' else '?token=' end
22
+ urlpath = urlpath + token_prefix + token if token
23
+ request = Net::HTTP::Get.new urlpath
24
+ unless public
25
+ request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath, @priv_key)
26
+ request['X-Identity'] = @pub_key
27
+ end
28
+ process_request(request)
29
+ end
30
+
31
+ def post(path:, token: nil, params:)
32
+ urlpath = '/' + path
33
+ request = Net::HTTP::Post.new urlpath
34
+ params[:token] = token if token
35
+ params[:guid] = SecureRandom.uuid
36
+ params[:id] = @client_id
37
+ request.body = params.to_json
38
+ if token
39
+ request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath + request.body, @priv_key)
40
+ request['X-Identity'] = @pub_key
41
+ end
42
+ process_request(request)
43
+ end
44
+
45
+ def delete(path:, token: nil)
46
+ urlpath = '/' + path
47
+ urlpath = urlpath + '?token=' + token if token
48
+ request = Net::HTTP::Delete.new urlpath
49
+ request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath, @priv_key)
50
+ request['X-Identity'] = @pub_key
51
+ process_request(request)
52
+ end
53
+
54
+ private
55
+
56
+ ## Processes HTTP Request and returns parsed response
57
+ # Otherwise throws error
58
+ #
59
+ def process_request(request)
60
+ request['User-Agent'] = @user_agent
61
+ request['Content-Type'] = 'application/json'
62
+ request['X-BitPay-Plugin-Info'] = 'Rubylib' + VERSION
63
+
64
+ begin
65
+ response = @https.request request
66
+ rescue => error
67
+ raise BitPay::ConnectionError, "#{error.message}"
68
+ end
69
+
70
+ if response.kind_of? Net::HTTPSuccess
71
+ return JSON.parse(response.body)
72
+ elsif JSON.parse(response.body)["error"]
73
+ raise(BitPayError, "#{response.code}: #{JSON.parse(response.body)['error']}")
74
+ else
75
+ raise BitPayError, "#{response.code}: #{JSON.parse(response.body)}"
76
+ end
77
+
78
+ end
79
+
80
+ ## Fetches the tokens hash from the server and
81
+ # updates @tokens
82
+ #
83
+ def refresh_tokens
84
+ response = get(path: 'tokens')["data"]
85
+ token_array = response || {}
86
+ tokens = {}
87
+ token_array.each do |t|
88
+ tokens[t.keys.first] = t.values.first
89
+ end
90
+ @tokens = tokens
91
+ return tokens
92
+ end
93
+
94
+ ## Makes a request to /tokens for pairing
95
+ # Adds passed params as post parameters
96
+ # If empty params, retrieves server-generated pairing code
97
+ # If pairingCode key/value is passed, will pair client ID to this account
98
+ # Returns response hash
99
+ #
100
+
101
+ def get_token(facade)
102
+ token = @tokens[facade] || refresh_tokens[facade] || raise(BitPayError, "Not authorized for facade: #{facade}")
103
+ end
104
+
105
+ end
106
+ end
@@ -1,7 +1,7 @@
1
- # license Copyright 2011-2014 BitPay, Inc., MIT License
1
+ # license Copyright 2011-2019 BitPay, Inc., MIT License
2
2
  # see http://opensource.org/licenses/MIT
3
3
  # or https://github.com/bitpay/php-bitpay-client/blob/master/LICENSE
4
4
 
5
5
  module BitPay
6
- VERSION = '2.4.0'
6
+ VERSION = '2.5.1905'
7
7
  end
@@ -0,0 +1,28 @@
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
+ libdir = File.dirname(__FILE__)
6
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
7
+ require 'bitpay/client'
8
+ require 'bitpay/version'
9
+
10
+ module BitPay
11
+
12
+ # Location of SSL Certificate Authority File
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
+
22
+ # User agent reported to API
23
+ USER_AGENT = 'BitPay_Ruby_Client_v'+VERSION
24
+
25
+ class BitPayError < StandardError; end
26
+ class ConnectionError < Errno::ECONNREFUSED; end
27
+
28
+ end
@@ -2,19 +2,82 @@ require 'spec_helper'
2
2
 
3
3
  def tokens
4
4
  {"data" =>
5
- [{"merchant" => "MERCHANTTOKEN"},
6
- {"pos" =>"POSTOKEN"},
5
+ [{"merchant" => "MERCHANT_TOKEN"},
6
+ {"pos" =>"POS_TOKEN"},
7
7
  {"merchant/invoice" => "9kv7gGqZLoQ2fxbKEgfgndLoxwjp5na6VtGSH3sN7buX"}
8
8
  ]
9
9
  }
10
10
  end
11
11
 
12
- describe BitPay::Client do
13
- let(:bitpay_client) { BitPay::Client.new({api_uri: BitPay::TEST_API_URI}) }
12
+ describe BitPay::SDK::Client do
13
+ let(:bitpay_client) { BitPay::SDK::Client.new({api_uri: BitPay::TEST_API_URI}) }
14
14
  let(:claim_code) { "a12bc3d" }
15
15
 
16
16
  before do
17
- stub_request(:get, /#{BitPay::TEST_API_URI}\/tokens.*/).to_return(:status => 200, :body => tokens.to_json, :headers => {})
17
+ # Stub JSON responses from fixtures
18
+ stub_request(:get, /#{BitPay::TEST_API_URI}\/tokens.*/)
19
+ .to_return(:status => 200, :body => tokens.to_json, :headers => {})
20
+ stub_request(:get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID?token=MERCHANT_TOKEN").
21
+ to_return(:body => get_fixture('invoices_{id}-GET.json'))
22
+ stub_request(:get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID/refunds?token=MERCHANT_INVOICE_TOKEN").
23
+ to_return(:body => get_fixture('invoices_{id}_refunds-GET.json'))
24
+ stub_request(:get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID/refunds/TEST_REQUEST_ID?token=MERCHANT_INVOICE_TOKEN").
25
+ to_return(:body => get_fixture('invoices_{id}_refunds-GET.json'))
26
+ stub_request(:post, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID/refunds").
27
+ to_return(:body => get_fixture('invoices_{id}_refunds-POST.json'))
28
+ stub_request(:post, "#{BitPay::TEST_API_URI}/nuttin").
29
+ to_return(:body => get_fixture('response-nodata.json'))
30
+ stub_request(:get, "#{BitPay::TEST_API_URI}/nuttin").
31
+ to_return(:body => get_fixture('response-nodata.json'))
32
+ stub_request(:delete, "#{BitPay::TEST_API_URI}/nuttin").
33
+ to_return(:body => get_fixture('response-nodata.json'))
34
+ end
35
+
36
+ describe "#initialize" do
37
+
38
+ it 'should be able to get pem file from the env' do
39
+ stub_const('ENV', {'BITPAY_PEM' => PEM})
40
+ expect {bitpay_client}.to_not raise_error
41
+ end
42
+
43
+ end
44
+
45
+ describe "requests to endpoint without data field" do
46
+ it "should return the json body" do
47
+ expect(bitpay_client.post(path: "nuttin", params: {})["facile"]).to eq("is easy")
48
+ expect(bitpay_client.get(path: "nuttin")["facile"]).to eq("is easy")
49
+ expect(bitpay_client.delete(path: "nuttin")["facile"]).to eq( "is easy")
50
+ end
51
+ end
52
+
53
+ describe "#send_request" do
54
+ before do
55
+ stub_const('ENV', {'BITPAY_PEM' => PEM})
56
+ end
57
+
58
+ context "GET" do
59
+ it 'should generate a get request' do
60
+ stub_request(:get, /#{BitPay::TEST_API_URI}\/whatever.*/).to_return(:body => '{"awesome": "json"}')
61
+ bitpay_client.send_request("GET", "whatever", facade: "merchant")
62
+ expect(WebMock).to have_requested(:get, "#{BitPay::TEST_API_URI}/whatever?token=MERCHANT_TOKEN")
63
+ end
64
+
65
+ it 'should handle query parameters gracefully' do
66
+ stub_request(:get, /#{BitPay::TEST_API_URI}\/ledgers.*/).to_return(:body => '{"awesome": "json"}')
67
+ bitpay_client.send_request("GET", "ledgers/BTC?startDate=2015-01-01&endDate=2015-02-01", facade: "merchant")
68
+ expect(WebMock).to have_requested(:get, "#{BitPay::TEST_API_URI}/ledgers/BTC?startDate=2015-01-01&endDate=2015-02-01&token=MERCHANT_TOKEN")
69
+ end
70
+
71
+ end
72
+
73
+ context "POST" do
74
+ it 'should generate a post request' do
75
+ stub_request(:post, /#{BitPay::TEST_API_URI}.*/).to_return(:body => '{"awesome": "json"}')
76
+ bitpay_client.send_request("POST", "whatever", facade: "merchant")
77
+ expect(WebMock).to have_requested(:post, "#{BitPay::TEST_API_URI}/whatever")
78
+ end
79
+ end
80
+
18
81
  end
19
82
 
20
83
  describe "#pair_pos_client" do
@@ -35,9 +98,93 @@ describe BitPay::Client do
35
98
  it 'short circuits on invalid pairing codes' do
36
99
  100.times do
37
100
  claim_code = an_illegal_claim_code
38
- expect{bitpay_client.pair_pos_client(claim_code)}.to raise_error BitPay::ArgumentError, "pairing code is not legal"
101
+ expect { bitpay_client.pair_pos_client(claim_code) }.to raise_error BitPay::ArgumentError, "pairing code is not legal"
39
102
  end
40
103
  end
41
104
  end
42
105
 
106
+ describe "#create_invoice" do
107
+ subject { bitpay_client }
108
+ before {stub_const('ENV', {'BITPAY_PEM' => PEM})}
109
+ it { is_expected.to respond_to(:create_invoice) }
110
+
111
+ describe "should make the call to the server to create an invoice" do
112
+ it 'allows numeric input for the price' do
113
+ stub_request(:post, /#{BitPay::TEST_API_URI}\/invoices.*/).to_return(:body => '{"data": "awesome"}')
114
+ bitpay_client.create_invoice(price: 20.00, currency: "USD")
115
+ assert_requested :post, "#{BitPay::TEST_API_URI}/invoices"
116
+ end
117
+
118
+ it 'allows string input for the price' do
119
+ stub_request(:post, /#{BitPay::TEST_API_URI}\/invoices.*/).to_return(:body => '{"data": "awesome"}')
120
+ bitpay_client.create_invoice(price: "20.00", currency: "USD")
121
+ assert_requested :post, "#{BitPay::TEST_API_URI}/invoices"
122
+ end
123
+ end
124
+
125
+ it 'should pass through the API error message from load_tokens' do
126
+ stub_request(:get, /#{BitPay::TEST_API_URI}\/tokens.*/).to_return(status: 500, body: '{"error": "load_tokens_error"}')
127
+ expect { bitpay_client.create_invoice(price: 20, currency: "USD") }.to raise_error(BitPay::BitPayError, '500: load_tokens_error')
128
+ end
129
+
130
+ it 'verifies the validity of the price argument' do
131
+ expect { bitpay_client.create_invoice(price: "3,999", currency: "USD") }.to raise_error(BitPay::ArgumentError, 'Illegal Argument: Price must be formatted as a float')
132
+ end
133
+
134
+ it 'verifies the validity of the currency argument' do
135
+ expect { bitpay_client.create_invoice(price: "3999", currency: "UASD") }.to raise_error(BitPay::ArgumentError, 'Illegal Argument: Currency is invalid.')
136
+ end
137
+ end
138
+
139
+ describe '#refund_invoice' do
140
+ subject { bitpay_client }
141
+ before { stub_const('ENV', {'BITPAY_PEM' => PEM}) }
142
+ it { is_expected.to respond_to(:refund_invoice) }
143
+
144
+ it 'should get the token for the invoice' do
145
+ bitpay_client.refund_invoice(id: 'TEST_INVOICE_ID')
146
+ expect(WebMock).to have_requested :get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID?token=MERCHANT_TOKEN"
147
+ end
148
+
149
+ it 'should generate a POST to the invoices/refund endpoint' do
150
+ bitpay_client.refund_invoice(id: 'TEST_INVOICE_ID')
151
+ expect(WebMock).to have_requested :post, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID/refunds"
152
+ end
153
+ end
154
+
155
+ describe '#get_all_refunds_for_invoice' do
156
+ subject { bitpay_client }
157
+ before {stub_const('ENV', {'BITPAY_PEM' => PEM})}
158
+ it { is_expected.to respond_to(:get_all_refunds_for_invoice) }
159
+
160
+ it 'should get the token for the invoice' do
161
+ bitpay_client.get_all_refunds_for_invoice(id: 'TEST_INVOICE_ID')
162
+ expect(WebMock).to have_requested :get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID?token=MERCHANT_TOKEN"
163
+ end
164
+ it 'should GET all refunds' do
165
+ bitpay_client.get_all_refunds_for_invoice(id: 'TEST_INVOICE_ID')
166
+ expect(WebMock).to have_requested :get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID/refunds?token=MERCHANT_INVOICE_TOKEN"
167
+ end
168
+ end
169
+
170
+ describe '#get_refund' do
171
+ subject { bitpay_client }
172
+ before {stub_const('ENV', {'BITPAY_PEM' => PEM})}
173
+ it { is_expected.to respond_to(:get_refund) }
174
+ it 'should get the token for the invoice' do
175
+ bitpay_client.get_refund(invoice_id: 'TEST_INVOICE_ID', request_id: 'TEST_REQUEST_ID')
176
+ expect(WebMock).to have_requested :get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID?token=MERCHANT_TOKEN"
177
+ end
178
+ it 'should GET a single refund' do
179
+ bitpay_client.get_refund(invoice_id: 'TEST_INVOICE_ID', request_id: 'TEST_REQUEST_ID')
180
+ expect(WebMock).to have_requested :get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID/refunds/TEST_REQUEST_ID?token=MERCHANT_INVOICE_TOKEN"
181
+ end
182
+ end
183
+
184
+ describe "#verify_tokens" do
185
+ subject { bitpay_client }
186
+ before {stub_const('ENV', {'BITPAY_PEM' => PEM})}
187
+ it { is_expected.to respond_to(:verify_tokens) }
188
+ end
43
189
  end
190
+