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