Commit 4cc1669e7107b7a0803f090c409f57a57b0e991a

Authored by Adhidarma Hadiwinoto
1 parent 3bef0d5aa2
Exists in master

Ready to running test

Showing 9 changed files with 622 additions and 0 deletions Side-by-side Diff

... ... @@ -0,0 +1,14 @@
  1 +{
  2 + "server_options": {
  3 + "port": 6789
  4 + },
  5 + "smscid": "XML1234",
  6 + "immediate_reply": 1,
  7 + "master_url": "http://127.0.0.1:4100/",
  8 + "res_port": 25300,
  9 + "oracle": {
  10 + "user": "user",
  11 + "password": "password",
  12 + "connectString": "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=172.0.0.1)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ST24)))"
  13 + }
  14 +}
... ... @@ -0,0 +1,11 @@
  1 +var neoxmlinutil = require('./neoxmlinutil');
  2 +var xmlrpcServer = require('./xmlrpc-server');
  3 +var logger = require('./logger.js').start();
  4 +
  5 +var options = {
  6 + logger: logger
  7 +};
  8 +
  9 +neoxmlinutil.init(options);
  10 +neoxmlinutil.connectToOracle();
  11 +xmlrpcServer.start(options);
... ... @@ -0,0 +1,48 @@
  1 +var strftime = require('strftime');
  2 +var winston = require('winston');
  3 +var fs = require('fs');
  4 +
  5 +var loggerTimestamp = function() {
  6 + return strftime('%F %T', new Date());
  7 +}
  8 +
  9 +function start(options) {
  10 + var logDir = __dirname + '/logs'
  11 +
  12 + var logLevel = 'debug';
  13 + if (options && options.logLevel) {
  14 + logLevel = options.logLevel;
  15 + }
  16 +
  17 + if (!fs.existsSync(logDir)) {
  18 + fs.mkdirSync(logDir);
  19 + }
  20 +
  21 + var logger = new winston.Logger({
  22 + transports: [
  23 + new (winston.transports.Console)({
  24 + timestamp: function() {
  25 + return strftime('%F %T', new Date());
  26 + },
  27 + level: 'verbose',
  28 + }),
  29 +
  30 + new(require('winston-daily-rotate-file')) ({
  31 + name: './log-file-txt',
  32 + filename: logDir + '/log.txt',
  33 + timestamp: loggerTimestamp,
  34 + formatter: function(options) {
  35 + return options.timestamp()
  36 + +' ' + options.level.toUpperCase()
  37 + +' ' + (undefined !== options.message ? options.message : '')
  38 + + (options.meta && Object.keys(options.meta).length ? '\n\t' + JSON.stringify(options.meta) : '' );
  39 + },
  40 + level: logLevel,
  41 + }),
  42 + ]
  43 + });
  44 +
  45 + return logger;
  46 +}
  47 +
  48 +exports.start = start;
