CnpOnline 8.31.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +139 -0
  3. data/CONTRIBUTORS +2 -0
  4. data/DESCRIPTION +5 -0
  5. data/LICENSE +22 -0
  6. data/README.md +72 -0
  7. data/Rakefile +89 -0
  8. data/Rakefile~ +89 -0
  9. data/SETUP.md +46 -0
  10. data/bin/Setup.rb +124 -0
  11. data/bin/sample_batch_driver.rb +123 -0
  12. data/bin/sample_driver.rb +49 -0
  13. data/lib/Communications.rb +85 -0
  14. data/lib/Configuration.rb +67 -0
  15. data/lib/EnvironmentVariables.rb +22 -0
  16. data/lib/LitleBatchRequest.rb +562 -0
  17. data/lib/LitleListeners.rb +142 -0
  18. data/lib/LitleOnline.rb +62 -0
  19. data/lib/LitleOnlineRequest.rb +297 -0
  20. data/lib/LitleRequest.rb +494 -0
  21. data/lib/LitleTransaction.rb +463 -0
  22. data/lib/LitleXmlMapper.rb +64 -0
  23. data/lib/XMLFields.rb +1869 -0
  24. data/lib/cacert.pem +3331 -0
  25. data/samples/Auth/LitleAuthReversalTransaction.rb +15 -0
  26. data/samples/Auth/LitleAuthorizationTransaction.rb +31 -0
  27. data/samples/Auth/LitlePaymentFullLifeCycle.rb +47 -0
  28. data/samples/Batch/AccountUpdate.rb +64 -0
  29. data/samples/Batch/SampleBatchDriver.rb +94 -0
  30. data/samples/Capture/LitleCaptureGivenAuthTransaction.rb +30 -0
  31. data/samples/Capture/LitleCaptureTransaction.rb +14 -0
  32. data/samples/Capture/LitleForceCaptureTransaction.rb +26 -0
  33. data/samples/Capture/LitlePartialCapture.rb +16 -0
  34. data/samples/Credit/LitleCreditTransaction.rb +16 -0
  35. data/samples/Credit/LitleRefundTransaction.rb +29 -0
  36. data/samples/Other/LitleAvsTransaction.rb +34 -0
  37. data/samples/Other/LitleVoidTransaction.rb +18 -0
  38. data/samples/Paypage/FullPaypageLifeCycle.rb +74 -0
  39. data/samples/Run_all.rb +17 -0
  40. data/samples/Sale/LitleSaleTransaction.rb +29 -0
  41. data/samples/Sale/SampleSaleTransaction.rb +24 -0
  42. data/test/certification/certTest1_base.rb +945 -0
  43. data/test/certification/certTest2_authenhanced.rb +573 -0
  44. data/test/certification/certTest3_authreversal.rb +185 -0
  45. data/test/certification/certTest4_echeck.rb +278 -0
  46. data/test/certification/certTest5_token.rb +204 -0
  47. data/test/certification/certTest_batchAll.rb +337 -0
  48. data/test/certification/ts_all.rb +33 -0
  49. data/test/functional/test_activate.rb +100 -0
  50. data/test/functional/test_activateReversal.rb +56 -0
  51. data/test/functional/test_auth.rb +298 -0
  52. data/test/functional/test_authReversal.rb +69 -0
  53. data/test/functional/test_balanceInquiry.rb +80 -0
  54. data/test/functional/test_batch.rb +164 -0
  55. data/test/functional/test_batchStream.rb +145 -0
  56. data/test/functional/test_cancelSubscription.rb +55 -0
  57. data/test/functional/test_capture.rb +84 -0
  58. data/test/functional/test_captureGivenAuth.rb +235 -0
  59. data/test/functional/test_configuration.rb +89 -0
  60. data/test/functional/test_createPlan.rb +85 -0
  61. data/test/functional/test_credit.rb +174 -0
  62. data/test/functional/test_deactivate.rb +80 -0
  63. data/test/functional/test_deactivateReversal.rb +56 -0
  64. data/test/functional/test_depositReversal.rb +56 -0
  65. data/test/functional/test_echeckCredit.rb +134 -0
  66. data/test/functional/test_echeckRedeposit.rb +88 -0
  67. data/test/functional/test_echeckSale.rb +177 -0
  68. data/test/functional/test_echeckVerification.rb +127 -0
  69. data/test/functional/test_echeckVoid.rb +41 -0
  70. data/test/functional/test_forceCapture.rb +183 -0
  71. data/test/functional/test_litle_requests.rb +356 -0
  72. data/test/functional/test_load.rb +82 -0
  73. data/test/functional/test_loadReversal.rb +56 -0
  74. data/test/functional/test_override.rb +64 -0
  75. data/test/functional/test_refundReversal.rb +56 -0
  76. data/test/functional/test_sale.rb +259 -0
  77. data/test/functional/test_token.rb +115 -0
  78. data/test/functional/test_unload.rb +82 -0
  79. data/test/functional/test_unloadReversal.rb +56 -0
  80. data/test/functional/test_updateCardValidationNumOnToken.rb +43 -0
  81. data/test/functional/test_updatePlan.rb +58 -0
  82. data/test/functional/test_updateSubscription.rb +76 -0
  83. data/test/functional/test_xmlfields.rb +427 -0
  84. data/test/functional/ts_all.rb +66 -0
  85. data/test/unit/test_LitleAUBatch.rb +216 -0
  86. data/test/unit/test_LitleBatchRequest.rb +643 -0
  87. data/test/unit/test_LitleOnlineRequest.rb +295 -0
  88. data/test/unit/test_LitleRequest.rb +316 -0
  89. data/test/unit/test_LitleTransaction.rb +397 -0
  90. data/test/unit/test_activate.rb +92 -0
  91. data/test/unit/test_activateReversal.rb +44 -0
  92. data/test/unit/test_auth.rb +421 -0
  93. data/test/unit/test_authReversal.rb +82 -0
  94. data/test/unit/test_balanceInquiry.rb +52 -0
  95. data/test/unit/test_cancelSubscription.rb +43 -0
  96. data/test/unit/test_capture.rb +73 -0
  97. data/test/unit/test_captureGivenAuth.rb +188 -0
  98. data/test/unit/test_createPlan.rb +52 -0
  99. data/test/unit/test_credit.rb +342 -0
  100. data/test/unit/test_deactivate.rb +52 -0
  101. data/test/unit/test_deactivateReversal.rb +44 -0
  102. data/test/unit/test_depositReversal.rb +44 -0
  103. data/test/unit/test_echeckCredit.rb +71 -0
  104. data/test/unit/test_echeckRedeposit.rb +94 -0
  105. data/test/unit/test_echeckSale.rb +71 -0
  106. data/test/unit/test_echeckVerification.rb +71 -0
  107. data/test/unit/test_echeckVoid.rb +54 -0
  108. data/test/unit/test_forceCapture.rb +145 -0
  109. data/test/unit/test_load.rb +53 -0
  110. data/test/unit/test_loadReversal.rb +44 -0
  111. data/test/unit/test_refundReversal.rb +44 -0
  112. data/test/unit/test_sale.rb +465 -0
  113. data/test/unit/test_token.rb +144 -0
  114. data/test/unit/test_unload.rb +53 -0
  115. data/test/unit/test_unloadReversal.rb +44 -0
  116. data/test/unit/test_updateCardValidationNumOnToken.rb +80 -0
  117. data/test/unit/test_updatePlan.rb +45 -0
  118. data/test/unit/test_updateSubscription.rb +172 -0
  119. data/test/unit/test_xmlfields.rb +2930 -0
  120. data/test/unit/ts_unit.rb +65 -0
  121. metadata +255 -0
