bitpay-sdk 2.2.0 → 2.3.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -2
- data/README.md +159 -32
- data/Rakefile +8 -1
- data/bitpay-sdk.gemspec +14 -14
- data/config/capybara.rb +1 -1
- data/config/constants.rb +16 -0
- data/features/creating_invoices.feature +5 -5
- data/features/pairing.feature +4 -1
- data/features/step_definitions/invoice_steps.rb +5 -5
- data/features/step_definitions/keygen_steps.rb +15 -1
- data/features/step_definitions/step_helpers.rb +19 -21
- data/lib/bitpay/client.rb +112 -35
- data/lib/bitpay/version.rb +1 -1
- data/spec/client_spec.rb +57 -12
- data/spec/fixtures/invoices-POST.json +29 -0
- data/spec/fixtures/invoices_{id}-GET.json +35 -0
- data/spec/fixtures/invoices_{id}_refunds-GET.json +17 -0
- data/spec/fixtures/invoices_{id}_refunds-POST.json +9 -0
- data/spec/fixtures/invoices_{id}_refunds_{refund_id}-GET.json +9 -0
- data/spec/spec_helper.rb +13 -10
- metadata +35 -34
- data/spec/features/pair_spec.rb +0 -29
- data/spec/features/pos_spec.rb +0 -37
- data/spec/features/setup_spec.rb +0 -13
- data/spec/features/verify_tokens_spec.rb +0 -34
@@ -1,17 +1,12 @@
|
|
1
1
|
require 'capybara/poltergeist'
|
2
2
|
require 'pry'
|
3
|
+
require 'fileutils'
|
3
4
|
|
4
5
|
require File.join File.dirname(__FILE__), '..', '..', 'lib', 'bitpay_sdk.rb'
|
5
6
|
require_relative '../../config/constants.rb'
|
6
7
|
require_relative '../../config/capybara.rb'
|
7
8
|
|
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"
|
9
|
+
|
15
10
|
module BitPay
|
16
11
|
# Location for API Credentials
|
17
12
|
BITPAY_CREDENTIALS_DIR = File.join(Dir.home, ".bitpay")
|
@@ -21,26 +16,25 @@ module BitPay
|
|
21
16
|
TOKEN_FILE_PATH = File.join(BITPAY_CREDENTIALS_DIR, TOKEN_FILE)
|
22
17
|
end
|
23
18
|
|
19
|
+
# Lots of sleeps in here to deal with finicky transitions and PhantomJS
|
24
20
|
def get_claim_code_from_server
|
25
21
|
Capybara::visit ROOT_ADDRESS
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
Capybara::
|
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
|
22
|
+
log_in unless logged_in
|
23
|
+
Capybara::visit DASHBOARD_URL
|
24
|
+
raise "Bad Login" unless Capybara.current_session.current_url == DASHBOARD_URL
|
25
|
+
Capybara::visit "#{ROOT_ADDRESS}/api-tokens"
|
26
|
+
Capybara::find(".token-access-new-button").find(".btn").find(".icon-plus", match: :first).trigger("click")
|
27
|
+
sleep 0.50
|
28
|
+
Capybara::find(".token-access-new-button-wrapper").find_by_id("token-new-form", visible: true).find(".btn").trigger("click")
|
36
29
|
Capybara::find(".token-claimcode", match: :first).text
|
37
30
|
end
|
38
31
|
|
39
32
|
def log_in
|
40
|
-
Capybara::
|
33
|
+
Capybara::visit "#{ROOT_ADDRESS}/dashboard/login/"
|
41
34
|
Capybara::fill_in 'email', :with => TEST_USER
|
42
35
|
Capybara::fill_in 'password', :with => TEST_PASS
|
43
|
-
Capybara::
|
36
|
+
Capybara::click_on('Login')
|
37
|
+
Capybara::find(".ion-gear-a", match: :first)
|
44
38
|
end
|
45
39
|
|
46
40
|
def new_paired_client
|
@@ -55,16 +49,20 @@ def new_client_from_stored_values
|
|
55
49
|
if File.file?(BitPay::PRIVATE_KEY_PATH) && File.file?(BitPay::TOKEN_FILE_PATH)
|
56
50
|
token = get_token_from_file
|
57
51
|
pem = File.read(BitPay::PRIVATE_KEY_PATH)
|
58
|
-
BitPay::SDK::Client.new(pem: pem, tokens: token, insecure: true, api_uri: ROOT_ADDRESS )
|
52
|
+
client = BitPay::SDK::Client.new(pem: pem, tokens: token, insecure: true, api_uri: ROOT_ADDRESS )
|
53
|
+
unless client.verify_tokens then
|
54
|
+
raise "Locally stored tokens are invalid, please remove #{BitPay::TOKEN_FILE_PATH}" end
|
59
55
|
else
|
60
56
|
claim_code = get_claim_code_from_server
|
61
57
|
pem = BitPay::KeyUtils.generate_pem
|
62
58
|
client = BitPay::SDK::Client.new(api_uri: ROOT_ADDRESS, pem: pem, insecure: true)
|
59
|
+
sleep 1 # rate limit compliance
|
63
60
|
token = client.pair_pos_client(claim_code)
|
61
|
+
FileUtils.mkdir_p(BitPay::BITPAY_CREDENTIALS_DIR)
|
64
62
|
File.write(BitPay::PRIVATE_KEY_PATH, pem)
|
65
63
|
File.write(BitPay::TOKEN_FILE_PATH, JSON.generate(token))
|
66
|
-
client
|
67
64
|
end
|
65
|
+
client
|
68
66
|
end
|
69
67
|
|
70
68
|
def get_token_from_file
|
data/lib/bitpay/client.rb
CHANGED
@@ -14,39 +14,52 @@ module BitPay
|
|
14
14
|
module SDK
|
15
15
|
class Client
|
16
16
|
|
17
|
-
|
18
17
|
# @return [Client]
|
19
18
|
# @example
|
20
19
|
# # Create a client with a pem file created by the bitpay client:
|
21
|
-
# client = BitPay::Client.new
|
20
|
+
# client = BitPay::SDK::Client.new
|
22
21
|
def initialize(opts={})
|
23
|
-
@pem
|
24
|
-
@key
|
25
|
-
@priv_key
|
26
|
-
@pub_key
|
27
|
-
@client_id
|
28
|
-
@uri
|
29
|
-
@user_agent
|
30
|
-
@https
|
31
|
-
@https.use_ssl
|
32
|
-
@https.
|
33
|
-
@
|
22
|
+
@pem = opts[:pem] || ENV['BITPAY_PEM'] || KeyUtils.generate_pem
|
23
|
+
@key = KeyUtils.create_key @pem
|
24
|
+
@priv_key = KeyUtils.get_private_key @key
|
25
|
+
@pub_key = KeyUtils.get_public_key @key
|
26
|
+
@client_id = KeyUtils.generate_sin_from_pem @pem
|
27
|
+
@uri = URI.parse opts[:api_uri] || API_URI
|
28
|
+
@user_agent = opts[:user_agent] || USER_AGENT
|
29
|
+
@https = Net::HTTP.new @uri.host, @uri.port
|
30
|
+
@https.use_ssl = true
|
31
|
+
@https.open_timeout = 10
|
32
|
+
@https.read_timeout = 10
|
33
|
+
|
34
|
+
@https.ca_file = CA_FILE
|
35
|
+
@tokens = opts[:tokens] || {}
|
34
36
|
|
35
37
|
# Option to disable certificate validation in extraordinary circumstance. NOT recommended for production use
|
36
38
|
@https.verify_mode = opts[:insecure] == true ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
|
37
39
|
|
38
40
|
# Option to enable http request debugging
|
39
41
|
@https.set_debug_output($stdout) if opts[:debug] == true
|
42
|
+
end
|
40
43
|
|
44
|
+
## Pair client with BitPay service
|
45
|
+
# => Pass empty hash {} to retreive client-initiated pairing code
|
46
|
+
# => Pass {pairingCode: 'WfD01d2'} to claim a server-initiated pairing code
|
47
|
+
#
|
48
|
+
def pair_client(params={})
|
49
|
+
pairing_request(params)
|
41
50
|
end
|
42
51
|
|
52
|
+
## Compatibility method for pos pairing
|
53
|
+
#
|
43
54
|
def pair_pos_client(claimCode)
|
44
55
|
raise BitPay::ArgumentError, "pairing code is not legal" unless verify_claim_code(claimCode)
|
45
|
-
|
46
|
-
get_token 'pos'
|
47
|
-
response
|
56
|
+
pair_client({pairingCode: claimCode})
|
48
57
|
end
|
49
58
|
|
59
|
+
## Create bitcoin invoice
|
60
|
+
#
|
61
|
+
# Defaults to pos facade, also works with merchant facade
|
62
|
+
#
|
50
63
|
def create_invoice(price:, currency:, facade: 'pos', params:{})
|
51
64
|
raise BitPay::ArgumentError, "Illegal Argument: Price must be formatted as a float" unless ( price.is_a?(Numeric) || /^[[:digit:]]+(\.[[:digit:]]{2})?$/.match(price) )
|
52
65
|
raise BitPay::ArgumentError, "Illegal Argument: Currency is invalid." unless /^[[:upper:]]{3}$/.match(currency)
|
@@ -55,29 +68,92 @@ module BitPay
|
|
55
68
|
response["data"]
|
56
69
|
end
|
57
70
|
|
71
|
+
## Gets the privileged merchant-version of the invoice
|
72
|
+
# Requires merchant facade token
|
73
|
+
#
|
74
|
+
def get_invoice(id:)
|
75
|
+
response = send_request("GET", "invoices/#{id}", facade: 'merchant')
|
76
|
+
response["data"]
|
77
|
+
end
|
78
|
+
|
79
|
+
## Gets the public version of the invoice
|
80
|
+
#
|
58
81
|
def get_public_invoice(id:)
|
59
82
|
request = Net::HTTP::Get.new("/invoices/#{id}")
|
60
83
|
response = process_request(request)
|
61
84
|
response["data"]
|
62
85
|
end
|
63
86
|
|
64
|
-
|
87
|
+
|
88
|
+
## Refund paid BitPay invoice
|
89
|
+
#
|
90
|
+
# If invoice["data"]["flags"]["refundable"] == true the a refund address was
|
91
|
+
# provided with the payment and the refund_address parameter is an optional override
|
92
|
+
#
|
93
|
+
# Amount and Currency are required fields for fully paid invoices but optional
|
94
|
+
# for under or overpaid invoices which will otherwise be completely refunded
|
95
|
+
#
|
96
|
+
# Requires merchant facade token
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
# client.refund_invoice(id: 'JB49z2MsDH7FunczeyDS8j', params: {amount: 10, currency: 'USD', bitcoinAddress: '1Jtcygf8W3cEmtGgepggtjCxtmFFjrZwRV'})
|
100
|
+
#
|
101
|
+
def refund_invoice(id:, params:{})
|
102
|
+
invoice = get_invoice(id: id)
|
103
|
+
response = send_request("POST", "invoices/#{id}/refunds", facade: nil, token: invoice["token"], params: params)
|
104
|
+
response["data"]
|
105
|
+
end
|
106
|
+
|
107
|
+
## Get All Refunds for Invoice
|
108
|
+
# Returns an array of all refund requests for a specific invoice,
|
109
|
+
#
|
110
|
+
# Requires merchant facade token
|
111
|
+
#
|
112
|
+
# @example:
|
113
|
+
# client.get_all_refunds_for_invoice(id: 'JB49z2MsDH7FunczeyDS8j')
|
114
|
+
#
|
115
|
+
def get_all_refunds_for_invoice(id:)
|
116
|
+
urlpath = "invoices/#{id}/refunds"
|
117
|
+
invoice = get_invoice(id: id)
|
118
|
+
response = send_request("GET", urlpath, facade: nil, token: invoice["token"])
|
119
|
+
response["data"]
|
65
120
|
end
|
66
121
|
|
67
|
-
|
68
|
-
|
69
|
-
|
122
|
+
## Get Refund
|
123
|
+
# Requires merchant facade token
|
124
|
+
#
|
125
|
+
# @example:
|
126
|
+
# client.get_refund(id: 'JB49z2MsDH7FunczeyDS8j', request_id: '4evCrXq4EDXk4oqDXdWQhX')
|
127
|
+
#
|
128
|
+
def get_refund(id:, request_id:)
|
129
|
+
urlpath = "invoices/#{id}/refunds/#{request_id}"
|
130
|
+
invoice = get_invoice(id: id)
|
131
|
+
response = send_request("GET", urlpath, facade: nil, token: invoice["token"])
|
132
|
+
response["data"]
|
133
|
+
end
|
134
|
+
|
135
|
+
## Checks that the passed tokens are valid by
|
136
|
+
# comparing them to those that are authorized by the server
|
137
|
+
#
|
138
|
+
# Uses local @tokens variable if no tokens are passed
|
139
|
+
# in order to validate the connector is properly paired
|
140
|
+
#
|
141
|
+
def verify_tokens(tokens: @tokens)
|
142
|
+
server_tokens = refresh_tokens
|
143
|
+
tokens.each{|key, value| return false if server_tokens[key] != value}
|
70
144
|
return true
|
71
145
|
end
|
146
|
+
|
72
147
|
## Generates REST request to api endpoint
|
73
|
-
|
148
|
+
# => Defaults to merchant facade unless token or facade is explicitly provided
|
149
|
+
#
|
74
150
|
def send_request(verb, path, facade: 'merchant', params: {}, token: nil)
|
75
151
|
token ||= get_token(facade)
|
76
152
|
|
77
153
|
# Verb-specific logic
|
78
154
|
case verb.upcase
|
79
155
|
when "GET"
|
80
|
-
urlpath = '/' + path + '?
|
156
|
+
urlpath = '/' + path + '?token=' + token
|
81
157
|
request = Net::HTTP::Get.new urlpath
|
82
158
|
request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath, @priv_key)
|
83
159
|
|
@@ -88,7 +164,6 @@ module BitPay
|
|
88
164
|
urlpath = '/' + path
|
89
165
|
request = Net::HTTP::Post.new urlpath
|
90
166
|
params[:token] = token
|
91
|
-
params[:nonce] = KeyUtils.nonce
|
92
167
|
params[:guid] = SecureRandom.uuid
|
93
168
|
params[:id] = @client_id
|
94
169
|
request.body = params.to_json
|
@@ -133,19 +208,17 @@ module BitPay
|
|
133
208
|
|
134
209
|
end
|
135
210
|
|
136
|
-
##
|
137
|
-
#
|
211
|
+
## Fetches the tokens hash from the server and
|
212
|
+
# updates @tokens
|
138
213
|
#
|
139
|
-
def
|
140
|
-
|
141
|
-
urlpath = '/tokens?nonce=' + KeyUtils.nonce
|
214
|
+
def refresh_tokens
|
215
|
+
urlpath = '/tokens'
|
142
216
|
|
143
217
|
request = Net::HTTP::Get.new(urlpath)
|
144
|
-
request['
|
145
|
-
request['
|
218
|
+
request['X-Identity'] = @pub_key
|
219
|
+
request['X-Signature'] = KeyUtils.sign(@uri.to_s + urlpath, @priv_key)
|
146
220
|
|
147
221
|
response = process_request(request)
|
148
|
-
|
149
222
|
token_array = response["data"] || {}
|
150
223
|
|
151
224
|
tokens = {}
|
@@ -158,9 +231,13 @@ module BitPay
|
|
158
231
|
|
159
232
|
end
|
160
233
|
|
161
|
-
##
|
162
|
-
|
163
|
-
|
234
|
+
## Makes a request to /tokens for pairing
|
235
|
+
# Adds passed params as post parameters
|
236
|
+
# If empty params, retrieves server-generated pairing code
|
237
|
+
# If pairingCode key/value is passed, will pair client ID to this account
|
238
|
+
# Returns response hash
|
239
|
+
#
|
240
|
+
def pairing_request(params)
|
164
241
|
urlpath = '/tokens'
|
165
242
|
request = Net::HTTP::Post.new urlpath
|
166
243
|
params[:guid] = SecureRandom.uuid
|
@@ -170,7 +247,7 @@ module BitPay
|
|
170
247
|
end
|
171
248
|
|
172
249
|
def get_token(facade)
|
173
|
-
token = @tokens[facade] ||
|
250
|
+
token = @tokens[facade] || refresh_tokens[facade] || raise(BitPayError, "Not authorized for facade: #{facade}")
|
174
251
|
end
|
175
252
|
|
176
253
|
def verify_claim_code(claim_code)
|
data/lib/bitpay/version.rb
CHANGED
data/spec/client_spec.rb
CHANGED
@@ -2,8 +2,8 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
def tokens
|
4
4
|
{"data" =>
|
5
|
-
[{"merchant" => "
|
6
|
-
{"pos" =>"
|
5
|
+
[{"merchant" => "MERCHANT_TOKEN"},
|
6
|
+
{"pos" =>"POS_TOKEN"},
|
7
7
|
{"merchant/invoice" => "9kv7gGqZLoQ2fxbKEgfgndLoxwjp5na6VtGSH3sN7buX"}
|
8
8
|
]
|
9
9
|
}
|
@@ -14,8 +14,17 @@ describe BitPay::SDK::Client do
|
|
14
14
|
let(:claim_code) { "a12bc3d" }
|
15
15
|
|
16
16
|
before do
|
17
|
-
|
18
|
-
stub_request(:get, /#{BitPay::TEST_API_URI}\/tokens.*/)
|
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'))
|
19
28
|
end
|
20
29
|
|
21
30
|
describe "#initialize" do
|
@@ -36,7 +45,7 @@ describe BitPay::SDK::Client do
|
|
36
45
|
it 'should generate a get request' do
|
37
46
|
stub_request(:get, /#{BitPay::TEST_API_URI}\/whatever.*/).to_return(:body => '{"awesome": "json"}')
|
38
47
|
bitpay_client.send_request("GET", "whatever", facade: "merchant")
|
39
|
-
expect(WebMock).to have_requested(:get, "#{BitPay::TEST_API_URI}/whatever?
|
48
|
+
expect(WebMock).to have_requested(:get, "#{BitPay::TEST_API_URI}/whatever?token=MERCHANT_TOKEN")
|
40
49
|
end
|
41
50
|
end
|
42
51
|
|
@@ -68,7 +77,7 @@ describe BitPay::SDK::Client do
|
|
68
77
|
it 'short circuits on invalid pairing codes' do
|
69
78
|
100.times do
|
70
79
|
claim_code = an_illegal_claim_code
|
71
|
-
expect{bitpay_client.pair_pos_client(claim_code)}.to raise_error BitPay::ArgumentError, "pairing code is not legal"
|
80
|
+
expect { bitpay_client.pair_pos_client(claim_code) }.to raise_error BitPay::ArgumentError, "pairing code is not legal"
|
72
81
|
end
|
73
82
|
end
|
74
83
|
end
|
@@ -106,19 +115,55 @@ describe BitPay::SDK::Client do
|
|
106
115
|
end
|
107
116
|
end
|
108
117
|
|
109
|
-
describe '#
|
118
|
+
describe '#refund_invoice' do
|
119
|
+
subject { bitpay_client }
|
120
|
+
before { stub_const('ENV', {'BITPAY_PEM' => PEM}) }
|
121
|
+
it { is_expected.to respond_to(:refund_invoice) }
|
122
|
+
|
123
|
+
it 'should get the token for the invoice' do
|
124
|
+
bitpay_client.refund_invoice(id: 'TEST_INVOICE_ID')
|
125
|
+
expect(WebMock).to have_requested :get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID?token=MERCHANT_TOKEN"
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should generate a POST to the invoices/refund endpoint' do
|
129
|
+
bitpay_client.refund_invoice(id: 'TEST_INVOICE_ID')
|
130
|
+
expect(WebMock).to have_requested :post, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID/refunds"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '#get_all_refunds_for_invoice' do
|
110
135
|
subject { bitpay_client }
|
111
136
|
before {stub_const('ENV', {'BITPAY_PEM' => PEM})}
|
112
|
-
it { is_expected.to respond_to(:
|
113
|
-
|
114
|
-
|
137
|
+
it { is_expected.to respond_to(:get_all_refunds_for_invoice) }
|
138
|
+
|
139
|
+
it 'should get the token for the invoice' do
|
140
|
+
bitpay_client.get_all_refunds_for_invoice(id: 'TEST_INVOICE_ID')
|
141
|
+
expect(WebMock).to have_requested :get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID?token=MERCHANT_TOKEN"
|
142
|
+
end
|
143
|
+
it 'should GET all refunds' do
|
144
|
+
bitpay_client.get_all_refunds_for_invoice(id: 'TEST_INVOICE_ID')
|
145
|
+
expect(WebMock).to have_requested :get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID/refunds?token=MERCHANT_INVOICE_TOKEN"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe '#get_refund' do
|
150
|
+
subject { bitpay_client }
|
151
|
+
before {stub_const('ENV', {'BITPAY_PEM' => PEM})}
|
152
|
+
it { is_expected.to respond_to(:get_refund) }
|
153
|
+
it 'should get the token for the invoice' do
|
154
|
+
bitpay_client.get_refund(id: 'TEST_INVOICE_ID', request_id: 'TEST_REQUEST_ID')
|
155
|
+
expect(WebMock).to have_requested :get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID?token=MERCHANT_TOKEN"
|
156
|
+
end
|
157
|
+
it 'should GET a single refund' do
|
158
|
+
bitpay_client.get_refund(id: 'TEST_INVOICE_ID', request_id: 'TEST_REQUEST_ID')
|
159
|
+
expect(WebMock).to have_requested :get, "#{BitPay::TEST_API_URI}/invoices/TEST_INVOICE_ID/refunds/TEST_REQUEST_ID?token=MERCHANT_INVOICE_TOKEN"
|
115
160
|
end
|
116
161
|
end
|
117
162
|
|
118
|
-
describe "#
|
163
|
+
describe "#verify_tokens" do
|
119
164
|
subject { bitpay_client }
|
120
165
|
before {stub_const('ENV', {'BITPAY_PEM' => PEM})}
|
121
|
-
it { is_expected.to respond_to(:
|
166
|
+
it { is_expected.to respond_to(:verify_tokens) }
|
122
167
|
end
|
123
168
|
end
|
124
169
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
{
|
2
|
+
"facade": "pos/invoice",
|
3
|
+
"data": {
|
4
|
+
"url": "https://test.bitpay.com/invoice?id=2RSyNDvsiTrA31rPwnnEcd",
|
5
|
+
"status": "new",
|
6
|
+
"btcPrice": "0.037523",
|
7
|
+
"btcDue": "0.037523",
|
8
|
+
"price": 10,
|
9
|
+
"currency": "USD",
|
10
|
+
"exRates": {
|
11
|
+
"USD": 266.5
|
12
|
+
},
|
13
|
+
"invoiceTime": 1422319964413,
|
14
|
+
"expirationTime": 1422320864413,
|
15
|
+
"currentTime": 1422319964431,
|
16
|
+
"guid": "34d7be05-eb65-4f72-a2ce-79bf23e93f17",
|
17
|
+
"id": "2RSyNDvsiTrA31rPwnnEcd",
|
18
|
+
"btcPaid": "0.000000",
|
19
|
+
"rate": 266.5,
|
20
|
+
"exceptionStatus": false,
|
21
|
+
"paymentUrls": {
|
22
|
+
"BIP21": "bitcoin:mhPM48eieakd6AgCuHMwAtpFXE5yQ3N7om?amount=0.037523",
|
23
|
+
"BIP72": "bitcoin:mhPM48eieakd6AgCuHMwAtpFXE5yQ3N7om?amount=0.037523&r=https://test.bitpay.com/i/2RSyNDvsiTrA31rPwnnEcd",
|
24
|
+
"BIP72b": "bitcoin:?r=https://test.bitpay.com/i/2RSyNDvsiTrA31rPwnnEcd",
|
25
|
+
"BIP73": "https://test.bitpay.com/i/2RSyNDvsiTrA31rPwnnEcd"
|
26
|
+
},
|
27
|
+
"token": "2RPipMRUXAvt5wAfthCzF7Tj4SppBWPHGQ7hCeWYeWDm7RtwUtDds1XUNt11VTf5C6UfCAACBhsKwjW6SAocLsd7"
|
28
|
+
}
|
29
|
+
}
|