... ... @@ -0,0 +1,78 @@
  1 +var oracledb = require('oracledb');
  2 +
  3 +var config = require("./config.json");
  4 +var logger = console;
  5 +
  6 +var oraCon;
  7 +
  8 +function init(options) {
  9 + if (options && options.logger) {
  10 + logger = options.logger;
  11 + }
  12 +}
  13 +
  14 +function connectToOracle(callback) {
  15 + oracledb.getConnection(config.oracle, function(err, connection) {
  16 + if (err) {
  17 + logger.warn('Can not connect to oracle db: ' + err);
  18 + process.exit(1);
  19 + }
  20 +
  21 + oraCon = connection;
  22 + logger.info("Oracle db connected")
  23 +
  24 + if (callback) {
  25 + callback(null, oraCon);
  26 + }
  27 + })
  28 +}
  29 +
  30 +function getTransactionIdFromMessage(message) {
  31 + try {
  32 + var matches = message.match(/ID=(\d+)/);
  33 + return matches[1];
  34 + }
  35 + catch(err) {
  36 + return '0';
  37 + }
  38 +}
  39 +
  40 +function getReverseUrl(msisdn, callback) {
  41 + oraCon.execute(
  42 + "SELECT trim(A.MSISDN) MSISDN, trim(B.REVERSE_URL) REVERSE_URL FROM T_STORE_USER_MSISDN A, T_STORE_USER B WHERE A.STORE_ID = B.STORE_ID AND A.USER_NAME = B.USER_NAME AND B.REVERSE_URL IS NOT NULL AND A.MSISDN = :msisdn",
  43 + [msisdn],
  44 + function(err, result) {
  45 + if (err) {
  46 + logger.warn('Error retrieving reverse url from oracle: ' + err);
  47 + callback(err);
  48 + return;
  49 + }
  50 +
  51 + var rowCount = result.rows.length;
  52 +
  53 + var retval = [];
  54 + for (var i = 0; i < rowCount; i++) {
  55 + retval.push(result.rows[i][1]);
  56 + }
  57 +
  58 + callback(null, retval);
  59 + }
  60 + );
  61 +}
  62 +
  63 +function getRequestIdFromResponseMessage(message) {
  64 + try {
  65 + var tokens = message.split('.');
  66 + return tokens[4];
  67 + }
  68 + catch(err) {
  69 + return;
  70 + }
  71 +
  72 +}
  73 +
  74 +exports.init = init;
  75 +exports.connectToOracle = connectToOracle;
  76 +exports.getReverseUrl = getReverseUrl;
  77 +exports.getTransactionIdFromMessage = getTransactionIdFromMessage;
  78 +exports.getRequestIdFromResponseMessage = getRequestIdFromResponseMessage;
... ... @@ -0,0 +1,35 @@
  1 +{
  2 + "name": "sate24-neoxmlin",
  3 + "version": "1.0.0",
  4 + "description": "Drop-in replacement for ST24 XML-RPC in",
  5 + "main": "index.js",
  6 + "scripts": {
  7 + "test": "mocha"
  8 + },
  9 + "repository": {
  10 + "type": "git",
  11 + "url": "git@gitlab.kodesumber.com:reload97/sate24-neoxmlin.git"
  12 + },
  13 + "keywords": [
  14 + "r97",
  15 + "reload97",
  16 + "st24",
  17 + "ppob",
  18 + "xmlrpc",
  19 + "xmlin"
  20 + ],
  21 + "author": "Adhidarma Hadiwinoto <me@adhisimon.org>",
  22 + "license": "ISC",
  23 + "dependencies": {
  24 + "oracledb": "^1.9.3",
  25 + "request": "^2.72.0",
  26 + "strftime": "^0.9.2",
  27 + "winston": "^2.2.0",
  28 + "winston-daily-rotate-file": "^1.1.1",
  29 + "xml2js": "^0.4.16",
  30 + "xmlrpc": "^1.3.1"
  31 + },
  32 + "devDependencies": {
  33 + "should": "^9.0.0"
  34 + }
  35 +}
