gentong.js 10.1 KB
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 redis = require('redis');

var Router = require('node-simple-router');

var winston = require('winston');

var logger;
var config;
var httpServer;
var redisClient;

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

var aaa;

var logTag = __filename.split('/').reverse()[0];

function getRedisKey(timestamp) {
    var prefix = config.globals.gateway_name;
    if (config.globals.redis_prefix) {
        prefix = config.globals.redis_prefix;
    }
    return prefix + '.ts:' + timestamp + '.rid';
}

function generateTimestamp(request_id) {
    var ts = strftime('%F %T', new Date());

    var key = getRedisKey(ts);
    redisClient.set(key, request_id);
    redisClient.expire(key, 3600*48);

    return ts;
}

function topupRequest(task) {
    var ts = strftime('%F %T', new Date());
    ts = generateTimestamp(task['requestId']);

    var options = {
        url: config.h2h_out.partner,
        qs: {
            code: task['remoteProduct'],
            msisdn: task['destination'],
            user_id: config.h2h_out.userid,
            password: config.h2h_out.password,
            ts: ts
        }
    };

    logger.info('Creating http request to gateway', {options: options});

    if (aaa) {
        aaa.incrementTrxCount();
        aaa.incrementActiveTrxCount();
    }

    request(options, function (error, response, body) {
        aaa.decrementActiveTrxCount();

        if (error) {

            var error_message = 'Error on http connection to gateway: ' + error;
            logger.warn(error_message);
            callbackReport(task['requestId'], '91', error_message);
            return;

        }

        if (response.statusCode != 200) {

            var error_message = 'Gateway error, http response code: ' + response.statusCode;
            logger.warn(error_message);
            callbackReport(task['requestId'], '91', error_message);
            return;
        }

        var responseCode = 40;
        var responseMessage;

        xml2js(body, function (err, result) {
            if (err) {
                logger.warn('Error parsing XML', {response_error: err, response_body: body});
                callbackReport(task['requestId'], '40', body);
                return;
            }

            logger.info('Got direct response from request', {result: result});

            try {
                responseMessage =  result.direct_ack.info[0];

                if (result.direct_ack.request_status[0] == 'OK') {
                    responseCode = 68;
                } else {
                    responseCode = 40;

                    var new_response_code = responseCodeFromMessage(responseMessage);
                    if (new_response_code) {
                        responseCode = new_response_code;
                    }

                }

            }
            catch(err) {
                logger.warn('Exception on parsing xml response');
                responseCode = 40;
                responseMessage = 'Invalid xml response from gateway';
            }

            callbackReport(task['requestId'], responseCode, responseMessage);

        });

    });
}

function createRedisClient() {
    redisClient = redis.createClient(config.globals.redis_port, config.globals.redis_host);
}

function paddingSN(sn, _config) {

    if (!_config) {
        _config = config;
    }

    if (_config.h2h_out.sn_min_length && (sn.length < Number(_config.h2h_out.sn_min_length))) {
        sn = '0000000000000000' + sn;
        sn = sn.slice(Number(_config.h2h_out.sn_min_length) * -1);
    }
    return sn;
}

function parseSN(message, _config) {

    if (!_config) {
        _config = config;
    }

    var sn_regex = new RegExp(_config.h2h_out.sn_pattern);
    var sn_match = message.match(sn_regex);

    if (sn_match <= 0) {
        logger.info('SN Not found: ' + message);
        return '';
    }

    var match_index = 0;
    if (_config.h2h_out.sn_match_index) {
        match_index = Number(_config.h2h_out.sn_match_index)
    }

    var sn = sn_match[match_index];

    if (_config.h2h_out.sn_remove_whitespace) {
        sn = sn.replace(/\s/g, '');
    }

    var sn_remove_patterns = _config.h2h_out.sn_remove_patterns.split(_config.h2h_out.sn_remove_patterns_separator);

    var count = sn_remove_patterns.length;

    for(var i = 0; i < count; i++) {

        //sn = sn.replace(sn_remove_patterns[i], '');

        var re = new RegExp(sn_remove_patterns[i], 'g');
        sn = sn.replace(re, '');
    }

    sn = paddingSN(sn, _config);

    return sn.trim();
}

