blockchyp 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Makefile +71 -0
- data/README.md +997 -0
- data/Rakefile +39 -0
- data/lib/blockchyp.rb +190 -0
- data/lib/blockchyp/version.rb +5 -0
- data/lib/blockchyp_client.rb +346 -0
- data/lib/crypto_utils.rb +25 -0
- data/test/boolean_prompt_test.rb +43 -0
- data/test/capture_signature_test.rb +40 -0
- data/test/gateway_timeout_test.rb +40 -0
- data/test/heartbeat_test.rb +27 -0
- data/test/new_transaction_display_test.rb +78 -0
- data/test/pan_charge_test.rb +53 -0
- data/test/pan_enroll_test.rb +51 -0
- data/test/pan_preauth_test.rb +52 -0
- data/test/simple_batch_close_test.rb +48 -0
- data/test/simple_capture_test.rb +47 -0
- data/test/simple_gift_activate_test.rb +42 -0
- data/test/simple_message_test.rb +40 -0
- data/test/simple_ping_test.rb +39 -0
- data/test/simple_refund_test.rb +48 -0
- data/test/simple_reversal_test.rb +48 -0
- data/test/simple_void_test.rb +48 -0
- data/test/terminal_charge_test.rb +51 -0
- data/test/terminal_clear_test.rb +39 -0
- data/test/terminal_ebt_balance_test.rb +41 -0
- data/test/terminal_ebt_charge_test.rb +53 -0
- data/test/terminal_enroll_test.rb +50 -0
- data/test/terminal_gift_card_balance_test.rb +40 -0
- data/test/terminal_keyed_charge_test.rb +52 -0
- data/test/terminal_manual_ebt_charge_test.rb +54 -0
- data/test/terminal_preauth_test.rb +51 -0
- data/test/terminal_status_test.rb +39 -0
- data/test/terminal_timeout_test.rb +39 -0
- data/test/terms_and_conditions_test.rb +44 -0
- data/test/test_helper.rb +65 -0
- data/test/text_prompt_test.rb +41 -0
- data/test/update_transaction_display_test.rb +78 -0
- metadata +81 -0
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
warn e.message
|
9
|
+
warn 'Run `bundle install` to install missing gems'
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'rake'
|
14
|
+
require 'rake/testtask'
|
15
|
+
|
16
|
+
task(default: %i[test lint])
|
17
|
+
|
18
|
+
Rake::TestTask.new(:test) do |t|
|
19
|
+
t.libs << '.' << 'lib' << 'test'
|
20
|
+
t.test_files = FileList['test/*_test.rb']
|
21
|
+
t.verbose = false
|
22
|
+
end
|
23
|
+
|
24
|
+
task :lint do
|
25
|
+
if RUBY_ENGINE == 'ruby'
|
26
|
+
require 'rubocop/rake_task'
|
27
|
+
RuboCop::RakeTask.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
task(gem: :build)
|
32
|
+
task :build do
|
33
|
+
system 'gem build blockchyp.gemspec'
|
34
|
+
end
|
35
|
+
|
36
|
+
task publish: :build do
|
37
|
+
system "gem push blockchyp-#{BlockChyp::VERSION}.gem"
|
38
|
+
system "rm blockchyp-#{BlockChyp::VERSION}.gem"
|
39
|
+
end
|
data/lib/blockchyp.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 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. Changes to this file will be lost
|
7
|
+
# every time the code is regenerated.
|
8
|
+
|
9
|
+
require_relative 'blockchyp_client'
|
10
|
+
|
11
|
+
module CardType
|
12
|
+
CREDIT = 0
|
13
|
+
DEBIT = 1
|
14
|
+
EBT = 2
|
15
|
+
BLOCKCHAIN_GIFT = 3
|
16
|
+
end
|
17
|
+
|
18
|
+
module SignatureFormat
|
19
|
+
NONE = ''
|
20
|
+
PNG = 'png'
|
21
|
+
JPG = 'jpg'
|
22
|
+
GIF = 'gif'
|
23
|
+
end
|
24
|
+
|
25
|
+
module PromptType
|
26
|
+
AMOUNT = 'amount'
|
27
|
+
EMAIL = 'email'
|
28
|
+
PHONE_NUMBER = 'phone'
|
29
|
+
CUSTOMER_NUMBER = 'customer-number'
|
30
|
+
REWARDS_NUMBER = 'rewards-number'
|
31
|
+
FIRST_NAME = 'first-name'
|
32
|
+
LAST_NAME = 'last-name'
|
33
|
+
end
|
34
|
+
|
35
|
+
module AVSResponse
|
36
|
+
NOT_APPLICABLE = ''
|
37
|
+
NOT_SUPPORTED = 'not_supported'
|
38
|
+
RETRY = 'retry'
|
39
|
+
NO_MATCH = 'no_match'
|
40
|
+
ADDRESS_MATCH = 'address_match'
|
41
|
+
POSTAL_CODE_MATCH = 'zip_match'
|
42
|
+
ADDRESS_AND_POSTAL_CODE_MATCH = 'match'
|
43
|
+
end
|
44
|
+
|
45
|
+
module BlockChyp
|
46
|
+
# the main autogenerated blockchyp client
|
47
|
+
class BlockChyp < BlockChypClient
|
48
|
+
|
49
|
+
def heartbeat(test)
|
50
|
+
gateway_request('GET', '/api/heartbeat', {'test' => test})
|
51
|
+
end
|
52
|
+
|
53
|
+
# Executes a standard direct preauth and capture.
|
54
|
+
def charge(request)
|
55
|
+
route_terminal_request('POST', '/api/charge', '/api/charge', request)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Executes a preauthorization intended to be captured later.
|
59
|
+
def preauth(request)
|
60
|
+
route_terminal_request('POST', '/api/preauth', '/api/preauth', request)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Tests connectivity with a payment terminal.
|
64
|
+
def ping(request)
|
65
|
+
route_terminal_request('POST', '/api/test', '/api/terminal-test', request)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Checks the remaining balance on a payment method.
|
69
|
+
def balance(request)
|
70
|
+
route_terminal_request('POST', '/api/balance', '/api/balance', request)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Clears the line item display and any in progress transaction.
|
74
|
+
def clear(request)
|
75
|
+
route_terminal_request('POST', '/api/clear', '/api/terminal-clear', request)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Prompts the user to accept terms and conditions.
|
79
|
+
def terms_and_conditions(request)
|
80
|
+
route_terminal_request('POST', '/api/tc', '/api/terminal-tc', request)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Appends items to an existing transaction display. Subtotal, Tax, and
|
84
|
+
# Total are overwritten by the request. Items with the same description
|
85
|
+
# are combined into groups.
|
86
|
+
def update_transaction_display(request)
|
87
|
+
route_terminal_request('PUT', '/api/txdisplay', '/api/terminal-txdisplay', request)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Displays a new transaction on the terminal.
|
91
|
+
def new_transaction_display(request)
|
92
|
+
route_terminal_request('POST', '/api/txdisplay', '/api/terminal-txdisplay', request)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Asks the consumer a text based question.
|
96
|
+
def text_prompt(request)
|
97
|
+
route_terminal_request('POST', '/api/text-prompt', '/api/text-prompt', request)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Asks the consumer a yes/no question.
|
101
|
+
def boolean_prompt(request)
|
102
|
+
route_terminal_request('POST', '/api/boolean-prompt', '/api/boolean-prompt', request)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Displays a short message on the terminal.
|
106
|
+
def message(request)
|
107
|
+
route_terminal_request('POST', '/api/message', '/api/message', request)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Executes a refund.
|
111
|
+
def refund(request)
|
112
|
+
route_terminal_request('POST', '/api/refund', '/api/refund', request)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Adds a new payment method to the token vault.
|
116
|
+
def enroll(request)
|
117
|
+
route_terminal_request('POST', '/api/enroll', '/api/enroll', request)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Activates or recharges a gift card.
|
121
|
+
def gift_activate(request)
|
122
|
+
route_terminal_request('POST', '/api/gift-activate', '/api/gift-activate', request)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns the current status of a terminal.
|
126
|
+
def terminal_status(request)
|
127
|
+
route_terminal_request('POST', '/api/terminal-status', '/api/terminal-status', request)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Captures and returns a signature.
|
131
|
+
def capture_signature(request)
|
132
|
+
route_terminal_request('POST', '/api/capture-signature', '/api/capture-signature', request)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Executes a manual time out reversal.
|
136
|
+
#
|
137
|
+
# We love time out reversals. Don't be afraid to use them whenever a
|
138
|
+
# request to a BlockChyp terminal times out. You have up to two minutes to
|
139
|
+
# reverse any transaction. The only caveat is that you must assign
|
140
|
+
# transactionRef values when you build the original request. Otherwise, we
|
141
|
+
# have no real way of knowing which transaction you're trying to reverse
|
142
|
+
# because we may not have assigned it an id yet. And if we did assign it
|
143
|
+
# an id, you wouldn't know what it is because your request to the terminal
|
144
|
+
# timed out before you got a response.
|
145
|
+
def reverse(request)
|
146
|
+
gateway_request('POST', '/api/reverse', request)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Captures a preauthorization.
|
150
|
+
def capture(request)
|
151
|
+
gateway_request('POST', '/api/capture', request)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Closes the current credit card batch.
|
155
|
+
def close_batch(request)
|
156
|
+
gateway_request('POST', '/api/close-batch', request)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Discards a previous preauth transaction.
|
160
|
+
def void(request)
|
161
|
+
gateway_request('POST', '/api/void', request)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Updates or creates a customer record.
|
165
|
+
def update_customer(request)
|
166
|
+
gateway_request('POST', '/api/update-customer', request)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Retrieves a customer by id.
|
170
|
+
def customer(request)
|
171
|
+
gateway_request('POST', '/api/customer', request)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Searches the customer database.
|
175
|
+
def customer_search(request)
|
176
|
+
gateway_request('POST', '/api/customer-search', request)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Retrieves the current status of a transaction.
|
180
|
+
def transaction_status(request)
|
181
|
+
gateway_request('POST', '/api/tx-status', request)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Creates and send a payment link to a customer.
|
185
|
+
def send_payment_link(request)
|
186
|
+
gateway_request('POST', '/api/send-payment-link', request)
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,346 @@
|
|
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
|
+
@https = false
|
25
|
+
@route_cache_location = File.join(Dir.tmpdir, '.blockchyp_route')
|
26
|
+
@route_cache_ttl = 60
|
27
|
+
@gateway_timeout = 20
|
28
|
+
@terminal_timeout = 120
|
29
|
+
@terminal_connect_timeout = 5
|
30
|
+
@offline_cache_enabled = true
|
31
|
+
@route_cache = {}
|
32
|
+
@offline_fixed_key = 'cb22789c9d5c344a10e0474f134db39e25eb3bbf5a1b1a5e89b507f15ea9519c'
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :api_key
|
36
|
+
attr_reader :bearer_token
|
37
|
+
attr_reader :signing_key
|
38
|
+
attr_reader :offline_fixed_key
|
39
|
+
attr_accessor :gateway_host
|
40
|
+
attr_accessor :test_gateway_host
|
41
|
+
attr_accessor :https
|
42
|
+
attr_accessor :route_cache_ttl
|
43
|
+
attr_accessor :gateway_timeout
|
44
|
+
attr_accessor :terminal_timeout
|
45
|
+
attr_accessor :offline_cache_enabled
|
46
|
+
attr_accessor :terminal_connect_timeout
|
47
|
+
attr_accessor :route_cache_location
|
48
|
+
|
49
|
+
def generate_gateway_headers
|
50
|
+
nonce = CryptoUtils.generate_nonce
|
51
|
+
tsp = CryptoUtils.timestamp
|
52
|
+
|
53
|
+
sig = compute_hmac(tsp, nonce)
|
54
|
+
|
55
|
+
{
|
56
|
+
'Nonce' => nonce,
|
57
|
+
'Timestamp' => tsp,
|
58
|
+
'Authorization' => 'Dual ' + bearer_token + ':' + api_key + ':' + sig
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def compute_hmac(tsp, nonce)
|
63
|
+
canonical_string = api_key + bearer_token + tsp + nonce
|
64
|
+
|
65
|
+
OpenSSL::HMAC.hexdigest('SHA256', CryptoUtils.hex2bin(signing_key), canonical_string)
|
66
|
+
end
|
67
|
+
|
68
|
+
def resolve_gateway_uri(path, request)
|
69
|
+
url = if request.nil? || !request['test']
|
70
|
+
gateway_host
|
71
|
+
else
|
72
|
+
test_gateway_host
|
73
|
+
end
|
74
|
+
|
75
|
+
URI.parse(url + path)
|
76
|
+
end
|
77
|
+
|
78
|
+
def generate_error_response(msg)
|
79
|
+
[
|
80
|
+
'success' => false,
|
81
|
+
'error' => msg,
|
82
|
+
'responseDescription' => msg
|
83
|
+
]
|
84
|
+
end
|
85
|
+
|
86
|
+
def route_terminal_request(method, terminal_path, gateway_path, request)
|
87
|
+
if request['terminalName'].nil?
|
88
|
+
return gateway_request(method, gateway_path, request)
|
89
|
+
end
|
90
|
+
|
91
|
+
route = resolve_terminal_route(request['terminalName'])
|
92
|
+
if !route
|
93
|
+
return generate_error_response('Unkown Terminal')
|
94
|
+
elsif route['cloudRelayEnabled']
|
95
|
+
return gateway_request(method, gateway_path, request, relay: true)
|
96
|
+
end
|
97
|
+
|
98
|
+
terminal_request(method, route, terminal_path, request, true)
|
99
|
+
end
|
100
|
+
|
101
|
+
def terminal_request(method, route, path, request, open_retry)
|
102
|
+
uri = resolve_terminal_uri(route, path)
|
103
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
104
|
+
http.use_ssl = uri.instance_of?(URI::HTTPS)
|
105
|
+
timeout = get_timeout(request, terminal_timeout)
|
106
|
+
http.open_timeout = timeout
|
107
|
+
http.read_timeout = timeout
|
108
|
+
|
109
|
+
tx_creds = route['transientCredentials']
|
110
|
+
|
111
|
+
wrapped_request = {
|
112
|
+
'apiKey' => tx_creds['apiKey'],
|
113
|
+
'bearerToken' => tx_creds['bearerToken'],
|
114
|
+
'signingKey' => tx_creds['signingKey'],
|
115
|
+
'request' => request
|
116
|
+
}
|
117
|
+
|
118
|
+
req = get_http_request(method, uri)
|
119
|
+
|
120
|
+
req['User-Agent'] = user_agent
|
121
|
+
json = wrapped_request.to_json
|
122
|
+
req['Content-Type'] = 'application/json'
|
123
|
+
req['Content-Length'] = json.length
|
124
|
+
req.body = json
|
125
|
+
|
126
|
+
begin
|
127
|
+
response = http.request(req)
|
128
|
+
rescue Net::OpenTimeout, Errno::EHOSTDOWN, Errno::EHOSTUNREACH, Errno::ETIMEDOUT, Errno::ENETUNREACH
|
129
|
+
if open_retry
|
130
|
+
evict(route['terminalName'])
|
131
|
+
route = resolve_terminal_route(route['terminalName'])
|
132
|
+
return terminal_request(method, route, path, request, false)
|
133
|
+
end
|
134
|
+
raise
|
135
|
+
end
|
136
|
+
if response.is_a?(Net::HTTPSuccess)
|
137
|
+
JSON.parse(response.body)
|
138
|
+
else
|
139
|
+
raise response.message
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def get_http_request(method, uri)
|
144
|
+
case method
|
145
|
+
when 'GET'
|
146
|
+
Net::HTTP::Get.new(uri.request_uri)
|
147
|
+
when 'PUT'
|
148
|
+
Net::HTTP::Put.new(uri.request_uri)
|
149
|
+
when 'POST'
|
150
|
+
Net::HTTP::Post.new(uri.request_uri)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def resolve_terminal_uri(route, path)
|
155
|
+
url = if https
|
156
|
+
'https://'
|
157
|
+
else
|
158
|
+
'http://'
|
159
|
+
end
|
160
|
+
url += route['ipAddress']
|
161
|
+
port = if https
|
162
|
+
':8443'
|
163
|
+
else
|
164
|
+
':8080'
|
165
|
+
end
|
166
|
+
URI.parse(url + port + path)
|
167
|
+
end
|
168
|
+
|
169
|
+
def gateway_request(method, path, request = nil, relay = false)
|
170
|
+
uri = resolve_gateway_uri(path, request)
|
171
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
172
|
+
http.use_ssl = uri.instance_of?(URI::HTTPS)
|
173
|
+
timeout = get_timeout(request, relay ? terminal_timeout : gateway_timeout)
|
174
|
+
http.open_timeout = timeout
|
175
|
+
http.read_timeout = timeout
|
176
|
+
|
177
|
+
req = get_http_request(method, uri)
|
178
|
+
|
179
|
+
req['User-Agent'] = user_agent
|
180
|
+
unless request.nil?
|
181
|
+
json = request.to_json
|
182
|
+
req['Content-Type'] = 'application/json'
|
183
|
+
req['Content-Length'] = json.length
|
184
|
+
req.body = json
|
185
|
+
end
|
186
|
+
|
187
|
+
headers = generate_gateway_headers
|
188
|
+
headers.each do |key, value|
|
189
|
+
req[key] = value
|
190
|
+
end
|
191
|
+
|
192
|
+
response = http.request(req)
|
193
|
+
|
194
|
+
if response.is_a?(Net::HTTPSuccess)
|
195
|
+
JSON.parse(response.body)
|
196
|
+
else
|
197
|
+
raise response.message
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
attr_accessor :routeCache
|
202
|
+
|
203
|
+
def resolve_terminal_route(terminal_name)
|
204
|
+
route = route_cache_get(terminal_name, false)
|
205
|
+
|
206
|
+
if route.nil?
|
207
|
+
route = request_route_from_gateway(terminal_name)
|
208
|
+
if route.nil?
|
209
|
+
return route
|
210
|
+
end
|
211
|
+
|
212
|
+
ttl = Time.now.utc + (route_cache_ttl * 60)
|
213
|
+
route_cache_entry = {}
|
214
|
+
route_cache_entry['route'] = route
|
215
|
+
route_cache_entry['ttl'] = ttl
|
216
|
+
@route_cache[api_key + terminal_name] = route_cache_entry
|
217
|
+
update_offline_cache(route_cache_entry)
|
218
|
+
end
|
219
|
+
route
|
220
|
+
end
|
221
|
+
|
222
|
+
def update_offline_cache(route_cache_entry)
|
223
|
+
if offline_cache_enabled
|
224
|
+
offline_cache = read_offline_cache
|
225
|
+
offline_entry = route_cache_entry.clone
|
226
|
+
route = route_cache_entry['route'].clone
|
227
|
+
tx_creds = route['transientCredentials'].clone
|
228
|
+
tx_creds['apiKey'] = encrypt(tx_creds['apiKey'])
|
229
|
+
tx_creds['bearerToken'] = encrypt(tx_creds['bearerToken'])
|
230
|
+
tx_creds['signingKey'] = encrypt(tx_creds['signingKey'])
|
231
|
+
route['transientCredentials'] = tx_creds
|
232
|
+
offline_entry['route'] = route
|
233
|
+
offline_cache[api_key + route['terminalName']] = offline_entry
|
234
|
+
File.write(route_cache_location, offline_cache.to_json)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def encrypt(plain_text)
|
239
|
+
return plain_text if plain_text.nil? || plain_text.empty?
|
240
|
+
|
241
|
+
cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
242
|
+
cipher.encrypt
|
243
|
+
cipher.key = derive_offline_key
|
244
|
+
iv = cipher.random_iv
|
245
|
+
|
246
|
+
Base64.encode64(iv) + ':' + Base64.encode64(cipher.update(plain_text) + cipher.final)
|
247
|
+
end
|
248
|
+
|
249
|
+
def decrypt(cipher_text)
|
250
|
+
return cipher_text if cipher_text.nil? || cipher_text.empty?
|
251
|
+
|
252
|
+
tokens = cipher_text.split(':')
|
253
|
+
|
254
|
+
return cipher_text if tokens[0].nil? || tokens[1].nil?
|
255
|
+
|
256
|
+
iv = Base64.decode64(tokens[0])
|
257
|
+
cp = Base64.decode64(tokens[1])
|
258
|
+
|
259
|
+
decipher = OpenSSL::Cipher::AES256.new(:CBC)
|
260
|
+
decipher.decrypt
|
261
|
+
decipher.key = derive_offline_key
|
262
|
+
decipher.iv = iv
|
263
|
+
|
264
|
+
decipher.update(cp) + decipher.final
|
265
|
+
end
|
266
|
+
|
267
|
+
def derive_offline_key
|
268
|
+
Digest::SHA256.digest offline_fixed_key + signing_key
|
269
|
+
end
|
270
|
+
|
271
|
+
def request_route_from_gateway(terminal_name)
|
272
|
+
route = gateway_request('GET', '/api/terminal-route?terminal=' + CGI.escape(terminal_name))
|
273
|
+
if !route.nil? && !route['ipAddress'].empty?
|
274
|
+
route['exists'] = true
|
275
|
+
end
|
276
|
+
route
|
277
|
+
end
|
278
|
+
|
279
|
+
def evict(terminal_name)
|
280
|
+
route_cache.delete(api_key + terminal_name)
|
281
|
+
|
282
|
+
offline_cache = read_offline_cache
|
283
|
+
offline_cache.delete(api_key + terminal_name)
|
284
|
+
File.write(route_cache_location, route_cache.to_json)
|
285
|
+
end
|
286
|
+
|
287
|
+
def route_cache_get(terminal_name, stale)
|
288
|
+
route_cache_entry = @route_cache[api_key + terminal_name]
|
289
|
+
|
290
|
+
if route_cache_entry.nil? && offline_cache_enabled
|
291
|
+
offline_cache = read_offline_cache
|
292
|
+
route_cache_entry = offline_cache[@api_key + terminal_name]
|
293
|
+
end
|
294
|
+
|
295
|
+
if route_cache_entry
|
296
|
+
route = route_cache_entry['route']
|
297
|
+
tx_creds = route['transientCredentials']
|
298
|
+
tx_creds['apiKey'] = decrypt(tx_creds['apiKey'])
|
299
|
+
tx_creds['bearerToken'] = decrypt(tx_creds['bearerToken'])
|
300
|
+
tx_creds['signingKey'] = decrypt(tx_creds['signingKey'])
|
301
|
+
route['transientCredentials'] = tx_creds
|
302
|
+
route_cache_entry['route'] = route
|
303
|
+
end
|
304
|
+
|
305
|
+
if route_cache_entry
|
306
|
+
now = Time.new
|
307
|
+
raw_ttl = route_cache_entry['ttl']
|
308
|
+
if raw_ttl.instance_of?(Time)
|
309
|
+
ttl = raw_ttl
|
310
|
+
else
|
311
|
+
ttl = Time.parse(route_cache_entry['ttl'])
|
312
|
+
end
|
313
|
+
if stale || now < ttl
|
314
|
+
route_cache_entry['route']
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def read_offline_cache
|
320
|
+
if File.file?(route_cache_location)
|
321
|
+
|
322
|
+
config_file = File.open(route_cache_location)
|
323
|
+
content = config_file.read
|
324
|
+
|
325
|
+
return JSON.parse(content)
|
326
|
+
end
|
327
|
+
{}
|
328
|
+
end
|
329
|
+
|
330
|
+
def user_agent
|
331
|
+
if defined? VERSION
|
332
|
+
"BlockChyp-Ruby/#{VERSION}"
|
333
|
+
else
|
334
|
+
"BlockChyp-Ruby"
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def get_timeout(request, default)
|
339
|
+
if request.nil? || request['timeout'].nil? || request['timeout'].zero?
|
340
|
+
return default
|
341
|
+
end
|
342
|
+
|
343
|
+
request['timeout']
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|