diff --git a/partner-trugee.js b/partner-trugee.js new file mode 100644 index 0000000..6f49908 --- /dev/null +++ b/partner-trugee.js @@ -0,0 +1,334 @@ +var http = require('http'); +var url = require('url'); +var math = require('mathjs'); +var xml = require('xml'); +var xml2js = require('xml2js').parseString; +var strftime = require('strftime'); +var xor = require('base64-xor'); +var request = require('request'); + +var config; +var callbackReport; + +var max_retry = 2; +var sleep_before_retry = 2000; + +var trx_balances = {}; +var trx_prices = {}; + +function calculateSignature(password, msisdn, timestamp) { + var a = timestamp + msisdn.substr(msisdn.length - 4); + var b = msisdn.substr(msisdn.length - 4).split('').reverse().join('') + password; + + return xor.encode(a,b); +} + +/** + * Kalkulasi signature untuk cek balance + * + * @deprecated + */ +function calculateBalanceSignature(userid, password, timestamp) { + var a = '0000' + timestamp; + var b = userid.substr(0, 4) + password; + + return xor.encode(a,b); +} + + +function createPayload(task) { + var timestamp = strftime('%H%M%S'); + + var payload = { + datacell: [ + {command: 'TOPUP'}, + {vtype: task['remoteProduct']}, + {userid: config.h2h_out.userid}, + {time: timestamp}, + {msisdn: task['destination']}, + {trxid: task['requestId']}, + {sign: calculateSignature(config.h2h_out.password, task['destination'], timestamp)} + ] + }; + + console.log(payload); + return "<?xml version=\"1.0\" ?>\n" + xml(payload); +} + +function topupRequest(task, retry) { + //balanceCheck(); + + var payload_xml = createPayload(task); + //console.log(payload_xml); + + var postRequest = { + host: "202.152.62.2", + path: "/RELOAD97.php", + port: 7713, + method: "POST", + headers: { + 'Content-Type': 'text/xml', + 'Content-Length': Buffer.byteLength(payload_xml) + } + }; + + var buffer = ""; + var req = http.request( postRequest, function( res ) { + + console.log('Status code: ' + res.statusCode ); + var buffer = ""; + res.on( "data", function( data ) { buffer = buffer + data; } ); + res.on( "end", function( data ) { + topupResponseHandler(buffer); + }); + + }); + + req.on('error', function(e) { + console.log('problem with request: ' + e.message); + callbackReport(task['requestId'], '40', e.message); + }); + + req.write( payload_xml ); + req.end(); +} + +function topupResponseHandler(body, request_id) { + xml2js(body, function (err, result) { + if (err) { + console.log(body); + callbackReport(request_id, '40', buffer); + return; + } + + console.log(result); + + request_id = result.datacell.ref_trxid[0].trim(); + + var response_code = '68'; + + var message = ''; + try { + if (result.datacell.message && result.datacell.message.length > 0) { + message = result.datacell.message[0].trim(); + } else if (result.datacell.msg && result.datacell.msg.length > 0) { + message = result.datacell.msg[0].trim(); + } + } + catch(err) { + message = 'exception saat parsing message'; + } + + if (result.datacell.resultcode && result.datacell.resultcode[0] == '999') { + response_code = '40'; + } + + if (message.indexOf('Nomor tujuan salah') >= 0) { + response_code = '14'; + } else if (message.indexOf('*GAGAL, transaksi yang sama sudah ada dalam 10 menit') >= 0) { + response_code = '55'; + } else if (message.indexOf('saldo sdh dikembalikan') >= 0) { + response_code = '40' + } else if (message.indexOf('Trx dpt diulang') >= 0) { + response_code = '40' + } else if (message.indexOf('SUKSES SN Operator:') >= 0) { + response_code = '00'; + + var sn = parseSN(message); + console.log ('SN Operator: ' + sn); + + /* + if (!sn) { + + console.log('Missing real operator SN, using SN from suplier'); + try { + sn = result.datacell.trxid[0].trim(); + } + catch(err) { + sn = ''; + } + } + */ + + if (sn) { + message = 'SN=' + sn + '; ' + message; + } else { + message = 'SN belum didapat. ' + message; + response_code = '68'; + } + } + + + var price = priceFromMessage(message); + if (price != null) { + console.log('Harga: ' + price); + trx_prices[request_id] = price; + setTimeout(deleteTrxPrice, 3 * 24 * 3600 * 1000, request_id); + } else if (response_code == '00' && trx_prices[request_id] !== undefined) { + price = trx_prices[request_id]; + console.log('Harga: ' + price); + message = message + ' -- Harga: ' + price; + } + + var balance = balanceFromMessage(message); + if (balance != null) { + console.log('Saldo: ' + balance); + trx_balances[request_id] = balance; + setTimeout(deleteTrxBalance, 3 * 24 * 3600 * 1000, request_id); + } else if (response_code == '00' && trx_balances[request_id] !== undefined) { + balance = trx_balances[request_id]; + console.log('Saldo: ' + balance); + message = message + ' -- Saldo: ' + balance; + } + + callbackReport(request_id, response_code, message); + }); +} + +function deleteTrxPrice(request_id) { + delete trx_prices[request_id]; +} + +function deleteTrxBalance(request_id) { + delete trx_balances[request_id]; +} + +function parseSN(message) { + var results = message.match(/SN Operator: .+ SN Kami/); + if (!results || results.length <= 0) { + return ''; + } + + var result = results[0]; + result = result.replace('SN Operator:', ''); + result = result.replace('SN Kami', ''); + result = result.trim(); + + if (result == '00') { + result = ''; + } + + return result; +} + +function createServer() { + + var httpServer = http.createServer(function(req, res) { + var parsed_url = url.parse(req.url, true, true); + + console.log('Got request from partner ("' + req.url + '")'); + + var body = ""; + req.on('data', function (chunk) { + body += chunk; + }); + + req.on('end', function () { + res.writeHead(200); + res.end('OK'); + + //console.log(body); + + if (parsed_url.pathname == '/sn') { + console.log('Reverse report -- SN'); + topupResponseHandler(body); + + } else if (parsed_url.pathname = '/refund') { + console.log('Reverse report -- REFUND'); + callbackReport(parsed_url.query.ref_trxid, '40', parsed_url.query.message); + + } else { + console.log('Reverse report -- UNKNOWN'); + console.log('Ignore unknown request on reverse url'); + } + }); + }); + + httpServer.listen(config.h2h_out.listen_port, function() { + console.log('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port); + }); +} + +function balanceCheck() { + var timestamp = strftime('%H%M%S'); + + var payload = { + datacell: [ + {perintah: 'saldo'}, + {userid: config.h2h_out.userid}, + {time: timestamp}, + {sgn: calculateBalanceSignature(config.h2h_out.userid, config.h2h_out.password, timestamp)} + ] + }; + + var postRequest = { + host: "202.152.62.2", + path: "/RELOAD97.php", + port: 7713, + method: "POST", + headers: { + 'Content-Type': 'text/xml', + 'Content-Length': Buffer.byteLength(payload_xml) + } + }; + + var buffer = ""; + var req = http.request( postRequest, function( res ) { + + console.log('Status code: ' + res.statusCode ); + var buffer = ""; + res.on( "data", function( data ) { buffer = buffer + data; } ); + res.on( "end", function( data ) { + console.log('CHECK BALANCE RESULT:'); + console.log(buffer); + }); + + }); + + req.on('error', function(e) { + console.log('problem with request: ' + e.message); + }); + + req.write( payload_xml ); + req.end(); + +} + +function balanceFromMessage(message) { + var matches = message.match(/Saldo: Rp (\d+)/); + + if (!matches) { + return null; + } + if (matches.length < 2) { + return null; + } + + return matches[1]; +} + +function priceFromMessage(message) { + var matches = message.match(/Harga: (\d+)/); + + if (!matches) { + return null; + } + if (matches.length < 2) { + return null; + } + + return matches[1]; +} + +function start(_config, _callbackReport) { + config = _config; + callbackReport = _callbackReport + + createServer(); +} + +exports.start = start; +exports.topupRequest = topupRequest; +exports.balanceFromMessage = balanceFromMessage; +exports.priceFromMessage = priceFromMessage; +exports.calculateSignature = calculateSignature;