function createServer() {
    var httpServer = http.createServer(function(request, response) {

        var response_code = '68';
        var sn = '';

        var qs = url.parse(request.url, true).query;

        logger.info('Got reverse report from gateway', {qs: qs});

        if (qs.topup_status == 'S') {
            response_code = '00';
            if (qs.sn && !config.h2h_out.force_parse_sn && !Number(config.h2h_out.force_parse_sn)) {
                sn = qs.sn;
            } else {
                logger.warn('Missing SN from query string. Trying to get SN from message');
                sn = parseSN(qs.info);
            }

            if (config.h2h_out.sn_split_index) {
                sn = splitSN(sn, config);
            }

            if (sn) {
                sn = paddingSN(sn, config);
            }

        } else if (qs.topup_status == 'R') {

            response_code = '40';

        }

        try {

            var new_response_code = responseCodeFromMessage(qs.info);
            if (new_response_code) {
                response_code = new_response_code;
            }

        }
        catch(err) {
            logger.warn('Exception on parsing reverse report', {exception: err} );
            response_code = '40';
        }

        message = qs.info;
        //updateBalance(message);
        if (sn) {
            message = 'SN=' + sn + '; ' + message;
        }

        response.end('OK');

        var key = getRedisKey(qs.ts);
        redisClient.get(key, function(err, request_id) {
            if (err) {
                logger.warn('Error when requesting request id for ts:' + qs.ts + ' (' + key + ')', {redis_error: err});
                return;
            }

            callbackReport(request_id, response_code, message);
        });
    });

    httpServer.listen(config.h2h_out.listen_port, function() {
        logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port);
    });
}

function splitSN(sn, _config) {
    var sn_pieces = sn.split(' ');

    if (sn_pieces.length <= 0) {
        logger.info('Returning original SN because SN only has one element');
        return sn;
    }

    if (!_config.h2h_out.sn_split_index) {
        logger.info('Returning original SN because config.h2h_out.sn_split_index undefined');
        return sn;
    }
    var sn_indexes = _config.h2h_out.sn_split_index.split(',');

    logger.info('Split SN', {sn_pieces: sn_pieces, sn_indexes: sn_indexes});

    var _sn = '';

    var id_count = sn_indexes.length;
    for(var i = 0; i < id_count; i++) {

        var sn_index = sn_indexes[i];
        var sn_piece = sn_pieces[sn_index];

        if (sn_pieces[i]) {
            _sn = _sn + sn_piece;
        } else {
            logger.warn('Undefined value on sn piece ' + sn_index);
        }
    }

    _sn = _sn.trim();

    if (_sn) {
        sn = _sn;
        logger.info('Got new SN: ' + sn);
    } else {
        logger.warn('Got empty SN when using split SN. Use original SN');
    }

    return sn;
}

function responseCodeFromMessage(message) {
    if (message.toLowerCase().indexOf('nomor salah/tidak terdaftar') >= 0) {
        return '14';
    }
    else if (message.toLowerCase().indexOf('nomor tidak di temukan/tidak aktif') >= 0) {
        return '14';
    }
    else if (message.toLowerCase().indexOf('kode produk tidak sesuai nomor tujuan') >= 0) {
        return '14';
    }
    else if (message.toLowerCase().indexOf('nomor yang anda masukan salah') >= 0) {
        return '14';
    }
    else if (message.toLowerCase().indexOf('nomor yang anda masukkan salah') >= 0) {
        return '14';
    }
    else if (message.toLowerCase().indexOf('nomor telepon seluler salah') >= 0) {
        return '14';
    }
    else if (message.toLowerCase().indexOf('bulk or forbidden request') >= 0) {
        return '55';
    }
    else if (message.toLowerCase().indexOf('sudah pernah dilakukan') >= 0) {
        return '55';
    }
    else if (message.toLowerCase().indexOf('transaksi yg sama sudah pernah dilakukan tunggu dlm') >= 0) {
        return '55';
    }
    else if (message.toLowerCase().indexOf('transaksi tsb sudah pernah dilakukan') >= 0) {
        return '55';
    }
    else if (message.toLowerCase().indexOf('mohon maaf saat ini stock belum tersedia') >= 0) {
        return '13';
    }
    else if (message.toLowerCase().indexOf('stock tidak tersedia') >= 0) {
        return '13';
    }
    else if (message.toLowerCase().indexOf('tidak tersedia atau nonaktif') >= 0) {
        return '13';
    }
    else if (message.toLowerCase().indexOf('saldo di account anda saat ini tidak mencukupi') >= 0) {
        return '40';
    }
    else if (message.toLowerCase().indexOf('tidak mencukupi untuk melakukan transaksi') >= 0) {
        return '40';
    }
    else if (message.search(/stok product .* tidak tersedia ke nomor .* dari pilihan2 yg tersedia/i) >= 0) {
        return '13';
    }
    else if (message.search(/stok produk .* gangguan ke no tujuan/i) >= 0) {
        return '13';
    }

    return;
}

function start(_config, _callbackReport, options) {
    config = _config;
    callbackReport = _callbackReport

    if (options && options.aaa) {
            aaa = options.aaa;
    }

    if (options && options.logger) {
        logger = options.logger;
    } else {
        logger = new winston.Logger({
            transports: [
              new (winston.transports.Console)()
            ]
        });
    }

    createRedisClient();
    createServer();
}

exports.start = start;
exports.topupRequest = topupRequest;
exports.parseSN = parseSN;