... ... @@ -0,0 +1,24 @@
  1 +-----BEGIN CERTIFICATE-----
  2 +MIID8zCCAtugAwIBAgIJAN0ti6ihYyvqMA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD
  3 +VQQGEwJJRDEUMBIGA1UECAwLREtJIEpha2FydGExFjAUBgNVBAcMDUpha2FydGEg
  4 +UHVzYXQxETAPBgNVBAoMCFJlbG9hZDk3MRswGQYDVQQDDBJ4bWxpbi5yZWxvYWQ5
  5 +Ny5jb20xIjAgBgkqhkiG9w0BCQEWE2FkaGlzaW1vbkBnbWFpbC5jb20wHhcNMTYw
  6 +NjAxMTMyMDUzWhcNMjYwNTMwMTMyMDUzWjCBjzELMAkGA1UEBhMCSUQxFDASBgNV
  7 +BAgMC0RLSSBKYWthcnRhMRYwFAYDVQQHDA1KYWthcnRhIFB1c2F0MREwDwYDVQQK
  8 +DAhSZWxvYWQ5NzEbMBkGA1UEAwwSeG1saW4ucmVsb2FkOTcuY29tMSIwIAYJKoZI
  9 +hvcNAQkBFhNhZGhpc2ltb25AZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC
  10 +AQ8AMIIBCgKCAQEA3kmctiCOWifW4FAJlvvK888LaZ7k5kTsBma+5D8THIhQiW2c
  11 +TNZhSiOFalgTFv2ICTpq/SJ81+v3Im8Lv+Y3MCWLvu5MkLYyrX/AJYq55dmn64WI
  12 +Mx+dHUJSuZ/IxOUVIVGwJ9RW8FpkwnUvEPg5hZG+B0sVJhbxKTuiTSqWRuT7DWGe
  13 +dG1ejnGYpQOM9H6+eF1sHAAGYQ7mqhD/kaDtPilpGNWe/ugfzoqok/L3D1uExMzA
  14 +ovoG0XgIN0f/JvSkhGV+4TblvPQ6HN+VWlkMJ9jLGXEGX/dqKk0wT9hgUPm4Erpj
  15 +Oeul3Gk5/bs50nMJotIQ2hxEsIY0IYD0yyY2HwIDAQABo1AwTjAdBgNVHQ4EFgQU
  16 +po0uWeWkrCwFIZE7KKUSZRUFKF4wHwYDVR0jBBgwFoAUpo0uWeWkrCwFIZE7KKUS
  17 +ZRUFKF4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAITnQYHKxZ+ZI
  18 +Ug3npsC+E6hsEXi8SuV9gO/PBpEITR+oJd6vNTmdiFtEqWPGZm0+Jbdi7/UsIIg1
  19 +b5kYLuCFpf+h6ulv94Na8aIOPdpoH0V7vrWZ6zSw7jUDZcFwiSEfsmozpXaspE3p
  20 +mESbXndBuP8lweUg4dBS4/+Dlqm0GLtacoLBuKlPqraOVAlVTy4JqxvOy0RlX/47
  21 +FldEIc8bhNIgYiJjnYnsQstNLtk9mvJ3eg0qy174JbE8Ls6hiitx1fI8gUq1wbmM
  22 +AtMt2xZic/U4xZuTf6SXF3ogBg5C9xkHn2gb7NCuMEAyCU+NYAR7NtNwRuqXWeHI
  23 +r8U0aGtxeg==
  24 +-----END CERTIFICATE-----
