bitpay-client 2.4.0 → 2.5.1905

Sign up to get free protection for your applications and to get access to all the features.
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
+