var fs = require('fs'); var https = require('https'); var http = require('http'); var url = require('url'); var request = require('request'); var xml2js = require('xml2js').parseString; var strftime = require('strftime'); var redis = require('redis'); var Router = require('node-simple-router'); var winston = require('winston'); var logger; var config; var httpServer; var redisClient; process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; var aaa; var logTag = __filename.split('/').reverse()[0]; function getRedisKey(timestamp) { var prefix = config.globals.gateway_name; if (config.globals.redis_prefix) { prefix = config.globals.redis_prefix; } return prefix + '.ts:' + timestamp + '.rid'; } function generateTimestamp(request_id) { var ts = strftime('%F %T', new Date()); var key = getRedisKey(ts); redisClient.set(key, request_id); redisClient.expire(key, 3600*48); return ts; } function topupRequest(task) { var ts = strftime('%F %T', new Date()); ts = generateTimestamp(task['requestId']); var options = { url: config.h2h_out.partner, qs: { code: task['remoteProduct'], msisdn: task['destination'], user_id: config.h2h_out.userid, password: config.h2h_out.password, ts: ts } }; logger.info('Creating http request to gateway', {options: options}); if (aaa) { aaa.incrementTrxCount(); aaa.incrementActiveTrxCount(); } request(options, function (error, response, body) { aaa.decrementActiveTrxCount(); if (error) { var error_message = 'Error on http connection to gateway: ' + error; logger.warn(error_message); callbackReport(task['requestId'], '91', error_message); return; } if (response.statusCode != 200) { var error_message = 'Gateway error, http response code: ' + response.statusCode; logger.warn(error_message); callbackReport(task['requestId'], '91', error_message); return; } var responseCode = 40; var responseMessage; xml2js(body, function (err, result) { if (err) { logger.warn('Error parsing XML', {response_error: err, response_body: body}); callbackReport(task['requestId'], '40', body); return; } logger.info('Got direct response from request', {result: result}); try { responseMessage = result.direct_ack.info[0]; if (result.direct_ack.request_status[0] == 'OK') { responseCode = 68; } else { responseCode = 40; var new_response_code = responseCodeFromMessage(responseMessage); if (new_response_code) { responseCode = new_response_code; } } } catch(err) { logger.warn('Exception on parsing xml response'); responseCode = 40; responseMessage = 'Invalid xml response from gateway'; } callbackReport(task['requestId'], responseCode, responseMessage); }); }); } function createRedisClient() { redisClient = redis.createClient(config.globals.redis_port, config.globals.redis_host); } function paddingSN(sn, _config) { if (!_config) { _config = config; } if (_config.h2h_out.sn_min_length && (sn.length < Number(_config.h2h_out.sn_min_length))) { sn = '0000000000000000' + sn; sn = sn.slice(Number(_config.h2h_out.sn_min_length) * -1); } return sn; } function parseSN(message, _config) { if (!_config) { _config = config; } var sn_regex = new RegExp(_config.h2h_out.sn_pattern); var sn_match = message.match(sn_regex); if (sn_match <= 0) { logger.info('SN Not found: ' + message); return ''; } var match_index = 0; if (_config.h2h_out.sn_match_index) { match_index = Number(_config.h2h_out.sn_match_index) } var sn = sn_match[match_index]; if (_config.h2h_out.sn_remove_whitespace) { sn = sn.replace(/\s/g, ''); } var sn_remove_patterns = _config.h2h_out.sn_remove_patterns.split(_config.h2h_out.sn_remove_patterns_separator); var count = sn_remove_patterns.length; for(var i = 0; i < count; i++) { //sn = sn.replace(sn_remove_patterns[i], ''); var re = new RegExp(sn_remove_patterns[i], 'g'); sn = sn.replace(re, ''); } sn = paddingSN(sn, _config); return sn.trim(); } function createServer() { var httpServer = http.createServer(function(request, response) { var response_code = '68'; var sn = ''; var qs = url.parse(request.url, true).query; logger.info('Got reverse report from gateway', {qs: qs}); if (qs.topup_status == 'S') { response_code = '00'; if (qs.sn && !config.h2h_out.force_parse_sn && !Number(config.h2h_out.force_parse_sn)) { sn = qs.sn; } else { logger.warn('Missing SN from query string. Trying to get SN from message'); sn = parseSN(qs.info); } if (config.h2h_out.sn_split_index) { sn = splitSN(sn, config); } if (sn) { sn = paddingSN(sn, config); } } else if (qs.topup_status == 'R') { response_code = '40'; } try { var new_response_code = responseCodeFromMessage(qs.info); if (new_response_code) { response_code = new_response_code; } } catch(err) { logger.warn('Exception on parsing reverse report', {exception: err} ); response_code = '40'; } message = qs.info; //updateBalance(message); if (sn) { message = 'SN=' + sn + '; ' + message; } response.end('OK'); var key = getRedisKey(qs.ts); redisClient.get(key, function(err, request_id) { if (err) { logger.warn('Error when requesting request id for ts:' + qs.ts + ' (' + key + ')', {redis_error: err}); return; } callbackReport(request_id, response_code, message); }); }); httpServer.listen(config.h2h_out.listen_port, function() { logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port); }); } function splitSN(sn, _config) { var sn_pieces = sn.split(' '); if (sn_pieces.length <= 0) { logger.info('Returning original SN because SN only has one element'); return sn; } if (!_config.h2h_out.sn_split_index) { logger.info('Returning original SN because config.h2h_out.sn_split_index undefined'); return sn; } var sn_indexes = _config.h2h_out.sn_split_index.split(','); logger.info('Split SN', {sn_pieces: sn_pieces, sn_indexes: sn_indexes}); var _sn = ''; var id_count = sn_indexes.length; for(var i = 0; i < id_count; i++) { var sn_index = sn_indexes[i]; var sn_piece = sn_pieces[sn_index]; if (sn_pieces[i]) { _sn = _sn + sn_piece; } else { logger.warn('Undefined value on sn piece ' + sn_index); } } _sn = _sn.trim(); if (_sn) { sn = _sn; logger.info('Got new SN: ' + sn); } else { logger.warn('Got empty SN when using split SN. Use original SN'); } return sn; } function responseCodeFromMessage(message) { if (message.indexOf('Nomor salah/tidak terdaftar') >= 0) { return '14'; } else if (message.indexOf('Nomor tidak di temukan/tidak aktif') >= 0) { return '14'; } else if (message.indexOf('Kode produk tidak sesuai nomor tujuan') >= 0) { return '14'; } else if (message.indexOf('nomor yang anda masukan salah') >= 0) { return '14'; } else if (message.indexOf('Nomor Telepon seluler salah') >= 0) { return '14'; } else if (message.indexOf('bulk or forbidden request') >= 0) { return '55'; } else if (message.indexOf('Sudah pernah dilakukan') >= 0) { return '55'; } else if (message.indexOf('transaksi yg sama sudah pernah dilakukan tunggu dlm') >= 0) { return '55'; } else if (message.indexOf('Transaksi tsb sudah pernah dilakukan') >= 0) { return '55'; } else if (message.indexOf('Mohon maaf saat ini stock belum tersedia') >= 0) { return '13'; } else if (message.indexOf('Stock tidak tersedia') >= 0) { return '13'; } else if (message.indexOf('Stok tidak tersedia') >= 0) { return '13'; } else if (message.indexOf('Saldo di account anda saat ini tidak mencukupi') >= 0) { return '40'; } return; } function start(_config, _callbackReport, options) { config = _config; callbackReport = _callbackReport if (options && options.aaa) { aaa = options.aaa; } if (options && options.logger) { logger = options.logger; } else { logger = new winston.Logger({ transports: [ new (winston.transports.Console)() ] }); } createRedisClient(); createServer(); } exports.start = start; exports.topupRequest = topupRequest; exports.parseSN = parseSN;