UNPKG

ndc-parser

Version:
282 lines (260 loc) 8.55 kB
function Parser(){ /** * [getIncomingMessageLength checks first two bytes of incoming message to get the length] * @param {[type]} message [message to be checked] * @return {[type]} [length of the message as decimal number] */ this.getIncomingMessageLength = function(message){ let len = parseInt(message.charCodeAt(0), 10) * 256 + parseInt(message.charCodeAt(1), 10); if(!isNaN(len)) return len; else return 0; }; /** * [parse parse the message from host] * @param {[type]} message [actual message without header length] * @param {[type]} length [length of the message to parse] * @return {[type]} [parsed object] */ this.parse = function (message, length){ let splitted = message.split('\x1c'); switch(splitted[0][0]){ case '1': return this.parseTerminalCommand(splitted); case '3': return this.parseDataCommand(splitted); case '4': return this.parseTransactionReply(splitted); case '8': return this.parseEMVConfiguration(splitted); default: //console.log('Unhandled message class' + splitted[0][0]); break; } }; /** * [parseTerminalCommand parse host command message class 1] * @param {[type]} splitted [description] * @return {[type]} [description] */ this.parseTerminalCommand = function(splitted){ let parsed = {}; parsed.message_class = 'Terminal Command'; parsed.LUNO = splitted[1]; parsed.message_sequence_number = splitted[2]; switch(splitted[3][0]){ case '1': parsed.command_code = 'Go in-service'; break; case '2': parsed.command_code = 'Go out-of-service'; break; case '3': parsed.command_code = 'Send Configuration ID'; break; case '4': parsed.command_code = 'Send Supply Counters'; break; case '7': parsed.command_code = 'Send Configuration Information'; break; default: parsed.command_code = splitted[3][0]; break; } return parsed; }; this.parseEMVConfiguration = function(splitted){ let parsed = {}; parsed.message_class = 'EMV Configuration'; parsed.LUNO = splitted[1]; parsed.message_sequence_number = splitted[2]; switch(splitted[3]){ case '1': parsed.message_subclass = 'ICC Currency Data Objects table'; break; case '2': parsed.message_subclass = 'ICC Transaction Data Objects table'; break; case '3': parsed.message_subclass = 'ICC Language Support table'; break; case '4': parsed.message_subclass = 'ICC Terminal Data Objects table'; break; case '5': parsed.message_subclass = 'ICC Terminal Acceptable AIDs table'; break; } return parsed; }; /** * [parseDataCommand description] * @param {[type]} splitted [description] * @return {[type]} [description] */ this.parseDataCommand = function(splitted){ let parsed = {}; parsed.message_class = 'Data Command'; parsed.LUNO = splitted[1]; parsed.message_sequence_number = splitted[2]; switch(splitted[3][0]){ case '1': parsed.message_subclass = 'Customization Command'; switch(splitted[3][1]){ case '1': parsed.message_identifier = 'Screen Data load'; parsed.screens = []; for(let i = 4; i < splitted.length; i++){ parsed.screens.push(splitted[i]); } break; case '2': parsed.message_identifier = 'State Tables load'; parsed.states = []; for(let i = 4; i < splitted.length; i++){ parsed.states.push(splitted[i]); } break; case '5': parsed.message_identifier = 'FIT Data load'; parsed.FITs = []; for(let i = 4; i < splitted.length; i++){ parsed.FITs.push(splitted[i]); } break; case '6': parsed.message_identifier = 'Configuration ID number load'; parsed.config_id = splitted[4]; break; default: break; } break; case '2': parsed.message_subclass = 'Interactive Transaction Response'; parsed.display_flag = splitted[3][1]; parsed.active_keys = splitted[3].substr(2); parsed.screen_timer_field = splitted[4]; parsed.screen_data_field = splitted[5]; break; case '4': parsed.message_subclass = 'Extended Encryption Key Information'; switch(splitted[3][1]){ case '2': parsed.modifier = 'Decipher new comms key with current master key'; parsed.new_key_length = splitted[4].substr(0, 3); parsed.new_key_data = splitted[4].substr(3); break; } break; } return parsed; }; /** * [parseTransactionReply parse host command message class 4] * @param {[type]} splitted [description] * @return {[type]} [description] */ this.parseTransactionReply = function(splitted){ let parsed = {}; parsed.message_class = 'Transaction Reply Command'; parsed.LUNO = splitted[1]; parsed.message_sequence_number = splitted[2]; parsed.next_state = splitted[3]; parsed.notes_to_dispense = splitted[4]; if(splitted[5]){ parsed.transaction_serial_number = splitted[5].substring(0, 4); parsed.function_identifier = this.getFunctionIdentifierDescription(splitted[5].substring(4, 5)); parsed.screen_number = splitted[5].substring(5, 8); if(splitted[5].substring(8)) parsed.screen_display_update = splitted[5].substring(8); } if(splitted[6]){ parsed.message_coordination_number = splitted[6].substring(0, 1); parsed.card_return_flag = this.getCardReturnFlagDescription(splitted[6].substring(1, 2)); // q and r sub fields splitted[6].substring(2).split('\x1d').forEach( (element, i) => { switch(element[0]){ case '1': // Print on journal printer only parsed.journal_printer_data = element.substr(1); break; case '2': // Print on receipt printer only parsed.receipt_printer_data = element.substr(1); break; case '3': // Print on receipt and journal printer parsed.receipt_printer_data = parsed.journal_printer_data = element.substr(1); break; } }); } return parsed; }; /** * [getFunctionIdentifierDescription description] * @param {[type]} function_identifier [description] * @return {[type]} [description] */ this.getFunctionIdentifierDescription = function(function_identifier){ switch(function_identifier){ case '1': case '7': return {[function_identifier]: 'Deposit and print'}; case '2': case '8': return {[function_identifier]: 'Dispense and print'}; case '3': case '9': return {[function_identifier]: 'Display and print'}; case '4': return {[function_identifier]: 'Print immediate'}; case '5': return {[function_identifier]: 'Set next state and print'}; case 'A': return {[function_identifier]: 'Eject card and dispense and print (card before cash)'}; case 'B': case 'C': return {[function_identifier]: 'Parallel dispense and print and card eject'}; } }; /** * [getCardReturnFlagDescription description] * @param {[type]} card_return_flag [description] * @return {[type]} [description] */ this.getCardReturnFlagDescription = function(card_return_flag){ switch(card_return_flag){ case '0': return {'0': 'Return card during the Close state'}; case '1': return {'1': 'Retain card during the Close state'}; case '4': return {'4': 'Return card while processing the transaction reply'}; } }; /** * [getPrinterFlagDescription description] * @param {[type]} printer_flag [description] * @return {[type]} [description] */ this.getPrinterFlagDescription = function(printer_flag){ switch(printer_flag){ case '0': return {'0': 'Do not print'}; case '1': return {'1': 'Print on journal printer only'}; case '2': return {'2': 'Print on receipt printer only'}; case '3': return {'3': 'Print on receipt and journal printer'}; } }; } Parser.prototype.parseHostMessage = function(message){ return this.parse(message.toString('utf8').substring(2), this.getIncomingMessageLength(message.toString('utf8'))); }; module.exports = Parser;