var winston = require('winston'); var crypto = require('crypto'); var xml = require('xml'); var url = require('url'); var http = require('http'); var xml2js = require('xml2js').parseString; var mongoClient = require('mongodb').MongoClient; var strftime = require('strftime'); var moment = require('moment'); var config; var callbackReport; var aaa; var logger; var options; var mongodb; function initMongoClient() { if (!config.mongodb || !config.mongodb.url) { return; } try { var url = config.mongodb.url; mongoClient.connect(url, function(err, db) { if (err) { logger.warn('Failed to connect to mongodb', {err: err}); return; } mongodb = db; logger.info('MongoDB connected'); }); } catch(err) { logger.warn('Exception when connecting to mongodb', {err: err, url: url}); } } var maxRetry = 10; var sleepBeforeRetry = 30; 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)() ] }); } initMongoClient(); } function calculateSignature(params) { var passwordHash = crypto.createHash('sha1').update(params.password).digest().toString('hex'); var plain = params.trxtype + params.prdcode + params.value + params.msisdn + params.trxid + params.uid + passwordHash; var signature = crypto.createHash('sha256').update(plain).digest().toString('hex'); try { logger.verbose('Signature calculated', {plain: plain, signature: signature}); } catch(err) {} return signature; } function createXmlPayload(params) { var payload = "<?xml version=\"1.0\" ?>\n" + xml({ ciwaru: [ {trxtype: params.trxtype}, {prdcode: params.prdcode}, {value: params.value}, {msisdn: params.msisdn}, {trxid: params.trxid}, {uid: params.uid}, {hash: calculateSignature(params)} ] }); try { logger.verbose("Payload: " + payload); } catch(errLog) {} return payload; } function insertTaskToMongoDb(task) { if (!isMongoReady()) { return; } try { mongodb.collection(config.mongodb.collection).insertOne(task); } catch(err) { //logger.warn('Exception when inserting document to mongodb', {err: err, task: task}); } } function pushResponseToMongoDb(task, response) { if (!isMongoReady()) { return; } try { mongodb.collection(config.mongodb.collection).updateOne( {requestId: task.requestId}, {$push: {responses: response}}, function(err, result) { if (err) { logger.warn('Error when pushing response to mongodb', {err: err, task: task, response: response}); return; } } ); } catch(err) { logger.warn('Exception when pushing response to mongodb', {err: err, task: task, response: response}); } } function isMongoReady() { if (!config.mongodb) { return; } if (!config.mongodb.collection) { return; } if (!mongodb) { return; } return true; } function getSNFromMessage(message) { try { var sn_match = message.match(/SN: (\w+)/); logger.verbose('Got SN: ' + sn_match[1]); return sn_match[1].trim(); } catch(err) { logger.verbose('Exception on getting sn from message', {err: err}); return ''; } } function hasSuccessKeywords(message) { var keywords = ['SUKSES', 'Finish']; var count = keywords.length; for (var i=0; i < count; i++) { if (message.indexOf(keywords[i]) >= 0) { return true; } } return false; } function supplierRcToST24Rc(rc) { var rcs = { '0001': '40', '0019': '13', // produk tidak tersedia } if (rcs[rc]) { return rcs[rc]; } else { return; } } function getSNFromResponseObject(respObj) { try { return respObj.ciwaru.sn[0].trim(); } catch(err) { return; } } function topupResponseHandler(body, task) { //logger.info('Got reply from partner', {body: body}); xml2js(body, function(err, result) { var ts = strftime('%Y-%m-%d %H:%M:%S', new Date()); if (err) { logger.warn('Got invalid XML from partner', {err: err, body: body, task: task}); callbackReport(task.requestId, '68', body); pushResponseToMongoDb(task, {ts: ts, raw: body}); return; } logger.info('XML message from partner', {result: result}); pushResponseToMongoDb(task, {ts: ts, raw: body, parsed: result}); var rc = '68'; var message = result.ciwaru.msg[0]; /* var trxid = 0; try { trxid = result.ciwaru.reqnum[0]; } catch(err) { trxid = result.ciwaru.trxid[0]; } */ if (message.toUpperCase().indexOf('PENDING') >= 0) { rc = '68'; } else if (hasSuccessKeywords(message)) { var sn = getSNFromResponseObject(result); if (!sn) { sn = getSNFromMessage(message); } message = 'SN=' + sn + '; ' + message; rc = '00'; } else { rc = supplierRcToST24Rc(result.ciwaru.rc[0]); if (!rc) { rc = '68'; } } if ((task.retry == maxRetry) || (rc != '68')) { callbackReport(task.requestId, rc, message); } else { logger.info('Not reporting to AAA for duplicate 68', {task: task}); } if (rc == '68') { topupRequestRetry(task); } }); } function topupRequestRetry(task) { task.retry--; if (task.retry > 0) { logger.info('Retrying in ' + sleepBeforeRetry + 's'); setTimeout(topupRequest, sleepBeforeRetry * 1000, task, task.retry); } else { logger.warn('Maximum retry for pending status exceeded', {task: task}); } } function topupRequest(task, retry) { if (retry === undefined) { task.ts = moment(task.timestamp, 'YYYYMMDDHHmmss').format('YYYY-MM-DD HH:mm:ss'); insertTaskToMongoDb(task); retry = maxRetry; } if (!task.retry) { task.retry = retry; } var remoteProduct = task.remoteProduct.split(','); var params = { trxtype: '01', prdcode: remoteProduct[0], value: remoteProduct[1], msisdn: task.destination, trxid: task.requestId, uid: config.h2h_out.userid, password: config.h2h_out.password, }; var postBody = createXmlPayload(params); var partnerUrl = url.parse(config.h2h_out.partner); var postRequest = { host: partnerUrl.hostname, path: partnerUrl.path, port: partnerUrl.port, method: "POST", headers: { 'Content-Type': 'text/xml', 'Content-Length': Buffer.byteLength(postBody) } }; logger.info('POST to partner', {postRequest: postRequest}); var req = http.request(postRequest, function( res ) { logger.info('Status code: ' + res.statusCode ); var buffer = ""; res.on( "data", function( data ) { buffer = buffer + data; } ); res.on( "end", function( data ) { topupResponseHandler(buffer, task); }); }); req.on('error', function(e) { logger.warn('problem with request: ' + e.message); callbackReport(task['requestId'], '68', e.message); topupRequestRetry(task); }); req.write(postBody); req.end(); } exports.start = start; exports.topupRequest = topupRequest; exports.calculateSignature = calculateSignature; exports.createXmlPayload = createXmlPayload; exports.getSNFromMessage = getSNFromMessage;