... ... @@ -0,0 +1,28 @@
  1 +-----BEGIN PRIVATE KEY-----
  2 +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDeSZy2II5aJ9bg
  3 +UAmW+8rzzwtpnuTmROwGZr7kPxMciFCJbZxM1mFKI4VqWBMW/YgJOmr9InzX6/ci
  4 +bwu/5jcwJYu+7kyQtjKtf8Alirnl2afrhYgzH50dQlK5n8jE5RUhUbAn1FbwWmTC
  5 +dS8Q+DmFkb4HSxUmFvEpO6JNKpZG5PsNYZ50bV6OcZilA4z0fr54XWwcAAZhDuaq
  6 +EP+RoO0+KWkY1Z7+6B/OiqiT8vcPW4TEzMCi+gbReAg3R/8m9KSEZX7hNuW89Doc
  7 +35VaWQwn2MsZcQZf92oqTTBP2GBQ+bgSumM566XcaTn9uznScwmi0hDaHESwhjQh
  8 +gPTLJjYfAgMBAAECggEAA+85nt+kpWPfIclI4itCxF39zbjKRGkj5FkdN90F8ZNY
  9 +GZafFZ0Vy8IlMmSssdN6/O7Txfc+TnQWw71Db/obL/b6Kj79i/Ito3SHaiKyd6cK
  10 +mdibOIxus33hCPgTDakgXp6bh5jykP6ImgiI9LvwCLUzr91cWng9fIVZshP25PpQ
  11 +I/dI+2fhL+f+QB3QJEMDyz2b7KR3i4IbX8gHUUcT83JPnqZ07lED1gqAUIuSwbDz
  12 +kskxVPm8AGOiQ/f1sCbrAyetGy3nZ0N51RvRgFOREyb+SnmKI86GtgwEo5kCAzip
  13 +D4Xy+GCHMUennnHOzcLwthEn1FFk3Xww2CbjmQPKQQKBgQD3c8dgY13gjaUVREru
  14 +aSoUv6sZWRbXYK/MhBrUyt0VgpISEgG2zXTgwPuYVJofJVUZB6QVAgiGKiKySHwN
  15 +zYd8sQAtiKjUQIEUr2uVO/l+uSwSnBCdI85+Rxt1a9PxM4JDs6e7HYn4N7UowBoJ
  16 +PofnE8X2mptZTRmNX3EDJC5lEwKBgQDl9007tRA+fePfKRfmePqb+3oC0PLaQxi6
  17 +ZMTudefDwUVzEBRw2aCsuS5Sqi+CAeyKsYORjNiR1xUw8dYrljP17wz7+I1/ksQe
  18 +PIl+Ujhm36cbCOPOdIJoOt4ocAMyJ3hGbWph3A0HSSagqsbfdYUScmMTvlnRJynu
  19 +L5VLtv4oRQKBgFdwbAVdV3Df3HO1XBz+wEXwrI8acf3NRpGoJKsIvDK7ns4P2tcz
  20 +ppc82iqryoIy+O0NTCe4PjbacMQ65Msj0zp2eS+d2LqXyIT8r7FeLf1fhXlV+HDu
  21 +lCr/CW7mmU/j+aXet+hgub4iQTuTFiAqHm/DBvLrEvXtTpgsH40SnepNAoGActV5
  22 +NbWGtQ38OjW0/5ut2TJMjRd2DdOvh3RGeqVZ5ICgg4KmEmw6NcYLAbCGJK99RQy7
  23 +nHTuuHTk2hoJTp6kt8x1eWV2kxvg3xBvwCqii4xnT7Kyvyei7xVmVl8I2YSoJNMC
  24 +aneN8fNvXI+q22890iZYn0VQaIbcA1qzfKmWZNkCgYEA0PYfpPUoEdkqzW+SqKb/
  25 +8ky8qkuYMYC0toHI2lGQZIomZ6RWXd8OVn9UeM5CTtP+n4IzZHQBAu5Qxvss/S2T
  26 +TAdpb/ILx3dK8Px1lUc5a0cf7mDL39nSWp9rmD1qoRVM1Wr0jeQ80jQXMZluAIjt
  27 +KbMtcoO4E/22+apAg33FRmE=
  28 +-----END PRIVATE KEY-----
... ... @@ -0,0 +1,24 @@
  1 +var should = require('should');
  2 +var neoxmlinutil = require('./neoxmlinutil');
  3 +
  4 +describe('#neoxmlinutil', function() {
  5 +
  6 + before(function(ready) {
  7 + neoxmlinutil.connectToOracle(ready);
  8 + })
  9 +
  10 +
  11 + it ('#should return correct transaction id', function() {
  12 + var message = 'SN=4786-4311-9060-7286-2722/RAJUDIN/R1/1300VA/13.7;31/05/16 12:10 ISI PLN20 KE 45008590213, BERHASIL.SAL=54.283.173,HRG=19.950,ID=2565605,SN=4786-4311-9060-7286-2722/RAJUDIN/R1/1300VA/13.7; -- Telkomsel Nasional kode S denom 5000 harga 5300 silahkan';
  13 + neoxmlinutil.getTransactionIdFromMessage(message).should.equal('2565605');
  14 + })
  15 +
  16 + it ('#should return correct reverse url', function(done) {
  17 +
  18 + neoxmlinutil.getReverseUrl('R97DEV', function(err, result) {
  19 + result.should.not.be.empty();
  20 + done();
  21 + })
  22 +
  23 + })
  24 +})
