diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec66ab7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +config.ini +config.ini.backup* +run.sh +admin-cli.js +logs/ diff --git a/index.js b/index.js new file mode 100644 index 0000000..822a3f2 --- /dev/null +++ b/index.js @@ -0,0 +1,29 @@ +var fs = require('fs'); +var ini = require('ini'); +var expresso = require('sate24-expresso'); +var config = ini.parse(fs.readFileSync(__dirname + '/config.ini', 'utf-8')); + +process.chdir(__dirname); + +var logDirectory = __dirname + '/logs'; +fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory); + +var logger = require('sate24/logger.js').start(); +var HttpServer = require('sate24/httpserver.js'); +var aaa = require('sate24/aaa.js'); +var partner = require('./partner-fm.js'); + +var matrix = aaa.prepareMatrix(); + +var options = { + 'aaa': aaa, + 'logger': logger, + 'config': config, + 'matrix': matrix, +} + +var httpServer = HttpServer.start(config, options); + +partner.start(config, aaa.callbackReport, options); +aaa.start(config, partner, options); +expresso.start(options); diff --git a/package.json b/package.json new file mode 100644 index 0000000..41c57d5 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "sate24-to-fm-bp", + "version": "1.0.0", + "description": "ST24 H2H-OUT to FM based on belanjapulsa.com", + "main": "index.js", + "scripts": { + "test": "mocha" + }, + "repository": { + "type": "git", + "url": "git@gitlab.kodesumber.com:reload97/sate24-to-fm-bp.git" + }, + "keywords": [ + "st24", + "reload97", + "r97", + "ppob", + "fm", + "flashmachine" + ], + "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", + "license": "ISC", + "dependencies": { + "request": "^2.74.0", + "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git", + "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git", + "xml2js": "^0.4.17" + }, + "devDependencies": { + "should": "^11.1.0" + } +} diff --git a/partner-fm.js b/partner-fm.js new file mode 100644 index 0000000..70792de --- /dev/null +++ b/partner-fm.js @@ -0,0 +1,178 @@ +var xml2js = require('xml2js'); + +var aaa; +var _callbackReport; +var config; +var logger; + +var xmlBuilder = new xml2js.Builder(); + +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 { + console.log('Undefined options.logger, terminating....') + process.exit(1); + } + + createServer(); + + /* + resendDelay.init({ + config: config, + topupRequest: topupRequest, + logger: logger + }); + */ +} + +function topupRequest(task) { + aaa.insertTaskToMongoDb(task); + + var payload = composeTopupStatusMessage( + config.h2h_out.pin, + task.remoteProduct, + task.destination, + task.requestId + ); + + var reqOpts = { + url: config.h2h_out.partner_url, + method: "POST", + headers: { + 'Content-Type': 'text/xml', + 'Content-Length': Buffer.byteLength(payload) + } + } + + logger.verbose('Requesting to partner', {reqOpts: reqOpts, payload: payload}); + var buffer = ""; + var req = http.request(reqOpts, function( res ) { + + logger.info('Status code: ' + res.statusCode ); + var buffer = ""; + res.on( "data", function( data ) { buffer = buffer + data; } ); + res.on( "end", function( data ) { + logger.verbose('Got a direct response from partner', {response: buffer, task: task}); + topupResponseHandler(buffer, task.requestId); + }); + }); +} + +function topupResponseHandler(xmlResponse, _requestId, cb) { + var xmlParser = xml2js.parseString; + xmlParser(xmlResponse, function(err, data) { + var msg; + var requestId; + var rc = '68'; + + if (_requestId) { + requestId = _requestId; + } + + if (err) { + msg = 'Error parsing xml response: ' + err; + + if (logger) { + logger.warn(msg, {err: err, response: xmlResponse, task: task}); + } else { + console.log(msg); + } + } else { + + try { + msg = data.fm.message + } + catch(e) { + msg = 'Unknown message' + } + + if (data.fm.status == '0') { + + rc = '00'; + msg = modifyMessageWithSn(msg); + + } else if (data.fm.status == '1') { + rc = '68'; + } else if (data.fm.status == '2') { + rc = '40'; + } else if (data.fm.status == '3') { + rc = '40'; + } else { + rc = '68'; + } + + if (data.fm.refTrxid) { + requestId = data.fm.refTrxid; + } + + } + + cb(requestId, rc, msg, xmlResponse) + }); +} + +function callbackReport(requestId, responseCode, msg, rawResponse) { + if (requestId) { + _callbackReport(requestId, responseCode, msg, null, rawResponse); + } else { + logger.warn('Undefined requestId, not sending callbackReport', {rc: responseCode, msg: msg, rawResponse: rawResponse}); + } + +} + +function getSnFromMessage(msg) { + try { + var matches = msg.match(/SN:(\w+)/); + return matches[1]; + } + catch(e) { + return; + } +} + +function modifyMessageWithSn(msg) { + var sn = getSnFromMessage(msg); + if (sn) { + msg = 'SN=' + sn + '; ' + msg; + } + return msg; +} + +function composeTopupStatusMessage(pin, product, destination, requestId) { + var data = {fm: { + command: 'TOPUP', + pin: pin, + product: product, + msisdn: destination, + refTrxid: requestId + }} + + return xmlBuilder.buildObject(data); +} + +exports.start = start; +exports.topupRequest = topupRequest; +exports.composeTopupStatusMessage = composeTopupStatusMessage; +exports.getSnFromMessage = getSnFromMessage; +exports.modifyMessageWithSn = modifyMessageWithSn; diff --git a/test.js b/test.js new file mode 100644 index 0000000..b92976b --- /dev/null +++ b/test.js @@ -0,0 +1,28 @@ +var should = require('should'); +var crypto = require('crypto'); + +describe('#partner', function () { + var partner = require('./partner-fm') + + describe('#composeTopupStatusMessage', function() { + it('should return correct xml message', function() { + var msg = partner.composeTopupStatusMessage('1234', 'S10', '08120812', '2345'); + crypto.createHash('sha256').update(msg, 'utf8').digest().toString('hex').should.equal('1b926cb9101d9b172ae12206d0c10d4800b553f3d9f2e320fe526c7effb11985'); + }) + }); + + describe('#getSnFromMessage', function() { + it('should return correct sn', function() { + partner.getSnFromMessage('S10.081300000000 berhasil, SN:123456789').should.equal('123456789'); + }); + }) + + describe('#modifyMessageWithSn', function() { + it('should return correct sn', function() { + partner.modifyMessageWithSn('S10.081300000000 berhasil.').should.equal('S10.081300000000 berhasil.'); + partner.modifyMessageWithSn('S10.081300000000 berhasil, SN:').should.equal('S10.081300000000 berhasil, SN:'); + partner.modifyMessageWithSn('S10.081300000000 berhasil, SN:123456789').should.equal('SN=123456789; S10.081300000000 berhasil, SN:123456789'); + partner.modifyMessageWithSn('S10.081300000000 berhasil, SN:123456789. Berita tambahan').should.equal('SN=123456789; S10.081300000000 berhasil, SN:123456789. Berita tambahan'); + }); + }); +})