xmlrpc-server.js 8.51 KB
var xmlrpc = require('xmlrpc');
var request = require('request');
var neoxmlinutil = require('./neoxmlinutil');
var http = require('http');
var https = require('https');
var fs = require('fs');
var xml2js = new require('xml2js');
var xml2jsParser = require('xml2js').parseString;
var xml2jsBuilder = new xml2js.Builder();
var url = require("url");
var strftime = require('strftime');

var options;
var config;
var logger;

var server;

function start(_options) {
    options = _options;

    if (options.config) {
        config = options.config;
    }
    else {
        config = require("./config.json");
    }

    if (options.logger) {
        logger = options.logger;
    }
    else {
        logger = console;
    }

    createResponseServer();

    createDiyHttpXmlRpcServer();
    //createXmlRpcServer()
    //createExpressXmlRpcServer();
}

function createXmlRpcServer() {
    var server = xmlrpc.createServer(config.server_options);
    logger.info("Server listening on port " + config.server_options.port);

    server.on('NotFound', function(method, params) {
        logger.warn('Method ' + method + ' does not exist');
    });

    server.on('topUpRequest', onTopUpRequest);
    //server.on('topUpInquiry', onTopUpInquiry);
}

function getXmlRpcParam(values) {
    try {

        var count = values.length
        var result = {};
        for (var i = 0; i < count; i++) {
            var value = values[i];

            var keys = Object.keys(value.value[0]);
            var firstKey = keys[0];
            result[value.name[0]] = value.value[0][firstKey][0];
        }

        return result;

    }
    catch(err) {
        return null;
    }
}

function createDiyHttpXmlRpcServer() {
    var serverOptions = {
        key: fs.readFileSync('./server.key'),
        cert: fs.readFileSync('./server.crt')
    }

    var httpServer = https.createServer(serverOptions, function(req, res) {

        var body = "";
        req.on('data', function (chunk) {
            body += chunk;
        });

        req.on('end', function () {

            xml2jsParser(body, function(err, message) {

                if (err) {
                    res.end('Unknown xml');
                    return;
                }

                var method;
                var _params;

                try {
                    method = message.methodCall.methodName[0];
                    _params = message.methodCall.params[0].param[0].value[0].struct[0].member;
                }
                catch(errSelectMethod) {
                    logger.warn('Failed to get method and _params');
                    res.end('Invalid')
                    return;
                }

                params = getXmlRpcParam(_params);

                immediateReply(params, function(errReply, data) {
                    var responseBody = composeXmlRpcResponse(data)
                    logger.info(responseBody);

                    res.writeHead(200, {'Content-Type': 'text/xml'});
                    res.end(responseBody);
                });

                sendToMaster(params, req.connection.remoteAddress);

            })
        });

    });

    httpServer.listen(config.server_options.port, function() {
        logger.info('HTTP XMLRPC listen on port ' + config.server_options.port);
    });
}

function composeXmlRpcResponse(param) {

    var values = [];
    var keys = Object.keys(param);
    var keysCount = keys.length;

    for (var i = 0; i < keysCount; i++) {
        var key = keys[i];
        var value = {
            name: key,
            value: {
                string: param[key]
            }
        }
        values.push(value);
    }

    var data = {
        methodResponse: {
            params: {
                param: {
                    value: {
                        struct: {
                            member: values
                        }
                    }
                }
            }
        }
    }

    logger.info(JSON.stringify(data));

    return xml2jsBuilder.buildObject(data);
}

function immediateReply(param, callback) {
    var message = 'ISI '
        + param.NOM
        + ' KE '
        + param.NOHP
        + ', Transaksi anda sedang diproses'

    var trxId = neoxmlinutil.getTransactionIdFromMessage(message);

    var response = {
        'RESPONSECODE': '68',
        'REQUESTID': param.REQUESTID,
        'MESSAGE': message,
        'TRANSACTIONID': trxId,
    }

    //logger.info(response);
    callback(null, response);
}

function onTopUpRequest(err, params, callback) {
    logger.info('METHOD: topUpRequest');
    logger.info(JSON.stringify(params));

    if (config.immediate_reply) {
        immediateReply(params[0], callback);
    }
    else {
        callback();
    }
}

