Class: CnpOnline::CnpRequest
- Inherits:
-
Object
- Object
- CnpOnline::CnpRequest
- Includes:
- XML::Mapping
- Defined in:
- lib/XMLFields.rb,
lib/CnpRequest.rb
Instance Method Summary collapse
-
#add_rfr_request(options, path = (File.dirname(@path_to_batches))) ⇒ Object
Adds an RFRRequest to the CnpRequest.
-
#commit_batch(arg) ⇒ Object
Adds a batch to the CnpRequest.
-
#create_new_cnp_request(path) ⇒ Object
Creates the necessary files for the CnpRequest 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 CnpRequest xml doc with the appropos CnpRequest tags.
- #get_path_to_batches ⇒ Object
-
#get_responses_from_server(args = {}) ⇒ Object
Grabs response files over SFTP from Cnp.
-
#initialize(options = {}) ⇒ CnpRequest
constructor
A new instance of CnpRequest.
-
#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
DefaultCnpListener
)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_cnp(path = (File.dirname(@path_to_batches)), options = {}) ⇒ Object
- FTPs all previously unsent CnpRequests located in the folder denoted by path to the server Params:
path
-
A
String
containing the path to the folder on disc where CnpRequests are located.
- FTPs all previously unsent CnpRequests located in the folder denoted by path to the server Params:
-
#send_to_cnp_stream(options = {}, path = (File.dirname(@path_to_batches))) ⇒ Object
Sends all previously unsent CnpRequests in the specified directory to the Cnp server by use of fast batch.
Constructor Details
#initialize(options = {}) ⇒ CnpRequest
Returns a new instance of CnpRequest.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/CnpRequest.rb', line 55 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 = 500000 = # current time out set to 2 mins # this value is in seconds @RESPONSE_TIME_OUT = 5200 @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 CnpRequest. params:
options
-
a required
Hash
containing configuration info for the RFRRequest. If the RFRRequest is for a batch, then the
cnpSessionId 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 cnp request containing the RFRRequest at
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 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/CnpRequest.rb', line 185 def add_rfr_request(, path = (File.dirname(@path_to_batches))) rfrrequest = CnpRFRRequest.new if(['cnpSessionId']) then rfrrequest.cnpSessionId = ['cnpSessionId'] elsif(['merchantId'] and ['postDay']) then accountUpdate = AccountUpdateFileRequestData.new accountUpdate.merchantId = ['merchantId'] accountUpdate.postDay = ['postDay'] rfrrequest.accountUpdateFileRequestData = accountUpdate else raise ArgumentError, "For an RFR Request, you must specify either a cnpSessionId for an RFRRequest for batch or a merchantId and a postDay for an RFRRequest for account updater." end cnpRequest = CnpRequestForRFR.new cnpRequest.rfrRequest = rfrrequest authentication = Authentication.new authentication.user = get_config(:user, ) authentication.password = get_config(:password, ) cnpRequest.authentication = authentication cnpRequest.numBatchRequests = "0" cnpRequest.version = '12.8' cnpRequest.xmlns = "http://www.vantivcnp.com/schema" xml = cnpRequest.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 if(File.file?(path)) then raise RuntimeError, "Entered a file not a path." end if(path[-1,1] != '/' and path[-1,1] != '\\') then path = path + File::SEPARATOR end if !File.directory?(path) then Dir.mkdir(path) end path_to_request = path + REQUEST_FILE_PREFIX + ts File.open(path_to_request, 'a+') do |file| file.write xml end File.rename(path_to_request, path_to_request + COMPLETE_FILE_SUFFIX) @RESPONSE_TIME_OUT += 90 end |
#commit_batch(arg) ⇒ Object
Adds a batch to the CnpRequest. If the batch is open when passed, it will be closed prior to being added. Params:
arg
-
a
CnpBatchRequest
containing the transactions you wish to send or aString
specifying the
path to the batch file
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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/CnpRequest.rb', line 115 def commit_batch(arg) path_to_batch = "" #they passed a batch if arg.kind_of?(CnpBatchRequest) then path_to_batch = arg.get_batch_name if((au = arg.get_au_batch) != nil) then # also commit the account updater batch commit_batch(au) end elsif arg.kind_of?(CnpAUBatch) then path_to_batch = arg.get_batch_name elsif arg.kind_of?(String) then path_to_batch = arg else raise RuntimeError, "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 then if arg.kind_of?(String) then new_batch = CnpBatchRequest.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. if(new_batch.get_au_batch != nil) then File.remove(path_to_batch) path_to_batch = new_batch.get_au_batch.get_batch_name end elsif arg.kind_of?(CnpBatchRequest) then arg.close_batch() path_to_batch = arg.get_batch_name elsif arg.kind_of?(CnpAUBatch) then 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 cnp request would be too big, let's make another! if (@num_total_transactions + transactions_in_batch) > @MAX_NUM_TRANSACTIONS then finish_request initialize() create_new_cnp_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_cnp_request(path) ⇒ Object
Creates the necessary files for the CnpRequest 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
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/CnpRequest.rb', line 75 def create_new_cnp_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 if(File.file?(path)) then raise RuntimeError, "Entered a file not a path." end if(path[-1,1] != '/' and path[-1,1] != '\\') then path = path + File::SEPARATOR end if !File.directory?(path) then Dir.mkdir(path) end @path_to_request = path + REQUEST_FILE_PREFIX + ts @path_to_batches = @path_to_request + '_batches' if File.file?(@path_to_request) or File.file?(@path_to_batches) then create_new_cnp_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 CnpRequest xml doc with the appropos CnpRequest tags.
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
# File 'lib/CnpRequest.rb', line 444 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 '</cnpRequest>' end #rename the requests file File.rename(@path_to_request, @path_to_request + COMPLETE_FILE_SUFFIX) #we don't need the master batch file anymore File.delete(@path_to_batches) end |
#get_path_to_batches ⇒ Object
438 439 440 |
# File 'lib/CnpRequest.rb', line 438 def get_path_to_batches return @path_to_batches end |
#get_responses_from_server(args = {}) ⇒ Object
Grabs response files over SFTP from Cnp. 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 Cnp 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
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
# File 'lib/CnpRequest.rb', line 343 def get_responses_from_server(args = {}) use_encryption = get_config(SFTP_USE_ENCRYPTION_CONFIG_NAME, args) @responses_expected = args[:responses_expected] ||= @responses_expected response_path = args[:response_path] ||= (File.dirname(@path_to_batches) + '/' + RESPONSE_PATH_DIR) username = get_config(SFTP_USERNAME_CONFIG_NAME, args) password = get_config(SFTP_PASSWORD_CONFIG_NAME, args) url = get_config(SFTP_URL_CONFIG_NAME, args) if(username == nil or password == nil or url == nil) then raise ArgumentError, "You are not configured to use sFTP for batch processing. Please run /bin/Setup.rb again!" end response_path = prepare_for_sftp(response_path, use_encryption) if use_encryption response_path += ENCRYPTED_PATH_DIR end download_from_sftp(response_path, url, username, password) if use_encryption decrypt_response_files(response_path, args) 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 DefaultCnpListener
)
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
402 403 404 405 406 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 |
# File 'lib/CnpRequest.rb', line 402 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" and reader.get_attribute('response') != nil then raise RuntimeError, "Error parsing Cnp Request: " + reader.get_attribute("message") end reader.read count = 0 while true and count < 500001 do count += 1 if(reader.node == nil) then return false end case reader.node.name.to_s when "batchResponse" reader.read when "cnpResponse" 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 DefaultCnpListener
). 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)
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 |
# File 'lib/CnpRequest.rb', line 371 def process_responses(args) #the transaction listener is required if(!args.has_key?(:transaction_listener)) then 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) + '/' + RESPONSE_PATH_DIR) delete_batch_files = args[:deleteBatchFiles] ||= get_config(:deleteBatchFiles, args) #deleteBatchFiles = get_config(:deleteBatchFiles, args) Dir.foreach(path_to_responses) do |filename| if ((filename =~ /#{RESPONSE_FILE_PREFIX}\d+#{COMPLETE_FILE_SUFFIX}.asc#{RECEIVED_FILE_SUFFIX}\z/) != nil) then process_response(path_to_responses + filename, transaction_listener, batch_listener) File.rename(path_to_responses + filename, path_to_responses + filename + '.processed') end end if delete_batch_files delete_files_in_path(path_to_responses, /#{RESPONSE_FILE_PREFIX}\d+#{COMPLETE_FILE_SUFFIX}.asc#{RECEIVED_FILE_SUFFIX}.processed\z/) end end |
#send_to_cnp(path = (File.dirname(@path_to_batches)), options = {}) ⇒ Object
FTPs all previously unsent CnpRequests located in the folder denoted by path to the server Params:
path
-
A
String
containing the path to the folder on disc where CnpRequests are located.
This should be the same location where the CnpRequests 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.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/CnpRequest.rb', line 250 def send_to_cnp(path = (File.dirname(@path_to_batches)), = {}) use_encryption = get_config(SFTP_USE_ENCRYPTION_CONFIG_NAME, ) username = get_config(SFTP_USERNAME_CONFIG_NAME, ) password = get_config(SFTP_PASSWORD_CONFIG_NAME, ) delete_batch_files = get_config(SFTP_DELETE_BATCH_FILES_CONFIG_NAME, ) url = get_config(SFTP_URL_CONFIG_NAME, ) if(username == nil or password == nil or url == nil) then raise ArgumentError, "You are not configured to use sFTP for batch processing. Please run /bin/Setup.rb again!" end path = path_to_requests = prepare_for_sftp(path, use_encryption) if use_encryption encrypted_path = path_to_requests + ENCRYPTED_PATH_DIR encrypt_request_files(path, encrypted_path, ) path_to_requests = encrypted_path end @responses_expected = upload_to_sftp(path_to_requests, url, username, password, use_encryption) if delete_batch_files delete_files_in_path(path_to_requests, /#{REQUEST_FILE_PREFIX}\d+#{COMPLETE_FILE_SUFFIX}#{ENCRYPTED_FILE_SUFFIX}?#{SENT_FILE_SUFFIX}\z/) if use_encryption delete_files_in_path(path, /#{REQUEST_FILE_PREFIX}\d+#{COMPLETE_FILE_SUFFIX}#{SENT_FILE_SUFFIX}\z/) end end end |
#send_to_cnp_stream(options = {}, path = (File.dirname(@path_to_batches))) ⇒ Object
Sends all previously unsent CnpRequests in the specified directory to the Cnp 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!
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 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 |
# File 'lib/CnpRequest.rb', line 287 def send_to_cnp_stream( = {}, path = (File.dirname(@path_to_batches))) url = get_config(:fast_url, ) port = get_config(:fast_port, ) if(url == nil or url == "") then raise ArgumentError, "A URL for fastbatch was not specified in the config file or passed options. Reconfigure and try again." end if(port == "" or port == nil) then raise ArgumentError, "A port number for fastbatch was not specified in the config file or passed options. Reconfigure and try again." end if(path[-1,1] != '/' && path[-1,1] != '\\') then path = path + File::SEPARATOR end if (!File.directory?(path + RESPONSE_PATH_DIR)) then Dir.mkdir(path + RESPONSE_PATH_DIR) end Dir.foreach(path) do |filename| if((filename =~ /#{REQUEST_FILE_PREFIX}\d+#{COMPLETE_FILE_SUFFIX}\z/) != nil) then 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_SUFFIX) File.open(path + RESPONSE_PATH_DIR + (filename + '.asc' + RECEIVED_FILE_SUFFIX).gsub("request", "response"), 'a+') do |fo| while line = ssl_socket.gets fo.puts(line) end end end end end |