@@ -0,0 +1,494 @@
1
+ # Copyright (c) 2011 Litle & Co.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person
4
+ # obtaining a copy of this software and associated documentation
5
+ # files (the "Software"), to deal in the Software without
6
+ # restriction, including without limitation the rights to use,
7
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the
9
+ # Software is furnished to do so, subject to the following
10
+ # conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ # OTHER DEALINGS IN THE SOFTWARE.
23
+ require_relative 'Configuration'
24
+ require 'net/sftp'
25
+ require 'libxml'
26
+ require 'crack/xml'
27
+ require 'socket'
28
+
29
+ include Socket::Constants
30
+ #
31
+ # This class handles sending the Litle Request (which is actually a series of batches!)
32
+ #
33
+
34
+ module LitleOnline
35
+ class LitleRequest
36
+ include XML::Mapping
37
+ def initialize(options = {})
38
+ # load configuration data
39
+ @config_hash = Configuration.new.config
40
+ @num_batch_requests = 0
41
+ @path_to_request = ''
42
+ @path_to_batches = ''
43
+ @num_total_transactions = 0
44
+ @MAX_NUM_TRANSACTIONS = 500_000
45
+ @options = options
46
+ # current time out set to 2 mins
47
+ # this value is in seconds
48
+ @RESPONSE_TIME_OUT = 360
49
+ @POLL_DELAY = 0
50
+ @responses_expected = 0
51
+ end
52
+
53
+ # Creates the necessary files for the LitleRequest at the path specified. path/request_(TIMESTAMP) will be
54
+ # the final XML markup and path/request_(TIMESTAMP) will hold intermediary XML markup
55
+ # Params:
56
+ # +path+:: A +String+ containing the path to the folder on disc to write the files to
57
+ def create_new_litle_request(path)
58
+ ts = Time.now.to_i.to_s
59
+ begin
60
+ ts += Time.now.nsec.to_s
61
+ rescue NoMethodError # ruby 1.8.7 fix
62
+ ts += Time.now.usec.to_s
63
+ end
64
+
65
+ raise 'Entered a file not a path.' if File.file?(path)
66
+
67
+ path += File::SEPARATOR if path[-1, 1] != '/' && path[-1, 1] != '\\'
68
+
69
+ Dir.mkdir(path) unless File.directory?(path)
70
+
71
+ @path_to_request = path + 'request_' + ts
72
+ @path_to_batches = @path_to_request + '_batches'
73
+
74
+ if File.file?(@path_to_request) || File.file?(@path_to_batches)
75
+ create_new_litle_request(path)
76
+ return
77
+ end
78
+
79
+ File.open(@path_to_request, 'a+') do |file|
80
+ file.write('')
81
+ end
82
+ File.open(@path_to_batches, 'a+') do |file|
83
+ file.write('')
84
+ end
85
+ end
86
+
87
+ # Adds a batch to the LitleRequest. If the batch is open when passed, it will be closed prior to being added.
88
+ # Params:
89
+ # +arg+:: a +LitleBatchRequest+ containing the transactions you wish to send or a +String+ specifying the
90
+ # path to the batch file
91
+ def commit_batch(arg)
92
+ path_to_batch = ''
93
+ # they passed a batch
94
+ if arg.is_a?(LitleBatchRequest)
95
+ path_to_batch = arg.get_batch_name
96
+ if (au = arg.get_au_batch) != nil
97
+ # also commit the account updater batch
98
+ commit_batch(au)
99
+ end
100
+ elsif arg.is_a?(LitleAUBatch)
101
+ path_to_batch = arg.get_batch_name
102
+ elsif arg.is_a?(String)
103
+ path_to_batch = arg
104
+ else
105
+ raise 'You entered neither a path nor a batch. Game over :('
106
+ end
107
+ # the batch isn't closed. let's help a brother out
108
+ if (ind = path_to_batch.index(/\.closed/)).nil?
109
+ if arg.is_a?(String)
110
+ new_batch = LitleBatchRequest.new
111
+ new_batch.open_existing_batch(path_to_batch)
112
+ new_batch.close_batch
113
+ path_to_batch = new_batch.get_batch_name
114
+ # if we passed a path to an AU batch, then new_batch will be a new, empty batch and the batch we passed
115
+ # will be in the AU batch variable. thus, we wanna grab that file name and remove the empty batch.
116
+ unless new_batch.get_au_batch.nil?
117
+ File.remove(path_to_batch)
118
+ path_to_batch = new_batch.get_au_batch.get_batch_name
119
+ end
120
+ elsif arg.is_a?(LitleBatchRequest)
121
+ arg.close_batch
122
+ path_to_batch = arg.get_batch_name
123
+ elsif arg.is_a?(LitleAUBatch)
124
+ arg.close_batch
125
+ path_to_batch = arg.get_batch_name
126
+ end
127
+ ind = path_to_batch.index(/\.closed/)
128
+ end
129
+ transactions_in_batch = path_to_batch[ind + 8..path_to_batch.length].to_i
130
+
131
+ # if the litle request would be too big, let's make another!
132
+ if (@num_total_transactions + transactions_in_batch) > @MAX_NUM_TRANSACTIONS
133
+ finish_request
134
+ initialize(@options)
135
+ create_new_litle_request
136
+ else # otherwise, let's add it line by line to the request doc
137
+ # @num_batch_requests += 1
138
+ # how long we wnat to wait around for the FTP server to get us a response
139
+ @RESPONSE_TIME_OUT += 90 + (transactions_in_batch * 0.25)
140
+ # don't start looking until there could possibly be a response
141
+ @POLL_DELAY += 30 + (transactions_in_batch * 0.02)
142
+ @num_total_transactions += transactions_in_batch
143
+ # Don't add empty batches
144
+ @num_batch_requests += 1 unless transactions_in_batch.eql?(0)
145
+ File.open(@path_to_batches, 'a+') do |fo|
146
+ File.foreach(path_to_batch) do |li|
147
+ fo.puts li
148
+ end
149
+ end
150
+
151
+ File.delete(path_to_batch)
152
+ end
153
+ end
154
+
155
+ # Adds an RFRRequest to the LitleRequest.
156
+ # params:
157
+ # +options+:: a required +Hash+ containing configuration info for the RFRRequest. If the RFRRequest is for a batch, then the
158
+ # litleSessionId is required as a key/val pair. If the RFRRequest is for account updater, then merchantId and postDay are required
159
+ # as key/val pairs.
160
+ # +path+:: optional path to save the new litle request containing the RFRRequest at
161
+ def add_rfr_request(options, path = File.dirname(@path_to_batches))
162
+ rfrrequest = LitleRFRRequest.new
163
+ if options['litleSessionId']
164
+ rfrrequest.litleSessionId = options['litleSessionId']
165
+ elsif options['merchantId'] && options['postDay']
166
+ accountUpdate = AccountUpdateFileRequestData.new
167
+ accountUpdate.merchantId = options['merchantId']
168
+ accountUpdate.postDay = options['postDay']
169
+ rfrrequest.accountUpdateFileRequestData = accountUpdate
170
+ else
171
+ raise ArgumentError, "For an RFR Request, you must specify either a litleSessionId for an RFRRequest for batch or a merchantId
172
+ and a postDay for an RFRRequest for account updater."
173
+ end
174
+
175
+ litleRequest = LitleRequestForRFR.new
176
+ litleRequest.rfrRequest = rfrrequest
177
+
178
+ authentication = Authentication.new
179
+ authentication.user = get_config(:user, options)
180
+ authentication.password = get_config(:password, options)
181
+
182
+ litleRequest.authentication = authentication
183
+ litleRequest.numBatchRequests = '0'
184
+
185
+ litleRequest.version = '8.31'
186
+ litleRequest.xmlns = 'http://www.litle.com/schema'
187
+
188
+ xml = litleRequest.save_to_xml.to_s
189
+
190
+ ts = Time.now.to_i.to_s
191
+ begin
192
+ ts += Time.now.nsec.to_s
193
+ rescue NoMethodError # ruby 1.8.7 fix
194
+ ts += Time.now.usec.to_s
195
+ end
196
+ raise 'Entered a file not a path.' if File.file?(path)
197
+
198
+ path += File::SEPARATOR if path[-1, 1] != '/' && path[-1, 1] != '\\'
199
+
200
+ Dir.mkdir(path) unless File.directory?(path)
201
+
202
+ path_to_request = path + 'request_' + ts
203
+
204
+ File.open(path_to_request, 'a+') do |file|
205
+ file.write xml
206
+ end
207
+ File.rename(path_to_request, path_to_request + '.complete')
208
+ @RESPONSE_TIME_OUT += 90
209
+ end
210
+
211
+ # FTPs all previously unsent LitleRequests located in the folder denoted by path to the server
212
+ # Params:
213
+ # +path+:: A +String+ containing the path to the folder on disc where LitleRequests are located.
214
+ # This should be the same location where the LitleRequests were written to. If no path is explicitly
215
+ # provided, then we use the directory where the current working batches file is stored.
216
+ # +options+:: An (option) +Hash+ containing the username, password, and URL to attempt to sFTP to.
217
+ # If not provided, the values will be populated from the configuration file.
218
+ def send_to_litle(path = File.dirname(@path_to_batches), options = {})
219
+ username = get_config(:sftp_username, options)
220
+ password = get_config(:sftp_password, options)
221
+
222
+ url = get_config(:sftp_url, options)
223
+
224
+ if username.nil? || password.nil? || url.nil?
225
+ raise ArgumentError, 'You are not configured to use sFTP for batch processing. Please run /bin/Setup.rb again!'
226
+ end
227
+
228
+ path += File::SEPARATOR if path[-1, 1] != '/' && path[-1, 1] != '\\'
229
+
230
+ begin
231
+ Net::SFTP.start(url, username, password: password) do |sftp|
232
+ # our folder is /SHORTNAME/SHORTNAME/INBOUND
233
+ Dir.foreach(path) do |filename|
234
+ # we have a complete report according to filename regex
235
+ if (filename =~ /request_\d+.complete\z/) != nil
236
+ # adding .prg extension per the XML
237
+ File.rename(path + filename, path + filename + '.prg')
238
+ end
239
+ end
240
+
241
+ @responses_expected = 0
242
+ Dir.foreach(path) do |filename|
243
+ if (filename =~ /request_\d+.complete.prg\z/) != nil
244
+ # upload the file
245
+ sftp.upload!(path + filename, '/inbound/' + filename)
246
+ @responses_expected += 1
247
+ # rename now that we're done
248
+ sftp.rename!('/inbound/' + filename, '/inbound/' + filename.gsub('prg', 'asc'))
249
+ File.rename(path + filename, path + filename.gsub('prg', 'sent'))
250
+ end
251
+ end
252
+ end
253
+ rescue Net::SSH::AuthenticationFailed
254
+ raise ArgumentError, 'The sFTP credentials provided were incorrect. Try again!'
255
+ end
256
+ end
257
+
258
+ # Sends all previously unsent LitleRequests in the specified directory to the Litle server
259
+ # by use of fast batch. All results will be written to disk as we get them. Note that use
260
+ # of fastbatch is strongly discouraged!
261
+ def send_to_litle_stream(options = {}, path = File.dirname(@path_to_batches))
262
+ url = get_config(:fast_url, options)
263
+ port = get_config(:fast_port, options)
264
+
265
+ if url.nil? || url == ''
266
+ raise ArgumentError, 'A URL for fastbatch was not specified in the config file or passed options. Reconfigure and try again.'
267
+ end
268
+
269
+ if port == '' || port.nil?
270
+ raise ArgumentError, 'A port number for fastbatch was not specified in the config file or passed options. Reconfigure and try again.'
271
+ end
272
+
273
+ path += File::SEPARATOR if path[-1, 1] != '/' && path[-1, 1] != '\\'
274
+
275
+ Dir.mkdir(path + 'responses/') unless File.directory?(path + 'responses/')
276
+
277
+ Dir.foreach(path) do |filename|
278
+ if (filename =~ /request_\d+.complete\z/) != nil
279
+ begin
280
+ socket = TCPSocket.open(url, port.to_i)
281
+ ssl_context = OpenSSL::SSL::SSLContext.new
282
+ ssl_context.ssl_version = :SSLv23
283
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
284
+ ssl_socket.sync_close = true
285
+ ssl_socket.connect
286
+ rescue => e
287
+ raise "A connection couldn't be established. Are you sure you have the correct credentials? Exception: " + e.message
288
+ end
289
+
290
+ File.foreach(path + filename) do |li|
291
+ ssl_socket.puts li
292
+ end
293
+ File.rename(path + filename, path + filename + '.sent')
294
+ File.open(path + 'responses/' + (filename + '.asc.received').gsub('request', 'response'), 'a+') do |fo|
295
+ while line = ssl_socket.gets
296
+ fo.puts(line)
297
+ end
298
+ end
299
+
300
+ end
301
+ end
302
+ end
303
+
304
+ # Grabs response files over SFTP from Litle.
305
+ # Params:
306
+ # +args+:: An (optional) +Hash+ containing values for the number of responses expected, the
307
+ # path to the folder on disk to write the responses from the Litle server to, the username and
308
+ # password with which to connect ot the sFTP server, and the URL to connect over sFTP. Values not
309
+ # provided in the hash will be populate automatically based on our best guess
310
+ def get_responses_from_server(args = {})
311
+ @responses_expected = args[:responses_expected] ||= @responses_expected
312
+ response_path = args[:response_path] ||= (File.dirname(@path_to_batches) + '/responses/')
313
+ username = get_config(:sftp_username, args)
314
+ password = get_config(:sftp_password, args)
315
+ url = get_config(:sftp_url, args)
316
+
317
+ if username.nil? || password.nil? || url.nil?
318
+ raise ConfigurationException, 'You are not configured to use sFTP for batch processing. Please run /bin/Setup.rb again!'
319
+ end
320
+
321
+ if response_path[-1, 1] != '/' && response_path[-1, 1] != '\\'
322
+ response_path += File::SEPARATOR
323
+ end
324
+
325
+ Dir.mkdir(response_path) unless File.directory?(response_path)
326
+ begin
327
+ responses_grabbed = 0
328
+ Net::SFTP.start(url, username, password: password) do |sftp|
329
+ # clear out the sFTP outbound dir prior to checking for new files, avoids leaving files on the server
330
+ # if files are left behind we are not counting then towards the expected total
331
+ sftp.dir.foreach('/outbound/') do |entry|
332
+ if (entry.name =~ /request_\d+.complete.asc\z/) != nil
333
+ sftp.download!('/outbound/' + entry.name, response_path + entry.name.gsub('request', 'response') + '.received')
334
+ 3.times do
335
+ begin
336
+ sftp.remove!('/outbound/' + entry.name)
337
+ break
338
+ rescue Net::SFTP::StatusException
339
+ # try, try, try again
340
+ puts "We couldn't remove it! Try again"
341
+ end
342
+ end
343
+ end
344
+ end
345
+ end
346
+ # wait until a response has a possibility of being there
347
+ sleep(@POLL_DELAY)
348
+ time_begin = Time.now
349
+ Net::SFTP.start(url, username, password: password) do |sftp|
350
+ while (Time.now - time_begin) < @RESPONSE_TIME_OUT && responses_grabbed < @responses_expected
351
+ # sleep for 60 seconds, ¿no es bueno?
352
+ sleep(60)
353
+ sftp.dir.foreach('/outbound/') do |entry|
354
+ if (entry.name =~ /request_\d+.complete.asc\z/) != nil
355
+ sftp.download!('/outbound/' + entry.name, response_path + entry.name.gsub('request', 'response') + '.received')
356
+ responses_grabbed += 1
357
+ 3.times do
358
+ begin
359
+ sftp.remove!('/outbound/' + entry.name)
360
+ break
361
+ rescue Net::SFTP::StatusException
362
+ # try, try, try again
363
+ puts "We couldn't remove it! Try again"
364
+ end
365
+ end
366
+ end
367
+ end
368
+ end
369
+ # if our timeout timed out, we're having problems
370
+ if responses_grabbed < @responses_expected
371
+ raise 'We timed out in waiting for a response from the server. :('
372
+ end
373
+ end
374
+ rescue Net::SSH::AuthenticationFailed
375
+ raise ArgumentError, 'The sFTP credentials provided were incorrect. Try again!'
376
+ end
377
+ end
378
+
379
+ # Params:
380
+ # +args+:: A +Hash+ containing arguments for the processing process. This hash MUST contain an entry
381
+ # for a transaction listener (see +DefaultLitleListener+). It may also include a batch listener and a
382
+ # custom path where response files from the server are located (if it is not provided, we'll guess the position)
383
+ def process_responses(args)
384
+ # the transaction listener is required
385
+ unless args.key?(:transaction_listener)
386
+ raise ArgumentError, 'The arguments hash must contain an entry for transaction listener!'
387
+ end
388
+
389
+ transaction_listener = args[:transaction_listener]
390
+ batch_listener = args[:batch_listener] ||= nil
391
+ path_to_responses = args[:path_to_responses] ||= (File.dirname(@path_to_batches) + '/responses/')
392
+
393
+ Dir.foreach(path_to_responses) do |filename|
394
+ if (filename =~ /response_\d+.complete.asc.received\z/) != nil
395
+ process_response(path_to_responses + filename, transaction_listener, batch_listener)
396
+ File.rename(path_to_responses + filename, path_to_responses + filename + '.processed')
397
+ end
398
+ end
399
+ end
400
+
401
+ # Params:
402
+ # +path_to_response+:: The path to a specific .asc file to process
403
+ # +transaction_listener+:: A listener to be applied to the hash of each transaction
404
+ # (see +DefaultLitleListener+)
405
+ # +batch_listener+:: An (optional) listener to be applied to the hash of each batch.
406
+ # Note that this will om-nom-nom quite a bit of memory
407
+ def process_response(path_to_response, transaction_listener, _batch_listener = nil)
408
+ reader = LibXML::XML::Reader.file(path_to_response)
409
+ reader.read # read into the root node
410
+ # if the response attribute is nil, we're dealing with an RFR and everything is a-okay
411
+ if reader.get_attribute('response') != '0' && !reader.get_attribute('response').nil?
412
+ raise 'Error parsing Litle Request: ' + reader.get_attribute('message')
413
+ end
414
+
415
+ reader.read
416
+ count = 0
417
+ while true && count < 500_001
418
+
419
+ count += 1
420
+ return false if reader.node.nil?
421
+
422
+ case reader.node.name.to_s
423
+ when 'batchResponse'
424
+ reader.read
425
+ when 'litleResponse'
426
+ return false
427
+ when 'text'
428
+ reader.read
429
+ else
430
+ xml = reader.read_outer_xml
431
+ duck = Crack::XML.parse(xml)
432
+ duck[duck.keys[0]]['type'] = duck.keys[0]
433
+ duck = duck[duck.keys[0]]
434
+ transaction_listener.apply(duck)
435
+ reader.next
436
+ end
437
+ end
438
+ end
439
+
440
+ def get_path_to_batches
441
+ @path_to_batches
442
+ end
443
+
444
+ # Called when you wish to finish adding batches to your request, this method rewrites the aggregate
445
+ # batch file to the final LitleRequest xml doc with the appropos LitleRequest tags.
446
+ def finish_request
447
+ File.open(@path_to_request, 'w') do |f|
448
+ # jam dat header in there
449
+ f.puts(build_request_header)
450
+ # read into the request file from the batches file
451
+ File.foreach(@path_to_batches) do |li|
452
+ f.puts li
453
+ end
454
+ # finally, let's poot in a header, for old time's sake
455
+ f.puts '</litleRequest>'
456
+ end
457
+
458
+ # rename the requests file
459
+ File.rename(@path_to_request, @path_to_request + '.complete')
460
+ # we don't need the master batch file anymore
461
+ File.delete(@path_to_batches)
462
+ end
463
+
464
+ private
465
+
466
+ def build_request_header(options = @options)
467
+ litle_request = self
468
+
469
+ authentication = Authentication.new
470
+ authentication.user = get_config(:user, options)
471
+ authentication.password = get_config(:password, options)
472
+
473
+ litle_request.authentication = authentication
474
+ litle_request.version = '8.31'
475
+ litle_request.xmlns = 'http://www.litle.com/schema'
476
+ # litle_request.id = options['sessionId'] #grab from options; okay if nil
477
+ litle_request.numBatchRequests = @num_batch_requests
478
+
479
+ xml = litle_request.save_to_xml.to_s
480
+ xml[/<\/litleRequest>/] = ''
481
+ xml
482
+ end
483
+
484
+ def get_config(field, options)
485
+ if options[field.to_s].nil? && options[field].nil?
486
+ @config_hash[field.to_s]
487
+ elsif !options[field.to_s].nil?
488
+ options[field.to_s]
489
+ else
490
+ options[field]
491
+ end
492
+ end
493
+ end
494
+ end