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 math = require('mathjs');
var winston = require('winston');
var cekstatus = require('./cekstatus.js');
var mongoClient = require('mongodb').MongoClient;
var LRU = require('lru-cache');
var config;
var httpServer;
var aaa;
var logger;
var callbackReport;
var mongodb;
var tasks = LRU(10000);
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
var sleep_before_retry = 30000;
var pendingResultCode = ['0005', '0012', '0068', '0090', '0063', '0018', '0096'];
var logTag = __filename.split('/').reverse()[0];
function initMongoClient() {
if (!config.mongodbstruk || !config.mongodbstruk.url) {
return;
}
try {
var url = config.mongodbstruk.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});
}
}
function prepareResultData(result) {
var task;
var data = {};
data.gateway = config.globals.gateway_name;
try {
data.requestId = result.reffid[0].trim();
var key = config.globals.gateway_name + '.rid:' + data.requestId;
task = tasks.get(key);
}
catch(err) { data.requestId = null; }
try {
data.status = result.ResultCode[0].trim();
}
catch(err) { data.status = '68' }
try {
data.rcmessage = result.ErrorMsg[0].trim();
}
catch(err) { data.rcmessage = ''; }
try {
data.resptext = '';
}
catch(err) { data.resptext = ''; }
try {
var ts = moment(task.timestamp, 'YYYYMMDDHHmmss').format('YYYY-MM-DD HH:mm:ss')
data.dt = ts;
}
catch(err) {
logger.warn('Exception when getting timestamp data, using current timestamp', {err: err, result: result, task: task});
data.dt = strftime('%Y-%m-%d %H:%M:%S', new Date());
}
try {
data.namapel = result.nama_pel[0].trim();
}
catch(err) { data.namapel = 'UNKNOWN'; }
try {
data.msn = result.nsm[0].trim();
}
catch(err) { data.msn = 'UNKNOWN'; }
try {
data.idpel = result.idpel[0].trim();
}
catch(err) { data.idpel = 'UNKNOWN'; }
try {
data.tarifdaya = result.tarif[0].trim();
}
catch(err) { data.tarifdaya = 'UNKNOWN'; }
try {
data.admin = parseInt(result.adminfee[0].trim());
}
catch(err) { data.admin = 0; }
try {
data.adm = parseInt(result.admin_fee[0].trim());
}
catch(err) { data.adm = 0; }
try {
data.hargapelanggan = parseInt(result.amount_trx[0].trim());
}
catch(err) { data.hargapelanggan = 0; }
try {
data.jumlahkwh = result.jml_daya[0].trim();
}
catch(err) { data.jumlahkwh = 0; }
try {
data.token = result.token[0].trim();
}
catch(err) { data.token = 0; }
try {
data.ppn = result.ppn_fee[0].trim();
}
catch(err) { data.ppn_fee = 0; }
try {
data.ppj = result.ppj_fee[0].trim();
}
catch(err) { data.ppj = 0; }
try {
data.angsuran = result.angsuran_fee[0].trim();
}
catch(err) { data.angsuran = 0; }
try {
data.meterai = result.materai_fee[0].trim();
}
catch(err) { data.materai_fee = 0; }
return data;
}
function saveTokenToMongoDb(result) {
if (!mongodb) {
return;
}
if (!config.mongodbstruk) {
return;
}
if (!config.mongodbstruk.collection) {
return;
}
var data = prepareResultData(result);
try {
mongodb.collection(config.mongodbstruk.collection).insertOne(data);
}
catch(err) {
logger.warn('Error when inserting data to mongodb', {err: err, data: data});
}
}
function putTaskToCache(task) {
var key = config.globals.gateway_name + '.rid:' + task.requestId;
try {
tasks.set(key, task);
}
catch(err) {
logger.warn('Error writing to task to cache', {err: err, key: key, task: task});
}
}
function topupRequest(task) {
aaa.insertTaskToMongoDb(task);
putTaskToCache(task);
var ts = strftime('%Y%m%d%H%M%S', new Date());
var data =
config.h2h_out.userid
+ '|' + config.h2h_out.password
+ '|' + task['remoteProduct']
+ '|' + task['destination'] + '|0';
var options = {
url: config.h2h_out.partner,
qs: {
ts: ts,
data: data,
reffid: task['requestId']
}
};
logger.info('Creating http request', {options: options});
request(options, function (error, response, body) {
var responseCode = '40';
var responseMessage = 'Gateway Error';
if (error) {
logger.warn('HTTP REQUEST ERROR', error);
callbackReport(task['requestId'], '89', 'HTTP REQUEST ERROR (' + error + ')');
} else if (response.statusCode != 200) {
var error_message = 'GATEWAY ERROR (HTTP RESPONSE CODE: ' + response.statusCode + ')';
logger.warn(error_message);
callbackReport(task['requestId'], '91', error_message);
} else {
logger.info('DIRECT RESPONSE', {requestId: task.requestId, body: body});
xml2js(body, function (err, result) {
if (err) {
logger.warn('Error parsing xml response', {requestId: task.requestId, err: err, body: body});
callbackReport(task['requestId'], '40', body);
} else {
var directResponse = result;
logger.info(directResponse);
saveTokenToMongoDb(directResponse.Result);
try {
var result_price;
try {
result_price = directResponse.Result.Price[0].trim();
}
catch(err) {
result_price = 0;
}
var result_error_message;
try {
result_error_message = directResponse.Result.ErrorMsg[0].trim();
}
catch(err) {
result_error_message = '';
}
var resultCode = directResponse.Result.ResultCode[0].trim();
responseMessage =
'ResultCode: ' + resultCode
+ ' | ErrorMsg: ' + result_error_message
+ ' | DateTime: ' + directResponse.Result.DateTime[0].trim()
+ ' | nsm: ' + directResponse.Result.nsm[0].trim()
+ ' | idpel: ' + directResponse.Result.idpel[0].trim()
+ ' | reffid: ' + directResponse.Result.reffid[0].trim()
+ ' | TransID: ' + directResponse.Result.TransID[0].trim()
+ ' | reff_switching: ' + directResponse.Result.reff_switching[0].trim()
+ ' | amount_trx: ' + directResponse.Result.amount_trx[0].trim()
+ ' | token: ' + directResponse.Result.token[0].trim()
+ ' | PrevBalance: ' + directResponse.Result.PrevBalance[0].trim()
+ ' | Price: ' + result_price
+ ' | EndBalance: ' + directResponse.Result.EndBalance[0].trim()
;
logger.info('Response message: ' + responseMessage);
if ( (resultCode == '0099') && (result_error_message.search(/METER .* YANG ANDA MASUKAN SALAH/) >= 0) ) {
callbackReport(task.requestId, '14', responseMessage);
return;
}
else if ( (resultCode == '0099') && (result_error_message.search(/IDPEL .* YANG ANDA MASUKAN SALAH/) >= 0) ) {
callbackReport(task.requestId, '14', responseMessage);
return;
}
else if ( (resultCode == '0099') && (result_error_message.search(/INQUIRY TIMEOUT, SILAHKAN DICOBA KEMBALI/) >= 0) ) {
callbackReport(task.requestId, '91', responseMessage);
return;
}
if (aaa) {
// update balance
aaa.updateBalance(directResponse.Result.EndBalance[0]);
}
if (resultCode == '0000') {
var nama_pelanggan = directResponse.Result.nama_pel[0].trim();
nama_pelanggan = nama_pelanggan.replace(/-\/-/g, '-');
var sn = directResponse.Result.token[0].trim() + '/' + nama_pelanggan + '/' + directResponse.Result.tarif[0].trim() + 'VA/' + directResponse.Result.jml_daya[0].trim();
sn = sn.replace(/\s/g, '-');
responseMessage = 'SN=' + sn + '; ' + responseMessage;
logger.info('New response message: ' + responseMessage);
}
if (pendingResultCode.indexOf(resultCode) != -1) {
callbackReport(task['requestId'], '68', responseMessage);
/*
logger.info('Got pending status, requesting advice from webreport in ' + sleep_before_retry + 'ms');
setTimeout(function () {
cekstatus.advice({trxid: directResponse.Result.TransID[0].trim()}, callbackFromWebReport);
}, sleep_before_retry);
*/
return;
}
responseCode = resultCode.replace(/^00/, "");
if (result_error_message == 'Inq - APLICATION SERVER RESPONSE TIMEOUT') {
responseCode = '91';
}
}
catch(err) {
responseCode = '40';
responseMessage = 'Invalid response from gateway';
}
}
callbackReport(task['requestId'], responseCode, responseMessage);
});
}
//callbackReport(task['requestId'], responseCode, responseMessage);
});
}
function callbackFromWebReport(status) {
if (!status) {
logger.warn('Advice from webreport return empty status');
return;
}
logger.info('Got advice result from webreport', {status: status});
var responseCode = '68';
var result_price = 0;
try {
result_price = directResponse.Result.Price[0].trim();
}
catch(err) {}
var errorMsg = '';
try {
errorMsg = status.response.errormsg[0];
}
catch(err) {}
var responseMessage = '';
try {
responseMessage =
'Hasil advice dari webreport '
+ 'ResultCode: ' + status.response.resultcode[0]
}
catch(err) {
logger.warn('Error parsing ResultCode from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | ErrorMsg: ' + errorMsg
}
catch(err) {
logger.warn('Error parsing ErrorMsg from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | TrxDate: ' + status.trxDate;
}
catch(err) {
logger.warn('Error parsing TrxDate from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | UpdateDate: ' + status.updateDate;
}
catch(err) {
logger.warn('Error parsing UpdateDate from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | nsm: ' + status.response.nsm[0];
}
catch(err) {
logger.warn('Error parsing nsm from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | idpel: ' + status.response.idpel[0];
}
catch(err) {
logger.warn('Error parsing idpel from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | reffid: ' + status.response.reffid[0].trim();
}
catch(err) {
logger.warn('Error parsing reffid from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | TransID: ' + status.response.transid[0].trim();
}
catch(err) {
logger.warn('Error parsing TransID from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | reff_switching: ' + status.response.reff_switching[0];
}
catch(err) {
logger.warn('Error parsing reff_switching from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | amount_trx: ' + status.response.amount_trx[0];
}
catch(err) {
logger.warn('Error parsing amount_trx from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | token: ' + status.response.token[0];
}
catch(err) {
logger.warn('Error parsing token from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | PrevBalance: ' + status.response.prevbalance[0];
}
catch(err) {
logger.warn('Error parsing PrevBalance from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | Price: ' + status.amount;
}
catch(err) {
logger.warn('Error parsing Price from webreport advice.', {err: err});
}
try {
responseMessage =
responseMessage
+ ' | EndBalance: ' + status.response.endbalance[0];
}
catch(err) {
logger.warn('Error parsing EndBalance from webreport advice.', {err: err});
}
if ((status.status == 'S') && (status.response.resultcode[0] == '0000')) {
responseCode = '00';
var nama_pelanggan = status.response.nama_pel[0] ;
nama_pelanggan = nama_pelanggan.replace(/-\/-/g, '-');
var sn = status.response.token[0] + '/' + nama_pelanggan + '/' + status.response.tarif[0] + 'VA/' + status.response.jml_daya[0];
responseMessage = 'SN=' + sn + '; ' + responseMessage;
} else if ((status.status == 'P') || (status.status == 'W')) {
callbackReport(status.response.reffid[0].trim(), '68', responseMessage);
logger.info('Got pending status, requesting advice from webreport in ' + sleep_before_retry + 'ms');
setTimeout(function () {
cekstatus.advice({trxid: status.trxId,}, callbackFromWebReport);
}, sleep_before_retry);
return;
} else {
responseCode = status.response.resultcode[0].replace(/^00/, "");
if (['00', '05', '12', '68', '90', '63', '18', '96'].indexOf(responseCode) >= 0) {
responseCode = '40';
}
}
callbackReport(status.response.reffid[0].trim(), responseCode, responseMessage);
}
function createHttpReportServer() {
var httpServer = http.createServer(function(request, response) {
var qs = url.parse(request.url, true).query;
var path = url.parse(request.url).pathname;
logger.info('Got reverse report from partner', {path: path, qs: qs});
response.end('OK');
var requestId = qs.reffid;
var resultCode = qs.rescode;
if (requestId && resultCode && resultCode != '0') {
if (pendingResultCode.indexOf(resultCode) >= 0) {
callbackReport(requestId, '68', 'Got reverse report: ' + qs.msg);
} else {
callbackReport(requestId, '40', 'Got reverse report: ' + qs.msg);
}
return;
}
var trxid;
try {
trxid = qs.transid;
}
catch(err) {
}
/*
if (trxid) {
logger.info('Requesting advice from webreport', {trxid: trxid})
cekstatus.advice({trxid: trxid}, callbackFromWebReport);
}
*/
});
httpServer.listen(config.h2h_out.listen_port, function() {
logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port);
});
}
function start(options) {
if (!options) {
console.log('Undefined options, terminating....');
process.exit(1);
}
if (options.config) {
config = options.config;
} else {
console.log('Undefined options.config, terminating....')
process.exit(1);
}
if (options.aaa) {
aaa = options.aaa;
callbackReport = options.aaa.callbackReportWithPushToMongoDb;
} else {
console.log('Undefined options.aaa, terminating....')
process.exit(1);
}
if (options && options.logger) {
logger = options.logger;
} else {
logger = new winston.Logger({
transports: [
new (winston.transports.Console)()
]
});
}
cekstatus.setLogger(logger);
createHttpReportServer();
initMongoClient();
}
exports.start = start;
exports.topupRequest = topupRequest;