blockchyp 1.16.1
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 +7 -0
- data/Makefile +78 -0
- data/README.md +4023 -0
- data/Rakefile +39 -0
- data/lib/blockchyp/version.rb +5 -0
- data/lib/blockchyp.rb +478 -0
- data/lib/blockchyp_client.rb +416 -0
- data/lib/crypto_utils.rb +25 -0
- data/test/activate_terminal_test.rb +45 -0
- data/test/add_test_merchant_test.rb +56 -0
- data/test/batch_history_test.rb +54 -0
- data/test/boolean_prompt_test.rb +49 -0
- data/test/cancel_payment_link_test.rb +72 -0
- data/test/capture_signature_test.rb +46 -0
- data/test/deactivate_terminal_test.rb +42 -0
- data/test/delete_branding_asset_test.rb +50 -0
- data/test/delete_customer_test.rb +55 -0
- data/test/delete_media_asset_test.rb +53 -0
- data/test/delete_queued_transaction_test.rb +56 -0
- data/test/delete_slide_show_test.rb +50 -0
- data/test/delete_survey_question_test.rb +51 -0
- data/test/delete_test_merchant_test.rb +59 -0
- data/test/delete_token_test.rb +55 -0
- data/test/empty_branding_asset_test.rb +44 -0
- data/test/empty_slide_show_test.rb +45 -0
- data/test/gateway_timeout_test.rb +49 -0
- data/test/get_customer_test.rb +55 -0
- data/test/get_merchants_test.rb +52 -0
- data/test/heartbeat_test.rb +27 -0
- data/test/invite_merchant_user_test.rb +45 -0
- data/test/link_token_test.rb +56 -0
- data/test/list_queued_transactions_test.rb +55 -0
- data/test/list_terminals_test.rb +42 -0
- data/test/media_asset_test.rb +57 -0
- data/test/media_test.rb +42 -0
- data/test/media_upload_test.rb +52 -0
- data/test/merchant_platforms_test.rb +59 -0
- data/test/merchant_profile_test.rb +43 -0
- data/test/merchant_users_test.rb +42 -0
- data/test/new_transaction_display_test.rb +64 -0
- data/test/pan_charge_test.rb +61 -0
- data/test/pan_enroll_test.rb +62 -0
- data/test/pan_preauth_test.rb +60 -0
- data/test/partial_refund_test.rb +58 -0
- data/test/payment_link_status_test.rb +72 -0
- data/test/resend_payment_link_test.rb +49 -0
- data/test/search_customer_test.rb +55 -0
- data/test/send_payment_link_test.rb +67 -0
- data/test/simple_batch_close_test.rb +54 -0
- data/test/simple_capture_test.rb +55 -0
- data/test/simple_gift_activate_test.rb +48 -0
- data/test/simple_locate_test.rb +44 -0
- data/test/simple_message_test.rb +46 -0
- data/test/simple_ping_test.rb +45 -0
- data/test/simple_refund_test.rb +57 -0
- data/test/simple_reversal_test.rb +56 -0
- data/test/simple_void_test.rb +56 -0
- data/test/slide_show_test.rb +51 -0
- data/test/slide_shows_test.rb +49 -0
- data/test/survey_question_test.rb +48 -0
- data/test/survey_questions_test.rb +50 -0
- data/test/survey_results_test.rb +48 -0
- data/test/tc_delete_template_test.rb +51 -0
- data/test/tc_entry_test.rb +56 -0
- data/test/tc_log_test.rb +42 -0
- data/test/tc_template_test.rb +53 -0
- data/test/tc_template_update_test.rb +48 -0
- data/test/tc_templates_test.rb +42 -0
- data/test/terminal_branding_test.rb +42 -0
- data/test/terminal_charge_test.rb +57 -0
- data/test/terminal_clear_test.rb +45 -0
- data/test/terminal_ebt_balance_test.rb +47 -0
- data/test/terminal_ebt_charge_test.rb +59 -0
- data/test/terminal_enroll_test.rb +56 -0
- data/test/terminal_gift_card_balance_test.rb +46 -0
- data/test/terminal_keyed_charge_test.rb +58 -0
- data/test/terminal_manual_ebt_charge_test.rb +60 -0
- data/test/terminal_preauth_test.rb +57 -0
- data/test/terminal_queued_transaction_test.rb +51 -0
- data/test/terminal_status_test.rb +45 -0
- data/test/terminal_timeout_test.rb +46 -0
- data/test/terms_and_conditions_test.rb +50 -0
- data/test/test_helper.rb +64 -0
- data/test/testdata/aviato.png +0 -0
- data/test/text_prompt_test.rb +47 -0
- data/test/token_metadata_test.rb +55 -0
- data/test/transaction_history_test.rb +54 -0
- data/test/unlink_token_test.rb +56 -0
- data/test/update_branding_asset_test.rb +62 -0
- data/test/update_customer_test.rb +49 -0
- data/test/update_merchant_platforms_test.rb +61 -0
- data/test/update_merchant_test.rb +60 -0
- data/test/update_slide_show_test.rb +60 -0
- data/test/update_survey_question_test.rb +47 -0
- data/test/update_transaction_display_test.rb +64 -0
- data/test/upload_status_test.rb +53 -0
- metadata +138 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'base64'
|
|
4
|
+
require 'cgi'
|
|
5
|
+
require 'digest'
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'net/http'
|
|
8
|
+
require 'openssl'
|
|
9
|
+
require 'time'
|
|
10
|
+
require 'tmpdir'
|
|
11
|
+
require 'uri'
|
|
12
|
+
|
|
13
|
+
require_relative 'crypto_utils'
|
|
14
|
+
|
|
15
|
+
module BlockChyp
|
|
16
|
+
# base class for the blockchyp generated blockchyp client
|
|
17
|
+
class BlockChypClient
|
|
18
|
+
def initialize(api_key, bearer_token, signing_key)
|
|
19
|
+
@api_key = api_key
|
|
20
|
+
@bearer_token = bearer_token
|
|
21
|
+
@signing_key = signing_key
|
|
22
|
+
@gateway_host = 'https://api.blockchyp.com'
|
|
23
|
+
@test_gateway_host = 'https://test.blockchyp.com'
|
|
24
|
+
@dashboard_host = 'https://dashboard.blockchyp.com'
|
|
25
|
+
@https = false
|
|
26
|
+
@route_cache_location = File.join(Dir.tmpdir, '.blockchyp_route')
|
|
27
|
+
@route_cache_ttl = 60
|
|
28
|
+
@gateway_timeout = 20
|
|
29
|
+
@terminal_timeout = 120
|
|
30
|
+
@terminal_connect_timeout = 5
|
|
31
|
+
@offline_cache_enabled = true
|
|
32
|
+
@route_cache = {}
|
|
33
|
+
@offline_fixed_key = 'cb22789c9d5c344a10e0474f134db39e25eb3bbf5a1b1a5e89b507f15ea9519c'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
attr_reader :api_key
|
|
37
|
+
attr_reader :bearer_token
|
|
38
|
+
attr_reader :signing_key
|
|
39
|
+
attr_reader :offline_fixed_key
|
|
40
|
+
attr_accessor :gateway_host
|
|
41
|
+
attr_accessor :test_gateway_host
|
|
42
|
+
attr_accessor :dashboard_host
|
|
43
|
+
attr_accessor :https
|
|
44
|
+
attr_accessor :route_cache_ttl
|
|
45
|
+
attr_accessor :gateway_timeout
|
|
46
|
+
attr_accessor :terminal_timeout
|
|
47
|
+
attr_accessor :offline_cache_enabled
|
|
48
|
+
attr_accessor :terminal_connect_timeout
|
|
49
|
+
attr_accessor :route_cache_location
|
|
50
|
+
attr_accessor :route_cache
|
|
51
|
+
|
|
52
|
+
def generate_gateway_headers
|
|
53
|
+
nonce = CryptoUtils.generate_nonce
|
|
54
|
+
tsp = CryptoUtils.timestamp
|
|
55
|
+
|
|
56
|
+
sig = compute_hmac(tsp, nonce)
|
|
57
|
+
|
|
58
|
+
{
|
|
59
|
+
'Nonce' => nonce,
|
|
60
|
+
'Timestamp' => tsp,
|
|
61
|
+
'Authorization' => 'Dual ' + bearer_token + ':' + api_key + ':' + sig
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def compute_hmac(tsp, nonce)
|
|
66
|
+
canonical_string = api_key + bearer_token + tsp + nonce
|
|
67
|
+
|
|
68
|
+
OpenSSL::HMAC.hexdigest('SHA256', CryptoUtils.hex2bin(signing_key), canonical_string)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def resolve_gateway_uri(path, request)
|
|
72
|
+
url = request.nil? || !request[:test] ? gateway_host : test_gateway_host
|
|
73
|
+
|
|
74
|
+
URI.parse(path.nil? ? url : url + path)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def resolve_dashboard_uri(path, request)
|
|
78
|
+
URI.parse(dashboard_host + path)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def generate_error_response(msg)
|
|
82
|
+
{
|
|
83
|
+
success: false,
|
|
84
|
+
error: msg,
|
|
85
|
+
responseDescription: msg
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def route_terminal_request(method, terminal_path, gateway_path, request)
|
|
90
|
+
unless request.key?(:terminalName)
|
|
91
|
+
return gateway_request(method, gateway_path, request)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
route = resolve_terminal_route(request[:terminalName])
|
|
95
|
+
if !route
|
|
96
|
+
return generate_error_response('Unkown Terminal')
|
|
97
|
+
elsif route[:cloudRelayEnabled]
|
|
98
|
+
return gateway_request(method, gateway_path, request, relay: true)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
terminal_request(method, route, terminal_path, request, true)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def terminal_request(method, route, path, request, open_retry)
|
|
105
|
+
uri = resolve_terminal_uri(route, path)
|
|
106
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
107
|
+
http.use_ssl = uri.instance_of?(URI::HTTPS)
|
|
108
|
+
timeout = get_timeout(request, terminal_timeout)
|
|
109
|
+
http.open_timeout = timeout
|
|
110
|
+
http.read_timeout = timeout
|
|
111
|
+
|
|
112
|
+
tx_creds = route[:transientCredentials]
|
|
113
|
+
|
|
114
|
+
wrapped_request = {
|
|
115
|
+
apiKey: tx_creds[:apiKey],
|
|
116
|
+
bearerToken: tx_creds[:bearerToken],
|
|
117
|
+
signingKey: tx_creds[:signingKey],
|
|
118
|
+
request: request
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
req = get_http_request(method, uri)
|
|
122
|
+
|
|
123
|
+
req['User-Agent'] = user_agent
|
|
124
|
+
json = wrapped_request.to_json
|
|
125
|
+
req['Content-Type'] = 'application/json'
|
|
126
|
+
req['Content-Length'] = json.length
|
|
127
|
+
req.body = json
|
|
128
|
+
|
|
129
|
+
begin
|
|
130
|
+
response = http.request(req)
|
|
131
|
+
rescue Net::OpenTimeout, Errno::EHOSTDOWN, Errno::EHOSTUNREACH, Errno::ETIMEDOUT, Errno::ENETUNREACH
|
|
132
|
+
if open_retry
|
|
133
|
+
evict(route[:terminalName])
|
|
134
|
+
route = resolve_terminal_route(route[:terminalName])
|
|
135
|
+
return terminal_request(method, route, path, request, false)
|
|
136
|
+
end
|
|
137
|
+
raise
|
|
138
|
+
end
|
|
139
|
+
if response.is_a?(Net::HTTPSuccess)
|
|
140
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
141
|
+
else
|
|
142
|
+
raise response.message
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def get_http_request(method, uri)
|
|
147
|
+
case method
|
|
148
|
+
when 'GET'
|
|
149
|
+
Net::HTTP::Get.new(uri.request_uri)
|
|
150
|
+
when 'PUT'
|
|
151
|
+
Net::HTTP::Put.new(uri.request_uri)
|
|
152
|
+
when 'POST'
|
|
153
|
+
Net::HTTP::Post.new(uri.request_uri)
|
|
154
|
+
when 'DELETE'
|
|
155
|
+
Net::HTTP::Delete.new(uri.request_uri)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def resolve_terminal_uri(route, path)
|
|
160
|
+
url = if https
|
|
161
|
+
'https://'
|
|
162
|
+
else
|
|
163
|
+
'http://'
|
|
164
|
+
end
|
|
165
|
+
url += route[:ipAddress]
|
|
166
|
+
port = if https
|
|
167
|
+
':8443'
|
|
168
|
+
else
|
|
169
|
+
':8080'
|
|
170
|
+
end
|
|
171
|
+
URI.parse(url + port + path)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def dashboard_request(method, path, request = nil)
|
|
175
|
+
uri = resolve_dashboard_uri(path, request)
|
|
176
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
177
|
+
http.use_ssl = uri.instance_of?(URI::HTTPS)
|
|
178
|
+
timeout = get_timeout(request, gateway_timeout)
|
|
179
|
+
http.open_timeout = timeout
|
|
180
|
+
http.read_timeout = timeout
|
|
181
|
+
|
|
182
|
+
req = get_http_request(method, uri)
|
|
183
|
+
|
|
184
|
+
req['User-Agent'] = user_agent
|
|
185
|
+
unless request.nil?
|
|
186
|
+
json = request.to_json
|
|
187
|
+
req['Content-Type'] = 'application/json'
|
|
188
|
+
req['Content-Length'] = json.length
|
|
189
|
+
req.body = json
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
headers = generate_gateway_headers
|
|
193
|
+
headers.each do |key, value|
|
|
194
|
+
req[key] = value
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
response = http.request(req)
|
|
198
|
+
|
|
199
|
+
if response.is_a?(Net::HTTPSuccess)
|
|
200
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
201
|
+
else
|
|
202
|
+
raise response.message
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def upload_request(path, request, content)
|
|
207
|
+
uri = resolve_dashboard_uri(path, request)
|
|
208
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
209
|
+
http.use_ssl = uri.instance_of?(URI::HTTPS)
|
|
210
|
+
timeout = get_timeout(request, gateway_timeout)
|
|
211
|
+
http.open_timeout = timeout
|
|
212
|
+
http.read_timeout = timeout
|
|
213
|
+
|
|
214
|
+
req = get_http_request("POST", uri)
|
|
215
|
+
req.body = content
|
|
216
|
+
req['User-Agent'] = user_agent
|
|
217
|
+
req['Content-Type'] = 'application/octet-stream'
|
|
218
|
+
|
|
219
|
+
if request[:fileName]
|
|
220
|
+
req['X-Upload-File-Name'] = request[:fileName]
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
if request[:uploadId]
|
|
224
|
+
req['X-Upload-ID'] = request[:uploadId]
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
if request[:fileSize]
|
|
228
|
+
req['X-File-Size'] = request[:fileSize]
|
|
229
|
+
req['Content-Length'] = request[:fileSize]
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
headers = generate_gateway_headers
|
|
233
|
+
headers.each do |key, value|
|
|
234
|
+
req[key] = value
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
response = http.request(req)
|
|
238
|
+
|
|
239
|
+
if response.is_a?(Net::HTTPSuccess)
|
|
240
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
241
|
+
else
|
|
242
|
+
raise response.message
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def gateway_request(method, path, request = nil, relay = false)
|
|
247
|
+
uri = resolve_gateway_uri(path, request)
|
|
248
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
249
|
+
http.use_ssl = uri.instance_of?(URI::HTTPS)
|
|
250
|
+
timeout = get_timeout(request, relay ? terminal_timeout : gateway_timeout)
|
|
251
|
+
http.open_timeout = timeout
|
|
252
|
+
http.read_timeout = timeout
|
|
253
|
+
|
|
254
|
+
req = get_http_request(method, uri)
|
|
255
|
+
|
|
256
|
+
req['User-Agent'] = user_agent
|
|
257
|
+
unless request.nil?
|
|
258
|
+
json = request.to_json
|
|
259
|
+
req['Content-Type'] = 'application/json'
|
|
260
|
+
req['Content-Length'] = json.length
|
|
261
|
+
req.body = json
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
headers = generate_gateway_headers
|
|
265
|
+
headers.each do |key, value|
|
|
266
|
+
req[key] = value
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
response = http.request(req)
|
|
270
|
+
|
|
271
|
+
if response.is_a?(Net::HTTPSuccess)
|
|
272
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
273
|
+
else
|
|
274
|
+
raise response.message
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def resolve_terminal_route(terminal_name)
|
|
279
|
+
route = route_cache_get(terminal_name, false)
|
|
280
|
+
|
|
281
|
+
if route.nil?
|
|
282
|
+
route = request_route_from_gateway(terminal_name)
|
|
283
|
+
if route.nil?
|
|
284
|
+
return route
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
ttl = Time.now.utc + (route_cache_ttl * 60)
|
|
288
|
+
route_cache_entry = {}
|
|
289
|
+
route_cache_entry[:route] = route
|
|
290
|
+
route_cache_entry[:ttl] = ttl
|
|
291
|
+
@route_cache[api_key + terminal_name] = route_cache_entry
|
|
292
|
+
update_offline_cache(route_cache_entry)
|
|
293
|
+
end
|
|
294
|
+
route
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def update_offline_cache(route_cache_entry)
|
|
298
|
+
if offline_cache_enabled
|
|
299
|
+
offline_cache = read_offline_cache
|
|
300
|
+
offline_entry = route_cache_entry.clone
|
|
301
|
+
route = route_cache_entry[:route].clone
|
|
302
|
+
tx_creds = route[:transientCredentials].clone
|
|
303
|
+
tx_creds[:apiKey] = encrypt(tx_creds[:apiKey])
|
|
304
|
+
tx_creds[:bearerToken] = encrypt(tx_creds[:bearerToken])
|
|
305
|
+
tx_creds[:signingKey] = encrypt(tx_creds[:signingKey])
|
|
306
|
+
route[:transientCredentials] = tx_creds
|
|
307
|
+
offline_entry[:route] = route
|
|
308
|
+
offline_cache[api_key + route[:terminalName]] = offline_entry
|
|
309
|
+
File.write(route_cache_location, offline_cache.to_json)
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def encrypt(plain_text)
|
|
314
|
+
return plain_text if plain_text.nil? || plain_text.empty?
|
|
315
|
+
|
|
316
|
+
cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
|
317
|
+
cipher.encrypt
|
|
318
|
+
cipher.key = derive_offline_key
|
|
319
|
+
iv = cipher.random_iv
|
|
320
|
+
|
|
321
|
+
Base64.encode64(iv) + ':' + Base64.encode64(cipher.update(plain_text) + cipher.final)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def decrypt(cipher_text)
|
|
325
|
+
return cipher_text if cipher_text.nil? || cipher_text.empty?
|
|
326
|
+
|
|
327
|
+
tokens = cipher_text.split(':')
|
|
328
|
+
|
|
329
|
+
return cipher_text if tokens[0].nil? || tokens[1].nil?
|
|
330
|
+
|
|
331
|
+
iv = Base64.decode64(tokens[0])
|
|
332
|
+
cp = Base64.decode64(tokens[1])
|
|
333
|
+
|
|
334
|
+
decipher = OpenSSL::Cipher::AES256.new(:CBC)
|
|
335
|
+
decipher.decrypt
|
|
336
|
+
decipher.key = derive_offline_key
|
|
337
|
+
decipher.iv = iv
|
|
338
|
+
|
|
339
|
+
decipher.update(cp) + decipher.final
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def derive_offline_key
|
|
343
|
+
Digest::SHA256.digest offline_fixed_key + signing_key
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def request_route_from_gateway(terminal_name)
|
|
347
|
+
route = gateway_request('GET', '/api/terminal-route?terminal=' + CGI.escape(terminal_name))
|
|
348
|
+
if !route.nil? && !route[:ipAddress].empty?
|
|
349
|
+
route[:exists] = true
|
|
350
|
+
end
|
|
351
|
+
route
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def evict(terminal_name)
|
|
355
|
+
route_cache.delete(api_key + terminal_name)
|
|
356
|
+
|
|
357
|
+
offline_cache = read_offline_cache
|
|
358
|
+
offline_cache.delete(api_key + terminal_name)
|
|
359
|
+
File.write(route_cache_location, route_cache.to_json)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def route_cache_get(terminal_name, stale)
|
|
363
|
+
route_cache_entry = @route_cache[api_key + terminal_name]
|
|
364
|
+
|
|
365
|
+
if route_cache_entry.nil? && offline_cache_enabled
|
|
366
|
+
offline_cache = read_offline_cache
|
|
367
|
+
route_cache_entry = offline_cache[@api_key + terminal_name]
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
if route_cache_entry
|
|
371
|
+
route = route_cache_entry[:route]
|
|
372
|
+
tx_creds = route[:transientCredentials]
|
|
373
|
+
tx_creds[:apiKey] = decrypt(tx_creds[:apiKey])
|
|
374
|
+
tx_creds[:bearerToken] = decrypt(tx_creds[:bearerToken])
|
|
375
|
+
tx_creds[:signingKey] = decrypt(tx_creds[:signingKey])
|
|
376
|
+
route[:transientCredentials] = tx_creds
|
|
377
|
+
route_cache_entry[:route] = route
|
|
378
|
+
|
|
379
|
+
raw_ttl = route_cache_entry[:ttl]
|
|
380
|
+
|
|
381
|
+
ttl = raw_ttl.instance_of?(Time) ? raw_ttl : Time.parse(route_cache_entry[:ttl])
|
|
382
|
+
|
|
383
|
+
if stale || Time.new < ttl
|
|
384
|
+
route_cache_entry[:route]
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def read_offline_cache
|
|
390
|
+
if File.file?(route_cache_location)
|
|
391
|
+
|
|
392
|
+
config_file = File.open(route_cache_location)
|
|
393
|
+
content = config_file.read
|
|
394
|
+
|
|
395
|
+
return JSON.parse(content, symbolize_names: true)
|
|
396
|
+
end
|
|
397
|
+
{}
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def user_agent
|
|
401
|
+
if defined? VERSION
|
|
402
|
+
"BlockChyp-Ruby/#{VERSION}"
|
|
403
|
+
else
|
|
404
|
+
'BlockChyp-Ruby'
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def get_timeout(request, default)
|
|
409
|
+
if request.nil? || !request.key?(:timeout) || request[:timeout].zero?
|
|
410
|
+
return default
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
request[:timeout]
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
end
|
data/lib/crypto_utils.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require 'date'
|
|
5
|
+
|
|
6
|
+
module BlockChyp
|
|
7
|
+
# crypto and encoding utilities
|
|
8
|
+
class CryptoUtils
|
|
9
|
+
def self.generate_nonce
|
|
10
|
+
bin2hex(SecureRandom.bytes(32))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.timestamp
|
|
14
|
+
Time.now.utc.to_datetime.rfc3339
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.bin2hex(val)
|
|
18
|
+
val.each_byte.map { |b| b.to_s(16) }.join
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.hex2bin(val)
|
|
22
|
+
val.scan(/../).map { |x| x.hex.chr }.join
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2019-2023 BlockChyp, Inc. All rights reserved. Use of this code is
|
|
4
|
+
# governed by a license that can be found in the LICENSE file.
|
|
5
|
+
#
|
|
6
|
+
# This file was generated automatically by the BlockChyp SDK Generator.
|
|
7
|
+
# Changes to this file will be lost every time the code is regenerated.
|
|
8
|
+
|
|
9
|
+
require ::File.expand_path('test_helper', __dir__)
|
|
10
|
+
|
|
11
|
+
module BlockChyp
|
|
12
|
+
class ActivateTerminalTest < TestCase
|
|
13
|
+
def test_activate_terminal
|
|
14
|
+
|
|
15
|
+
puts "Running test_activate_terminal..."
|
|
16
|
+
|
|
17
|
+
config = load_test_config
|
|
18
|
+
|
|
19
|
+
blockchyp = BlockChyp.new(
|
|
20
|
+
config[:apiKey],
|
|
21
|
+
config[:bearerToken],
|
|
22
|
+
config[:signingKey]
|
|
23
|
+
)
|
|
24
|
+
blockchyp.gateway_host = config[:gatewayHost]
|
|
25
|
+
blockchyp.test_gateway_host = config[:testGatewayHost]
|
|
26
|
+
blockchyp.dashboard_host = config[:dashboardHost]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Set request parameters
|
|
33
|
+
request = {
|
|
34
|
+
terminalName: 'Bad Terminal Code',
|
|
35
|
+
activationCode: 'XXXXXX'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
response = blockchyp.activate_terminal(request)
|
|
39
|
+
assert_not_nil(response)
|
|
40
|
+
# response assertions
|
|
41
|
+
assert(!response[:success])
|
|
42
|
+
assert_equal('Invalid Activation Code', response[:error])
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2019-2023 BlockChyp, Inc. All rights reserved. Use of this code is
|
|
4
|
+
# governed by a license that can be found in the LICENSE file.
|
|
5
|
+
#
|
|
6
|
+
# This file was generated automatically by the BlockChyp SDK Generator.
|
|
7
|
+
# Changes to this file will be lost every time the code is regenerated.
|
|
8
|
+
|
|
9
|
+
require ::File.expand_path('test_helper', __dir__)
|
|
10
|
+
|
|
11
|
+
module BlockChyp
|
|
12
|
+
class AddTestMerchantTest < TestCase
|
|
13
|
+
def test_add_test_merchant
|
|
14
|
+
|
|
15
|
+
puts "Running test_add_test_merchant..."
|
|
16
|
+
|
|
17
|
+
config = load_test_config
|
|
18
|
+
|
|
19
|
+
blockchyp = BlockChyp.new(
|
|
20
|
+
config[:apiKey],
|
|
21
|
+
config[:bearerToken],
|
|
22
|
+
config[:signingKey]
|
|
23
|
+
)
|
|
24
|
+
blockchyp.gateway_host = config[:gatewayHost]
|
|
25
|
+
blockchyp.test_gateway_host = config[:testGatewayHost]
|
|
26
|
+
blockchyp.dashboard_host = config[:dashboardHost]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
profile = config[:profiles][:partner]
|
|
30
|
+
blockchyp = BlockChyp.new(
|
|
31
|
+
profile[:apiKey],
|
|
32
|
+
profile[:bearerToken],
|
|
33
|
+
profile[:signingKey]
|
|
34
|
+
)
|
|
35
|
+
blockchyp.gateway_host = config[:gatewayHost]
|
|
36
|
+
blockchyp.test_gateway_host = config[:testGatewayHost]
|
|
37
|
+
blockchyp.dashboard_host = config[:dashboardHost]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Set request parameters
|
|
42
|
+
request = {
|
|
43
|
+
dbaName: 'Test Merchant',
|
|
44
|
+
companyName: 'Test Merchant'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
response = blockchyp.add_test_merchant(request)
|
|
48
|
+
assert_not_nil(response)
|
|
49
|
+
# response assertions
|
|
50
|
+
assert(response[:success])
|
|
51
|
+
assert_equal('Test Merchant', response[:dbaName])
|
|
52
|
+
assert_equal('Test Merchant', response[:companyName])
|
|
53
|
+
assert(response[:visa])
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2019-2023 BlockChyp, Inc. All rights reserved. Use of this code is
|
|
4
|
+
# governed by a license that can be found in the LICENSE file.
|
|
5
|
+
#
|
|
6
|
+
# This file was generated automatically by the BlockChyp SDK Generator.
|
|
7
|
+
# Changes to this file will be lost every time the code is regenerated.
|
|
8
|
+
|
|
9
|
+
require ::File.expand_path('test_helper', __dir__)
|
|
10
|
+
|
|
11
|
+
module BlockChyp
|
|
12
|
+
class BatchHistoryTest < TestCase
|
|
13
|
+
def test_batch_history
|
|
14
|
+
|
|
15
|
+
puts "Running test_batch_history..."
|
|
16
|
+
|
|
17
|
+
config = load_test_config
|
|
18
|
+
|
|
19
|
+
blockchyp = BlockChyp.new(
|
|
20
|
+
config[:apiKey],
|
|
21
|
+
config[:bearerToken],
|
|
22
|
+
config[:signingKey]
|
|
23
|
+
)
|
|
24
|
+
blockchyp.gateway_host = config[:gatewayHost]
|
|
25
|
+
blockchyp.test_gateway_host = config[:testGatewayHost]
|
|
26
|
+
blockchyp.dashboard_host = config[:dashboardHost]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Set request parameters
|
|
33
|
+
setup_request = {
|
|
34
|
+
pan: '4111111111111111',
|
|
35
|
+
expMonth: '12',
|
|
36
|
+
expYear: '2025',
|
|
37
|
+
amount: '25.55',
|
|
38
|
+
test: true,
|
|
39
|
+
transactionRef: uuid
|
|
40
|
+
}
|
|
41
|
+
response = blockchyp.charge(setup_request)
|
|
42
|
+
|
|
43
|
+
# Set request parameters
|
|
44
|
+
request = {
|
|
45
|
+
maxResults: 10
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
response = blockchyp.batch_history(request)
|
|
49
|
+
assert_not_nil(response)
|
|
50
|
+
# response assertions
|
|
51
|
+
assert(response[:success])
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2019-2023 BlockChyp, Inc. All rights reserved. Use of this code is
|
|
4
|
+
# governed by a license that can be found in the LICENSE file.
|
|
5
|
+
#
|
|
6
|
+
# This file was generated automatically by the BlockChyp SDK Generator.
|
|
7
|
+
# Changes to this file will be lost every time the code is regenerated.
|
|
8
|
+
|
|
9
|
+
require ::File.expand_path('test_helper', __dir__)
|
|
10
|
+
|
|
11
|
+
module BlockChyp
|
|
12
|
+
class BooleanPromptTest < TestCase
|
|
13
|
+
def test_boolean_prompt
|
|
14
|
+
|
|
15
|
+
puts "Running test_boolean_prompt..."
|
|
16
|
+
|
|
17
|
+
config = load_test_config
|
|
18
|
+
|
|
19
|
+
blockchyp = BlockChyp.new(
|
|
20
|
+
config[:apiKey],
|
|
21
|
+
config[:bearerToken],
|
|
22
|
+
config[:signingKey]
|
|
23
|
+
)
|
|
24
|
+
blockchyp.gateway_host = config[:gatewayHost]
|
|
25
|
+
blockchyp.test_gateway_host = config[:testGatewayHost]
|
|
26
|
+
blockchyp.dashboard_host = config[:dashboardHost]
|
|
27
|
+
|
|
28
|
+
test_delay(blockchyp, 'boolean_prompt_test', config[:defaultTerminalName])
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Set request parameters
|
|
34
|
+
request = {
|
|
35
|
+
test: true,
|
|
36
|
+
terminalName: config[:defaultTerminalName],
|
|
37
|
+
prompt: 'Would you like to become a member?',
|
|
38
|
+
yesCaption: 'Yes',
|
|
39
|
+
noCaption: 'No'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
response = blockchyp.boolean_prompt(request)
|
|
43
|
+
assert_not_nil(response)
|
|
44
|
+
# response assertions
|
|
45
|
+
assert(response[:success])
|
|
46
|
+
assert(response[:response])
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|