gentong.js 9.91 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.indexOf('Nomor salah/tidak terdaftar') >= 0) {
        return '14';
    }
    else if (message.indexOf('Nomor tidak di temukan/tidak aktif') >= 0) {
        return '14';
    }
    else if (message.indexOf('Kode produk tidak sesuai nomor tujuan') >= 0) {
        return '14';
    }
    else if (message.indexOf('nomor yang anda masukan salah') >= 0) {
        return '14';
    }
    else if (message.indexOf('Nomor Telepon seluler salah') >= 0) {
        return '14';
    }
    else if (message.indexOf('bulk or forbidden request') >= 0) {
        return '55';
    }
    else if (message.indexOf('Sudah pernah dilakukan') >= 0) {
        return '55';
    }
    else if (message.indexOf('transaksi yg sama sudah pernah dilakukan tunggu dlm') >= 0) {
        return '55';
    }
    else if (message.indexOf('Transaksi tsb sudah pernah dilakukan') >= 0) {
        return '55';
    }
    else if (message.indexOf('Mohon maaf saat ini stock belum tersedia') >= 0) {
        return '13';
    }
    else if (message.indexOf('Stock tidak tersedia') >= 0) {
        return '13';
    }
    else if (message.indexOf('Stok tidak tersedia') >= 0) {
        return '13';
    }
    else if (message.indexOf('Saldo di account anda saat ini tidak mencukupi') >= 0) {
        return '40';
    }
    
    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;