function onTopUpInquiry(err, params, callback) {
    callback();
}

function composeMessage(params, remoteAddress) {
    try {
        var nom = params.NOM.replace(/\./g, '').trim();
        var destination = params.NOHP.replace(/\./g, '').trim();
        var pin = params.PIN.replace(/\./g, '').trim();
        var requestId = params.REQUESTID.replace(/\./g, '').trim();

        return 'MI.' + nom + '."' + destination + '".' + pin + '.' + requestId + '.NOTRUST."' + remoteAddress + '"';
    }
    catch(err) {
        return;
    }


}

function sendToMaster(param, remoteAddress) {

    /*
    var smscidSuffix = '99999999999999' + String(Math.round(Math.random() * 99999999999999));
    var smscid = 'XML1' + smscidSuffix.slice(-13);
    */

    //var smscid = 'XML' + '12345' + strftime('%H%M%S%L');
    var smscid = config.smscid;// + Math.round(Math.random() * 9999999999999);

    var message = composeMessage(param, remoteAddress);
    if (!message) {
        logger.warn('Invalid parameter');
        return;
    }

    var requestOpts = {
		url: config.master_url,
		qs: {
			PhoneNumber: param.MSISDN,
			'Text': message,
			Res_Port: config.res_port,
			Smscid: smscid
		}
	};

    logger.info(requestOpts);
    request(requestOpts, function(err, response, body) {
        if (err) {
            logger.warn('Failed to contact master: ' + err);
            setTimeout(sendToMaster, 2000, param, remoteAddress);
            return;
        }

        logger.info(body);
    })

}

function sendReply(response) {
    var requestId = neoxmlinutil.getRequestIdFromResponseMessage(response.text);

    if (!requestId) {
        logger.warn('No request id found, skipping');
        return;
    }

    var params = {
        REQUESTID: requestId,
        RESPONSECODE: response.resp_code,
        MESSAGE: response.text,
    }

    logger.info('PARAMS:');
    logger.info(params);

    neoxmlinutil.getReverseUrl(response.PhoneNumber, function(err, reverseUrls) {
        if (err) {
            logger.warn('Fail to get reverse urls, skipping');
            return;
        }

        if (reverseUrls.length <= 0) {
            logger.warn('No reverse urls found, skipping');
            return;
        }

        sendTopUpReport(reverseUrls, params, 0, 4);
    });
}

function sendTopUpReport(reverseUrls, params, urlIdx, retry) {
    if (retry === null || retry === undefined) {
        retry = 4;
    }

    if (urlIdx === null || urlIdx === undefined) {
        urlIdx = 0;
    }

    if (urlIdx >= reverseUrls.length) {
        if (retry) {
            logger.info('Retrying to send topUpReport to partner');
            setTimeout(
                sendTopUpReport,
                10000,
                reverseUrls, 0, --retry
            )
            return;
        }
        else {
            logger.warn('topUpReport retry exceed');
        }
    }

    var partnerUrl = url.parse(reverseUrls[urlIdx]);

    var clientOptions = {
        host: partnerUrl.hostname
        , port: partnerUrl.port
        , path: partnerUrl.pathname
    };
    logger.info(clientOptions);

    var client;
    if (partnerUrl.protocol == 'https:') {
        client = xmlrpc.createSecureClient(clientOptions);
    } else {
        client = xmlrpc.createClient(clientOptions);
    }

    var methodName = 'topUpReport';
    logger.info(params);
    client.methodCall(methodName, [ params ], function (error, value) {
        if (!error) {
            return;
        }

        sendTopUpReport(reverseUrls, ++urlIdx, retry)
    });
}

function createResponseServer() {
    var httpServer = http.createServer(function(req, res) {

        res.end();

        var parsed_url = url.parse(req.url, true, true);
        logger.info(parsed_url.query);

        sendReply(parsed_url.query);

    });

    httpServer.listen(config.res_port, function() {
        logger.info('HTTP Response server listen on port ' + config.res_port);
    });
}

exports.start = start;