... ... @@ -0,0 +1,360 @@
  1 +var xmlrpc = require('xmlrpc');
  2 +var request = require('request');
  3 +var neoxmlinutil = require('./neoxmlinutil');
  4 +var http = require('http');
  5 +var https = require('https');
  6 +var fs = require('fs');
  7 +var xml2js = new require('xml2js');
  8 +var xml2jsParser = require('xml2js').parseString;
  9 +var xml2jsBuilder = new xml2js.Builder();
  10 +var url = require("url");
  11 +var strftime = require('strftime');
  12 +
  13 +var options;
  14 +var config;
  15 +var logger;
  16 +
  17 +var server;
  18 +
  19 +function start(_options) {
  20 + options = _options;
  21 +
  22 + if (options.config) {
  23 + config = options.config;
  24 + }
  25 + else {
  26 + config = require("./config.json");
  27 + }
  28 +
  29 + if (options.logger) {
  30 + logger = options.logger;
  31 + }
  32 + else {
  33 + logger = console;
  34 + }
  35 +
  36 + createResponseServer();
  37 +
  38 + createDiyHttpXmlRpcServer();
  39 + //createXmlRpcServer()
  40 + //createExpressXmlRpcServer();
  41 +}
  42 +
  43 +function createXmlRpcServer() {
  44 + var server = xmlrpc.createServer(config.server_options);
  45 + logger.info("Server listening on port " + config.server_options.port);
  46 +
  47 + server.on('NotFound', function(method, params) {
  48 + logger.warn('Method ' + method + ' does not exist');
  49 + });
  50 +
  51 + server.on('topUpRequest', onTopUpRequest);
  52 + //server.on('topUpInquiry', onTopUpInquiry);
  53 +}
  54 +
  55 +function getXmlRpcParam(values) {
  56 + try {
  57 +
  58 + var count = values.length
  59 + var result = {};
  60 + for (var i = 0; i < count; i++) {
  61 + var value = values[i];
  62 +
  63 + var keys = Object.keys(value.value[0]);
  64 + var firstKey = keys[0];
  65 + result[value.name[0]] = value.value[0][firstKey][0];
  66 + }
  67 +
  68 + return result;
  69 +
  70 + }
  71 + catch(err) {
  72 + return null;
  73 + }
  74 +}
  75 +
  76 +function createDiyHttpXmlRpcServer() {
  77 + var serverOptions = {
  78 + key: fs.readFileSync('./server.key'),
  79 + cert: fs.readFileSync('./server.crt')
  80 + }
  81 +
  82 + var httpServer = https.createServer(serverOptions, function(req, res) {
  83 +
  84 + var body = "";
  85 + req.on('data', function (chunk) {
  86 + body += chunk;
  87 + });
  88 +
  89 + req.on('end', function () {
  90 +
  91 + xml2jsParser(body, function(err, message) {
  92 +
  93 + if (err) {
  94 + res.end('Unknown xml');
  95 + return;
  96 + }
  97 +
  98 + var method;
  99 + var _params;
  100 +
  101 + try {
  102 + method = message.methodCall.methodName[0];
  103 + _params = message.methodCall.params[0].param[0].value[0].struct[0].member;
  104 + }
  105 + catch(errSelectMethod) {
  106 + logger.warn('Failed to get method and _params');
  107 + res.end('Invalid')
  108 + return;
  109 + }
  110 +
  111 + params = getXmlRpcParam(_params);
  112 +
  113 + immediateReply(params, function(errReply, data) {
  114 + var responseBody = composeXmlRpcResponse(data)
  115 + logger.info(responseBody);
  116 +
  117 + res.writeHead(200, {'Content-Type': 'text/xml'});
  118 + res.end(responseBody);
  119 + });
  120 +
  121 + sendToMaster(params, req.connection.remoteAddress);
  122 +
  123 + })
  124 + });
  125 +
  126 + });
  127 +
  128 + httpServer.listen(config.server_options.port, function() {
  129 + logger.info('HTTP XMLRPC listen on port ' + config.server_options.port);
  130 + });
  131 +}
  132 +
  133 +function composeXmlRpcResponse(param) {
  134 +
  135 + var values = [];
  136 + var keys = Object.keys(param);
  137 + var keysCount = keys.length;
  138 +
  139 + for (var i = 0; i < keysCount; i++) {
  140 + var key = keys[i];
  141 + var value = {
  142 + name: key,
  143 + value: {
  144 + string: param[key]
  145 + }
  146 + }
  147 + values.push(value);
  148 + }
  149 +
  150 + var data = {
  151 + methodResponse: {
  152 + params: {
  153 + param: {
  154 + value: {
  155 + struct: {
  156 + member: values
  157 + }
  158 + }
  159 + }
  160 + }
  161 + }
  162 + }
  163 +
  164 + logger.info(JSON.stringify(data));
  165 +
  166 + return xml2jsBuilder.buildObject(data);
  167 +}
  168 +
  169 +function immediateReply(param, callback) {
  170 + var message = 'ISI '
  171 + + param.NOM
  172 + + ' KE '
  173 + + param.NOHP
  174 + + ', Transaksi anda sedang diproses'
  175 +
  176 + var trxId = neoxmlinutil.getTransactionIdFromMessage(message);
  177 +
  178 + var response = {
  179 + 'RESPONSECODE': '68',
  180 + 'REQUESTID': param.REQUESTID,
  181 + 'MESSAGE': message,
  182 + 'TRANSACTIONID': trxId,
  183 + }
  184 +
  185 + //logger.info(response);
  186 + callback(null, response);
  187 +}
  188 +
  189 +function onTopUpRequest(err, params, callback) {
  190 + logger.info('METHOD: topUpRequest');
  191 + logger.info(JSON.stringify(params));
  192 +
  193 + if (config.immediate_reply) {
  194 + immediateReply(params[0], callback);
  195 + }
  196 + else {
  197 + callback();
  198 + }
  199 +}
  200 +
  201 +function onTopUpInquiry(err, params, callback) {
  202 + callback();
  203 +}
  204 +
  205 +function composeMessage(params, remoteAddress) {
  206 + try {
  207 + var nom = params.NOM.replace(/\./g, '').trim();
  208 + var destination = params.NOHP.replace(/\./g, '').trim();
  209 + var pin = params.PIN.replace(/\./g, '').trim();
  210 + var requestId = params.REQUESTID.replace(/\./g, '').trim();
  211 +
  212 + return 'MI.' + nom + '."' + destination + '".' + pin + '.' + requestId + '.NOTRUST."' + remoteAddress + '"';
  213 + }
  214 + catch(err) {
  215 + return;
  216 + }
  217 +
  218 +
  219 +}
  220 +
  221 +function sendToMaster(param, remoteAddress) {
  222 +
  223 + /*
  224 + var smscidSuffix = '99999999999999' + String(Math.round(Math.random() * 99999999999999));
  225 + var smscid = 'XML1' + smscidSuffix.slice(-13);
  226 + */
  227 +
  228 + //var smscid = 'XML' + '12345' + strftime('%H%M%S%L');
  229 + var smscid = config.smscid;// + Math.round(Math.random() * 9999999999999);
  230 +
  231 + var message = composeMessage(param, remoteAddress);
  232 + if (!message) {
  233 + logger.warn('Invalid parameter');
  234 + return;
  235 + }
  236 +
  237 + var requestOpts = {
  238 + url: config.master_url,
  239 + qs: {
  240 + PhoneNumber: param.MSISDN,
  241 + 'Text': message,
  242 + Res_Port: config.res_port,
  243 + Smscid: smscid
  244 + }
  245 + };
  246 +
  247 + logger.info(requestOpts);
  248 + request(requestOpts, function(err, response, body) {
  249 + if (err) {
  250 + logger.warn('Failed to contact master: ' + err);
  251 + setTimeout(sendToMaster, 2000, param, remoteAddress);
  252 + return;
  253 + }
  254 +
  255 + logger.info(body);
  256 + })
  257 +
  258 +}
  259 +
  260 +function sendReply(response) {
  261 + var requestId = neoxmlinutil.getRequestIdFromResponseMessage(response.text);
  262 +
  263 + if (!requestId) {
  264 + logger.warn('No request id found, skipping');
  265 + return;
  266 + }
  267 +
  268 + var params = {
  269 + REQUESTID: requestId,
  270 + RESPONSECODE: response.resp_code,
  271 + MESSAGE: response.text,
  272 + }
  273 +
  274 + logger.info('PARAMS:');
  275 + logger.info(params);
  276 +
  277 + neoxmlinutil.getReverseUrl(response.PhoneNumber, function(err, reverseUrls) {
  278 + if (err) {
  279 + logger.warn('Fail to get reverse urls, skipping');
  280 + return;
  281 + }
  282 +
  283 + if (reverseUrls.length <= 0) {
  284 + logger.warn('No reverse urls found, skipping');
  285 + return;
  286 + }
  287 +
  288 + sendTopUpReport(reverseUrls, params, 0, 4);
  289 + });
  290 +}
  291 +
  292 +function sendTopUpReport(reverseUrls, params, urlIdx, retry) {
  293 + if (retry === null || retry === undefined) {
  294 + retry = 4;
  295 + }
  296 +
  297 + if (urlIdx === null || urlIdx === undefined) {
  298 + urlIdx = 0;
  299 + }
  300 +
  301 + if (urlIdx >= reverseUrls.length) {
  302 + if (retry) {
  303 + logger.info('Retrying to send topUpReport to partner');
  304 + setTimeout(
  305 + sendTopUpReport,
  306 + 10000,
  307 + reverseUrls, 0, --retry
  308 + )
  309 + return;
  310 + }
  311 + else {
  312 + logger.warn('topUpReport retry exceed');
  313 + }
  314 + }
  315 +
  316 + var partnerUrl = url.parse(reverseUrls[urlIdx]);
  317 +
  318 + var clientOptions = {
  319 + host: partnerUrl.hostname
  320 + , port: partnerUrl.port
  321 + , path: partnerUrl.pathname
  322 + };
  323 + logger.info(clientOptions);
  324 +
  325 + var client;
  326 + if (partnerUrl.protocol == 'https:') {
  327 + client = xmlrpc.createSecureClient(clientOptions);
  328 + } else {
  329 + client = xmlrpc.createClient(clientOptions);
  330 + }
  331 +
  332 + var methodName = 'topUpReport';
  333 + logger.info(params);
  334 + client.methodCall(methodName, [ params ], function (error, value) {
  335 + if (!error) {
  336 + return;
  337 + }
  338 +
  339 + sendTopUpReport(reverseUrls, ++urlIdx, retry)
  340 + });
  341 +}
  342 +
  343 +function createResponseServer() {
  344 + var httpServer = http.createServer(function(req, res) {
  345 +
  346 + res.end();
  347 +
  348 + var parsed_url = url.parse(req.url, true, true);
  349 + logger.info(parsed_url.query);
  350 +
  351 + sendReply(parsed_url.query);
  352 +
  353 + });
  354 +
  355 + httpServer.listen(config.res_port, function() {
  356 + logger.info('HTTP Response server listen on port ' + config.res_port);
  357 + });
  358 +}
  359 +
  360 +exports.start = start;