Class: LitleOnline::LitleRequest
- Inherits:
-
Object
- Object
- LitleOnline::LitleRequest
- Includes:
- XML::Mapping
- Defined in:
- lib/XMLFields.rb,
lib/LitleRequest.rb
Instance Method Summary collapse
-
#add_rfr_request(options, path = File.dirname(@path_to_batches)) ⇒ Object
Adds an RFRRequest to the LitleRequest.
-
#commit_batch(arg) ⇒ Object
Adds a batch to the LitleRequest.
-
#create_new_litle_request(path) ⇒ Object
Creates the necessary files for the LitleRequest at the path specified.
-
#finish_request ⇒ Object
Called when you wish to finish adding batches to your request, this method rewrites the aggregate batch file to the final LitleRequest xml doc with the appropos LitleRequest tags.
- #get_path_to_batches ⇒ Object
-
#get_responses_from_server(args = {}) ⇒ Object
Grabs response files over SFTP from Litle.
-
#initialize(options = {}) ⇒ LitleRequest
constructor
A new instance of LitleRequest.
-
#process_response(path_to_response, transaction_listener, _batch_listener = nil) ⇒ Object
- Params:
path_to_response
- The path to a specific .asc file to process
transaction_listener
- A listener to be applied to the hash of each transaction (see
DefaultLitleListener
)batch_listener
-
An (optional) listener to be applied to the hash of each batch.
- A listener to be applied to the hash of each transaction (see
- The path to a specific .asc file to process
- Params:
-
#process_responses(args) ⇒ Object
- Params:
args
-
A
Hash
containing arguments for the processing process.
- Params:
-
#send_to_litle(path = File.dirname(@path_to_batches), options = {}) ⇒ Object
- FTPs all previously unsent LitleRequests located in the folder denoted by path to the server Params:
path
-
A
String
containing the path to the folder on disc where LitleRequests are located.
- FTPs all previously unsent LitleRequests located in the folder denoted by path to the server Params:
-
#send_to_litle_stream(options = {}, path = File.dirname(@path_to_batches)) ⇒ Object
Sends all previously unsent LitleRequests in the specified directory to the Litle server by use of fast batch.
Constructor Details
#initialize(options = {}) ⇒ LitleRequest
Returns a new instance of LitleRequest.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/LitleRequest.rb', line 37 def initialize( = {}) # load configuration data @config_hash = Configuration.new.config @num_batch_requests = 0 @path_to_request = '' @path_to_batches = '' @num_total_transactions = 0 @MAX_NUM_TRANSACTIONS = 500_000 @options = # current time out set to 2 mins # this value is in seconds @RESPONSE_TIME_OUT = 360 @POLL_DELAY = 0 @responses_expected = 0 end |
Instance Method Details
#add_rfr_request(options, path = File.dirname(@path_to_batches)) ⇒ Object
Adds an RFRRequest to the LitleRequest. params:
options
-
a required
Hash
containing configuration info for the RFRRequest. If the RFRRequest is for a batch, then the
litleSessionId is required as a key/val pair. If the RFRRequest is for account updater, then merchantId and postDay are required as key/val pairs.
path
-
optional path to save the new litle request containing the RFRRequest at
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/LitleRequest.rb', line 161 def add_rfr_request(, path = File.dirname(@path_to_batches)) rfrrequest = LitleRFRRequest.new if ['litleSessionId'] rfrrequest.litleSessionId = ['litleSessionId'] elsif ['merchantId'] && ['postDay'] accountUpdate = AccountUpdateFileRequestData.new accountUpdate.merchantId = ['merchantId'] accountUpdate.postDay = ['postDay'] rfrrequest.accountUpdateFileRequestData = accountUpdate else raise ArgumentError, "For an RFR Request, you must specify either a litleSessionId for an RFRRequest for batch or a merchantId and a postDay for an RFRRequest for account updater." end litleRequest = LitleRequestForRFR.new litleRequest.rfrRequest = rfrrequest authentication = Authentication.new authentication.user = get_config(:user, ) authentication.password = get_config(:password, ) litleRequest.authentication = authentication litleRequest.numBatchRequests = '0' litleRequest.version = '8.31' litleRequest.xmlns = 'http://www.litle.com/schema' xml = litleRequest.save_to_xml.to_s ts = Time.now.to_i.to_s begin ts += Time.now.nsec.to_s rescue NoMethodError # ruby 1.8.7 fix ts += Time.now.usec.to_s end raise 'Entered a file not a path.' if File.file?(path) path += File::SEPARATOR if path[-1, 1] != '/' && path[-1, 1] != '\\' Dir.mkdir(path) unless File.directory?(path) path_to_request = path + 'request_' + ts File.open(path_to_request, 'a+') do |file| file.write xml end File.rename(path_to_request, path_to_request + '.complete') @RESPONSE_TIME_OUT += 90 end |
#commit_batch(arg) ⇒ Object
Adds a batch to the LitleRequest. If the batch is open when passed, it will be closed prior to being added. Params:
arg
-
a
LitleBatchRequest
containing the transactions you wish to send or aString
specifying the
path to the batch file
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/LitleRequest.rb', line 91 def commit_batch(arg) path_to_batch = '' # they passed a batch if arg.is_a?(LitleBatchRequest) path_to_batch = arg.get_batch_name if (au = arg.get_au_batch) != nil # also commit the account updater batch commit_batch(au) end elsif arg.is_a?(LitleAUBatch) path_to_batch = arg.get_batch_name elsif arg.is_a?(String) path_to_batch = arg else raise 'You entered neither a path nor a batch. Game over :(' end # the batch isn't closed. let's help a brother out if (ind = path_to_batch.index(/\.closed/)).nil? if arg.is_a?(String) new_batch = LitleBatchRequest.new new_batch.open_existing_batch(path_to_batch) new_batch.close_batch path_to_batch = new_batch.get_batch_name # if we passed a path to an AU batch, then new_batch will be a new, empty batch and the batch we passed # will be in the AU batch variable. thus, we wanna grab that file name and remove the empty batch. unless new_batch.get_au_batch.nil? File.remove(path_to_batch) path_to_batch = new_batch.get_au_batch.get_batch_name end elsif arg.is_a?(LitleBatchRequest) arg.close_batch path_to_batch = arg.get_batch_name elsif arg.is_a?(LitleAUBatch) arg.close_batch path_to_batch = arg.get_batch_name end ind = path_to_batch.index(/\.closed/) end transactions_in_batch = path_to_batch[ind + 8..path_to_batch.length].to_i # if the litle request would be too big, let's make another! if (@num_total_transactions + transactions_in_batch) > @MAX_NUM_TRANSACTIONS finish_request initialize(@options) create_new_litle_request else # otherwise, let's add it line by line to the request doc # @num_batch_requests += 1 # how long we wnat to wait around for the FTP server to get us a response @RESPONSE_TIME_OUT += 90 + (transactions_in_batch * 0.25) # don't start looking until there could possibly be a response @POLL_DELAY += 30 + (transactions_in_batch * 0.02) @num_total_transactions += transactions_in_batch # Don't add empty batches @num_batch_requests += 1 unless transactions_in_batch.eql?(0) File.open(@path_to_batches, 'a+') do |fo| File.foreach(path_to_batch) do |li| fo.puts li end end File.delete(path_to_batch) end end |
#create_new_litle_request(path) ⇒ Object
Creates the necessary files for the LitleRequest at the path specified. path/request_(TIMESTAMP) will be the final XML markup and path/request_(TIMESTAMP) will hold intermediary XML markup Params:
path
-
A
String
containing the path to the folder on disc to write the files to
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/LitleRequest.rb', line 57 def create_new_litle_request(path) ts = Time.now.to_i.to_s begin ts += Time.now.nsec.to_s rescue NoMethodError # ruby 1.8.7 fix ts += Time.now.usec.to_s end raise 'Entered a file not a path.' if File.file?(path) path += File::SEPARATOR if path[-1, 1] != '/' && path[-1, 1] != '\\' Dir.mkdir(path) unless File.directory?(path) @path_to_request = path + 'request_' + ts @path_to_batches = @path_to_request + '_batches' if File.file?(@path_to_request) || File.file?(@path_to_batches) create_new_litle_request(path) return end File.open(@path_to_request, 'a+') do |file| file.write('') end File.open(@path_to_batches, 'a+') do |file| file.write('') end end |
#finish_request ⇒ Object
Called when you wish to finish adding batches to your request, this method rewrites the aggregate batch file to the final LitleRequest xml doc with the appropos LitleRequest tags.
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'lib/LitleRequest.rb', line 446 def finish_request File.open(@path_to_request, 'w') do |f| # jam dat header in there f.puts(build_request_header) # read into the request file from the batches file File.foreach(@path_to_batches) do |li| f.puts li end # finally, let's poot in a header, for old time's sake f.puts '</litleRequest>' end # rename the requests file File.rename(@path_to_request, @path_to_request + '.complete') # we don't need the master batch file anymore File.delete(@path_to_batches) end |
#get_path_to_batches ⇒ Object
440 441 442 |
# File 'lib/LitleRequest.rb', line 440 def get_path_to_batches @path_to_batches end |
#get_responses_from_server(args = {}) ⇒ Object
Grabs response files over SFTP from Litle. Params:
args
-
An (optional)
Hash
containing values for the number of responses expected, the
path to the folder on disk to write the responses from the Litle server to, the username and password with which to connect ot the sFTP server, and the URL to connect over sFTP. Values not provided in the hash will be populate automatically based on our best guess
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/LitleRequest.rb', line 310 def get_responses_from_server(args = {}) @responses_expected = args[:responses_expected] ||= @responses_expected response_path = args[:response_path] ||= (File.dirname(@path_to_batches) + '/responses/') username = get_config(:sftp_username, args) password = get_config(:sftp_password, args) url = get_config(:sftp_url, args) if username.nil? || password.nil? || url.nil? raise ConfigurationException, 'You are not configured to use sFTP for batch processing. Please run /bin/Setup.rb again!' end if response_path[-1, 1] != '/' && response_path[-1, 1] != '\\' response_path += File::SEPARATOR end Dir.mkdir(response_path) unless File.directory?(response_path) begin responses_grabbed = 0 Net::SFTP.start(url, username, password: password) do |sftp| # clear out the sFTP outbound dir prior to checking for new files, avoids leaving files on the server # if files are left behind we are not counting then towards the expected total sftp.dir.foreach('/outbound/') do |entry| if (entry.name =~ /request_\d+.complete.asc\z/) != nil sftp.download!('/outbound/' + entry.name, response_path + entry.name.gsub('request', 'response') + '.received') 3.times do begin sftp.remove!('/outbound/' + entry.name) break rescue Net::SFTP::StatusException # try, try, try again puts "We couldn't remove it! Try again" end end end end end # wait until a response has a possibility of being there sleep(@POLL_DELAY) time_begin = Time.now Net::SFTP.start(url, username, password: password) do |sftp| while (Time.now - time_begin) < @RESPONSE_TIME_OUT && responses_grabbed < @responses_expected # sleep for 60 seconds, ¿no es bueno? sleep(60) sftp.dir.foreach('/outbound/') do |entry| if (entry.name =~ /request_\d+.complete.asc\z/) != nil sftp.download!('/outbound/' + entry.name, response_path + entry.name.gsub('request', 'response') + '.received') responses_grabbed += 1 3.times do begin sftp.remove!('/outbound/' + entry.name) break rescue Net::SFTP::StatusException # try, try, try again puts "We couldn't remove it! Try again" end end end end end # if our timeout timed out, we're having problems if responses_grabbed < @responses_expected raise 'We timed out in waiting for a response from the server. :(' end end rescue Net::SSH::AuthenticationFailed raise ArgumentError, 'The sFTP credentials provided were incorrect. Try again!' end end |
#process_response(path_to_response, transaction_listener, _batch_listener = nil) ⇒ Object
Params:
path_to_response
-
The path to a specific .asc file to process
transaction_listener
-
A listener to be applied to the hash of each transaction
(see DefaultLitleListener
)
batch_listener
-
An (optional) listener to be applied to the hash of each batch.
Note that this will om-nom-nom quite a bit of memory
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
# File 'lib/LitleRequest.rb', line 407 def process_response(path_to_response, transaction_listener, _batch_listener = nil) reader = LibXML::XML::Reader.file(path_to_response) reader.read # read into the root node # if the response attribute is nil, we're dealing with an RFR and everything is a-okay if reader.get_attribute('response') != '0' && !reader.get_attribute('response').nil? raise 'Error parsing Litle Request: ' + reader.get_attribute('message') end reader.read count = 0 while true && count < 500_001 count += 1 return false if reader.node.nil? case reader.node.name.to_s when 'batchResponse' reader.read when 'litleResponse' return false when 'text' reader.read else xml = reader.read_outer_xml duck = Crack::XML.parse(xml) duck[duck.keys[0]]['type'] = duck.keys[0] duck = duck[duck.keys[0]] transaction_listener.apply(duck) reader.next end end end |
#process_responses(args) ⇒ Object
Params:
args
-
A
Hash
containing arguments for the processing process. This hash MUST contain an entry
for a transaction listener (see DefaultLitleListener
). It may also include a batch listener and a custom path where response files from the server are located (if it is not provided, we’ll guess the position)
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
# File 'lib/LitleRequest.rb', line 383 def process_responses(args) # the transaction listener is required unless args.key?(:transaction_listener) raise ArgumentError, 'The arguments hash must contain an entry for transaction listener!' end transaction_listener = args[:transaction_listener] batch_listener = args[:batch_listener] ||= nil path_to_responses = args[:path_to_responses] ||= (File.dirname(@path_to_batches) + '/responses/') Dir.foreach(path_to_responses) do |filename| if (filename =~ /response_\d+.complete.asc.received\z/) != nil process_response(path_to_responses + filename, transaction_listener, batch_listener) File.rename(path_to_responses + filename, path_to_responses + filename + '.processed') end end end |
#send_to_litle(path = File.dirname(@path_to_batches), options = {}) ⇒ Object
FTPs all previously unsent LitleRequests located in the folder denoted by path to the server Params:
path
-
A
String
containing the path to the folder on disc where LitleRequests are located.
This should be the same location where the LitleRequests were written to. If no path is explicitly provided, then we use the directory where the current working batches file is stored.
options
-
An (option)
Hash
containing the username, password, and URL to attempt to sFTP to.
If not provided, the values will be populated from the configuration file.
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/LitleRequest.rb', line 218 def send_to_litle(path = File.dirname(@path_to_batches), = {}) username = get_config(:sftp_username, ) password = get_config(:sftp_password, ) url = get_config(:sftp_url, ) if username.nil? || password.nil? || url.nil? raise ArgumentError, 'You are not configured to use sFTP for batch processing. Please run /bin/Setup.rb again!' end path += File::SEPARATOR if path[-1, 1] != '/' && path[-1, 1] != '\\' begin Net::SFTP.start(url, username, password: password) do |sftp| # our folder is /SHORTNAME/SHORTNAME/INBOUND Dir.foreach(path) do |filename| # we have a complete report according to filename regex if (filename =~ /request_\d+.complete\z/) != nil # adding .prg extension per the XML File.rename(path + filename, path + filename + '.prg') end end @responses_expected = 0 Dir.foreach(path) do |filename| if (filename =~ /request_\d+.complete.prg\z/) != nil # upload the file sftp.upload!(path + filename, '/inbound/' + filename) @responses_expected += 1 # rename now that we're done sftp.rename!('/inbound/' + filename, '/inbound/' + filename.gsub('prg', 'asc')) File.rename(path + filename, path + filename.gsub('prg', 'sent')) end end end rescue Net::SSH::AuthenticationFailed raise ArgumentError, 'The sFTP credentials provided were incorrect. Try again!' end end |
#send_to_litle_stream(options = {}, path = File.dirname(@path_to_batches)) ⇒ Object
Sends all previously unsent LitleRequests in the specified directory to the Litle server by use of fast batch. All results will be written to disk as we get them. Note that use of fastbatch is strongly discouraged!
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/LitleRequest.rb', line 261 def send_to_litle_stream( = {}, path = File.dirname(@path_to_batches)) url = get_config(:fast_url, ) port = get_config(:fast_port, ) if url.nil? || url == '' raise ArgumentError, 'A URL for fastbatch was not specified in the config file or passed options. Reconfigure and try again.' end if port == '' || port.nil? raise ArgumentError, 'A port number for fastbatch was not specified in the config file or passed options. Reconfigure and try again.' end path += File::SEPARATOR if path[-1, 1] != '/' && path[-1, 1] != '\\' Dir.mkdir(path + 'responses/') unless File.directory?(path + 'responses/') Dir.foreach(path) do |filename| if (filename =~ /request_\d+.complete\z/) != nil begin socket = TCPSocket.open(url, port.to_i) ssl_context = OpenSSL::SSL::SSLContext.new ssl_context.ssl_version = :SSLv23 ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context) ssl_socket.sync_close = true ssl_socket.connect rescue => e raise "A connection couldn't be established. Are you sure you have the correct credentials? Exception: " + e. end File.foreach(path + filename) do |li| ssl_socket.puts li end File.rename(path + filename, path + filename + '.sent') File.open(path + 'responses/' + (filename + '.asc.received').gsub('request', 'response'), 'a+') do |fo| while line = ssl_socket.gets fo.puts(line) end end end end end |