diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e20c3ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +config.ini +log.txt +logs/log* diff --git a/aaa.js b/aaa.js new file mode 100644 index 0000000..752bfe3 --- /dev/null +++ b/aaa.js @@ -0,0 +1,159 @@ +var request = require('request'); +var strftime = require('strftime'); + +var max_retry = 10; +var sleep_before_retry = 3000; + +var config; +var partner; + +var available_products = []; + +function unaliasResponseCode(response_code, config_responsecode_alias) { + if (config_responsecode_alias == undefined && config && config.h2h_out && config.h2h_out.responsecode_alias) { + config_responsecode_alias = config.h2h_out.responsecode_alias; + } + + if (!config_responsecode_alias) { + return response_code; + } + + items = config_responsecode_alias.split(','); + items_count = items.length; + + for (var i=0; i < items_count; i++) { + codes = items[i].split(':'); + + if (codes.length <= 0) { continue; } + + if (response_code == codes[0]) { + console.log('Change response code from ' + codes[0] + ' to ' + codes[1]); + return codes[1]; + } + } + + return response_code; +} + +function pullCity() { + var url = config.globals.aaa_host + '/pull_city'; + console.log('Pull cities from AAA - ' + url); + request(url, function (error, response, body) { + if (!error && response.statusCode == 200) { + //console.log('city=' + body); + } else { + console.log('Error in pull city'); + } + }); +} + +function pullProduct() { + var url = config.globals.aaa_host + '/pull_product?opr_name=' + config.globals.operators; + console.log('Pull products from AAA - ' + url); + + request(url, function (error, response, body) { + if (error || response.statusCode != 200) { + console.log('Error in pull products'); + return; + } + + var productsAndOperators = body.split(';'); + var productsCount = productsAndOperators.length; + + for (var i=0; i < productsCount; i++) { + var product = productsAndOperators[i].split(',', 1)[0]; + available_products.push(product); + } + //console.log(available_products); + }); +} + +function pull() { + var url = config.globals.aaa_host + + '/pull?city=ALL&nom=' + config.globals.products + + '&chip_info=' + config.globals.gateway_name; + + //console.log('AAA PULL - ' + url); + request(url, function (error, response, body) { + if (!error && response.statusCode == 200) { + if (body == 'NONE') { + return; + } + console.log(body); + + var result = body.split(';'); + if (result[0] != 'OK') { + return; + } + + var task = []; + task['requestId'] = result[1]; + task['timestamp'] = result[3]; + task['destination'] = result[4]; + task['product'] = result[7]; + + if (config.products[task['product']] !== undefined) { + task['remoteProduct'] = config.products[task['product']]; + } else { + task['remoteProduct'] = task['product']; + } + + partner.topupRequest(task); + + } else { + console.log('Error in pull task'); + return; + } + }); +} + +function pullLoop() { + if (!config.globals.pause) { + pull(); + } + + setTimeout(pullLoop, config.globals.interval); +} + +function callbackReport(requestId, responseCode, message, retry) { + if (retry === undefined) { + retry = max_retry; + } + + responseCode = unaliasResponseCode(responseCode); + + timestamp = strftime('%Y%m%d%H%M%S'); + var url = config.globals.aaa_host + + '/topup?trans_id=' + requestId + + '&trans_date' + timestamp + + '&trans_date=' + timestamp + + '&resp_code=' + responseCode + + '&ussd_msg=' + config.globals.gateway_name + + '$' + encodeURIComponent(message); + + console.log('Report to AAA - ' + url); + request(url, function (error, response, body) { + if (error || response.statusCode != 200) { + console.log('Error report to AAA'); + + if (retry) { + console.log('Retrying to report to AAA (' + retry + ')'); + callbackReport(requestId, responseCode, message, retry - 1); + } + } + }); +} + +function start(_config, _partner) { + config = _config; + partner = _partner; + + pullCity(); + pullProduct(); + + setTimeout(pullLoop, 10 * 1000); +} + +exports.start = start; +exports.callbackReport = callbackReport; +exports.unaliasResponseCode = unaliasResponseCode; diff --git a/config.sample.ini b/config.sample.ini new file mode 100644 index 0000000..4fad175 --- /dev/null +++ b/config.sample.ini @@ -0,0 +1,18 @@ +[globals] +operators=TEST,XL_ALTERNATIF +products=TST1 +gateway_name=NODEJS-DEV +aaa_host=http://172.23.0.12:4250 +interval=1000 +admin_port=17456 + +[h2h_out] +partner=https://172.23.0.12:6789 +userid=R97DEV +password=czcb +listen_port=13522 +check_interval=2000 + +[products] +TST1=TR1 + diff --git a/httpserver.js b/httpserver.js new file mode 100644 index 0000000..d90ecc6 --- /dev/null +++ b/httpserver.js @@ -0,0 +1,90 @@ +var http = require('http'); +var nsr = require('node-simple-router'); +var router = nsr(); + +var config; +var httpServer; + +function start(_config) { + if (_config != undefined) { + config = _config; + } + listenPort = config.globals.admin_port; + + router.get("/info", function(request, response) { + response.setHeader("Content-Type", "text/plain"); + response.write('CHIPINFO / GATEWAY NAME: ' + config.globals.gateway_name + "\n"); + response.write('PRODUCTS: ' + config.globals.products + "\n"); + response.write('AAA HOST: ' + config.globals.aaa_host + "\n"); + response.write('PARTNER: ' + config.h2h_out.partner + "\n"); + response.write('PAUSED: ' + config.globals.pause + "\n"); + response.write('UPTIME: ' + process.uptime() + "\n"); + response.write('REQUESTS COUNT: ' + config.globals.requests_count + "\n"); + response.write('ACTIVE REQUESTS COUNT: ' + config.globals.active_requests_count + "\n"); + response.write('MAX ACTIVE REQUESTS COUNT: ' + config.globals.max_active_requests_count + "\n"); + + response.end(); + }); + + router.get("/pause/:apikey", function(request, response) { + if (!config.globals.apikey) { + response.end('Undefined APIKEY on config'); + return; + } + + if (request.params.apikey != config.globals.apikey) { + response.end('Invalid APIKEY'); + return; + } + + config.globals.pause = 1; + response.end('Paused'); + }); + + router.get("/resume/:apikey", function(request, response) { + if (!config.globals.apikey) { + response.end('Undefined APIKEY on config'); + return; + } + + if (request.params.apikey != config.globals.apikey) { + response.end('Invalid APIKEY'); + return; + } + + delete config.globals.pause; + response.end('Resume'); + }); + + router.get("/reset-stats/:apikey", function(request, response) { + if (!config.globals.apikey) { + response.end('Undefined APIKEY on config'); + return; + } + + if (request.params.apikey != config.globals.apikey) { + response.end('Invalid APIKEY'); + return; + } + + config.globals.max_active_requests_count = 0; + + response.writeHead(307, { + 'Location': '/info' + }); + + response.end(); + }); + + httpServer = http.createServer(router).listen(listenPort); + console.log('HTTP server listens on port ' + listenPort); + + return httpServer; +} + +function setConfig(_config) { + config = _config; +} + +exports.start = start; +exports.setConfig = setConfig; diff --git a/index.js b/index.js new file mode 100644 index 0000000..79404a8 --- /dev/null +++ b/index.js @@ -0,0 +1,13 @@ +var iniparser = require('iniparser'); +var config = iniparser.parseSync('./config.ini'); + +var aaaHost = config.globals.aaa_host; + +HttpServer = require('./httpserver.js'); +var httpServer = HttpServer.start(config); + +var aaa = require('./aaa.js'); +var partner = require('./partner-scrappingkisel.js'); + +partner.start(config, aaa.callbackReport); +aaa.start(config, partner); diff --git a/logs/empty b/logs/empty new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json new file mode 100644 index 0000000..21e1d23 --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "sate24-to-sc", + "version": "0.0.1", + "description": "ST24 to SimpleConnect H2H OUT", + "main": "index.js", + "scripts": { + "test": "mocha" + }, + "repository": { + "type": "git", + "url": "git@gitlab.kodesumber.com:reload97/sate24-to-sc.git" + }, + "keywords": [ + "st24", + "reload97", + "ppob", + "h2h", + "m2m", + "xmlrpc" + ], + "author": "Adhidarma Hadiwinoto <gua@adhisimon.org>", + "license": "BSD", + "dependencies": { + "mocha": "~2.2.5", + "request": "~2.57.0", + "strftime": "~0.9.2", + "iniparser": "~1.0.5", + "mathjs": "~1.7.0", + "xmlrpc": "~1.3.1", + "xml2js": "~0.4.9", + "redis": "~0.12.1", + "hiredis": "~0.4.0", + "node-simple-router": "~0.9.4-2" + } +} diff --git a/partner-scrappingkisel.js b/partner-scrappingkisel.js new file mode 100644 index 0000000..ad77f06 --- /dev/null +++ b/partner-scrappingkisel.js @@ -0,0 +1,104 @@ +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 config; +var httpServer; + +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + +var logTag = __filename.split('/').reverse()[0]; + +function topupRequest(task) { + if (config.globals.requests_count == undefined) { + config.globals.requests_count = 1; + } else { + config.globals.requests_count++; + } + + if (config.globals.active_requests_count == undefined) { + config.globals.active_requests_count = 1; + } else { + config.globals.active_requests_count++; + } + + if (config.globals.max_active_requests_count == undefined) { + config.globals.max_active_requests_count = config.globals.active_requests_count; + } else { + config.globals.max_active_requests_count = math.max(config.globals.max_active_requests_count, config.globals.active_requests_count); + } + + var options = { + url: config.h2h_out.partner, + qs: { + reqid: task['requestId'], + msisdn: task['destination'], + product: task['product'] + } + }; + console.log(options); + + request(options, function (error, response, body) { + if (config.globals.active_requests_count == undefined) { + config.globals.active_requests_count = 0; + } else { + config.globals.active_requests_count--; + } + + if (error || response.statusCode != 200) { + console.log(logTag + ': Gateway Error'); + callbackReport(task['requestId'], '40', 'Gateway Error'); + return; + } + + console.log(logTag + ': Supplier response:'); + console.log(body); + + xml2js(body, function (err, result) { + if (err) { + callbackReport(task['requestId'], '40', body); + return; + } + + console.log(result); + + var response_code = '68'; + var message = result.trx_response.info[0].trim(); + + if (message == 'Error Parsing') { + + response_code = '40'; + + } else if (message == "Phone number's not found") { + + response_code = '14'; + + } else if (message == "TRANSAKSI SUKSES !!!") { + var destination = result.trx_response.msisdn.join(' ').trim(); + var product = result.trx_response.product.join(' ').trim(); + var ref_num = result.trx_response.ref_num.join(' ').trim(); + var harga = result.trx_response.harga.join(' ').trim(); + var kode_voucher = result.trx_response.kode_voucher.join(' ').trim(); + + response_code = '00'; + + message = 'SN=' + ref_num + '; ' + product + ' ' + msisdn + ' ' + harga + ' ref_num: ' + ref_num + ' kode_voucher: ' + kode_voucher; + } + + callbackReport(task['requestId'], response_code, message); + }); + }); +} + +function start(_config, _callbackReport) { + config = _config; + callbackReport = _callbackReport +} + +exports.start = start; +exports.topupRequest = topupRequest; diff --git a/test.js b/test.js new file mode 100644 index 0000000..46a97e3 --- /dev/null +++ b/test.js @@ -0,0 +1,49 @@ +var assert = require("assert"); + + +describe('aaa', function() { + var aaa = require('./aaa'); + + describe("#unaliasResponseCode()", function() { + it('should return 68', function() { + assert.equal('68', aaa.unaliasResponseCode('01', '01:68')); + }); + + it('should return 68', function() { + assert.equal('68', aaa.unaliasResponseCode('68', '01:68')); + }); + + it('should return 00', function() { + assert.equal('00', aaa.unaliasResponseCode('00', '01:68')); + }); + + it('should return 40', function() { + assert.equal('40', aaa.unaliasResponseCode('40', '')); + }); + + it('should return 40', function() { + assert.equal('40', aaa.unaliasResponseCode('40', '')); + }); + + it('should return 40', function() { + assert.equal('40', aaa.unaliasResponseCode('40')); + }); + + }); +}); + +describe('aaa', function() { + var partner = require('./httppulsakita'); + + describe("#parseResult()", function() { + message = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><respon><tanggal>2015/6/16 15:43:35</tanggal><idagen>P0039</idagen><refid>AEE15B32987941D89FFF4BC7EF676C13</refid><produk>PLN20</produk><tujuan>14204279369</tujuan><rc>0000</rc><data> </data><token> </token><pesan>#14836 PLN20 ke:14204279369 SUKSES. SN:3520-2887-6627-6699-4826/TestDummyPanjang6955555/P1/7000VA/32,4. \ +Sisa saldo Rp. 5,000,000 - Rp. 18,700 = Rp. 4,981,300</pesan></respon>'; + + data = partner.parseResult(message); + console.log(data); + + it('should return 2015/6/16 15:43:35', function() { + assert.equal('2015/6/16 15:43:35', data.respon.tanggal); + }); + }); +});