partner-bnisp.js 9.16 KB
"use strict";

const http = require('http');
http.globalAgent.maxSockets = Infinity;

const request = require('request');
const resendDelay = require('sate24/resend-delay')

var config;
var aaa;
var logger;

function start(options) {
    if (!options) {
        console.log('Undefined options, terminating....');
        process.exit(1);
    }

    if (options.config) {
        config = options.config;
    } else {
        console.log('Undefined options.config, terminating....')
        process.exit(1);
    }

    if (options.aaa) {
        aaa = options.aaa;
    } else {
        console.log('Undefined options.aaa, terminating....')
        process.exit(1);
    }

    if (options && options.logger) {
        logger = options.logger;
    } else {
        console.log('Undefined options.logger, terminating....')
        process.exit(1);
    }

    resendDelay.init({config: config, logger: logger, topupRequest: topupAdvice});
}

function callbackReport(requestId, rc, message, options) {
    if (rc == '68') {
        resendDelay.register(options.task);
    } else {
        resendDelay.cancel(options.task)
    }

    aaa.callbackReportWithPushToMongoDb(requestId, rc, message);
}

function splitRemoteProduct(remoteProduct) {
    return remoteProduct.replace(/^\s+|\s+$/gm,'').split(/[\/\W]+/);
}

function topupRequest(task) {
    aaa.insertTaskToMongoDb(task);

    const remoteProduct = splitRemoteProduct(task.remoteProduct);

    if (remoteProduct.length < 4) {
        callbackReport(task.requestId, '40', 'INTERNAL_MSG: Invalid remoteProduct', task)
        return;
    }

    let pathParams = {
        request_type: "purchase",
        noid: config.h2h_out.noid,
        token: config.h2h_out.token,
        product: remoteProduct[0],
        product_type: remoteProduct[1],
        id_pel: task.destination,
        nominal: remoteProduct[2],
        admin: remoteProduct[3],
        trace_id: task.requestId
    }

    const requestOptions = {
        url: config.h2h_out.partner.replace(/\/+$/, '') + '/' + createUrlPath(pathParams).replace(/^\/+/, '')
    }

    logger.verbose('Requeting to partner', {requestOptions: requestOptions});
    request(requestOptions, function(error, response, body) {
        if (error) {
            let rc = '68';

            if (!error.syscall == 'connect') {
                rc = '91';
            }

            logger.warn('Error requesting to partner', {task: task, rc: rc, error: error});
            callbackReport(task.requestId, rc, 'INTERNAL_MSG: Error requesting to partner. ' + error, {task: task});
            return;
        }

        if (response.statusCode != 200) {
            let rc = '68';

            logger.warn('HTTP status code is not 200', {task: task, http_status_code: response.statusCode});
            callbackReport(task.requestId, rc, 'INTERNAL_MSG: HTTP status code ' + response.statusCode, {task: task});
            return;
        }

        if (!body) {
            let rc = '68';

            logger.warn('Missing response body', {task: task, responseBody: body});
            callbackReport(task.requestId, rc, 'INTERNAL_MSG: Missing response body', {task: task});
            return;
        }

        if (body.trim() == 'invalid specs') {
            let rc = '40';

            logger.warn('Invalid specs', {task: task, responseBody: body});
            callbackReport(task.requestId, rc, body);
            return;
        }

        logger.verbose('Got response from partner', {task: task, responseBody: body});

        const responseData = parseResponseBody(body);
        logger.verbose('Response body parsed as json value', {responseData: responseData});
        const data = responseDataProcessor(responseData);

        if (data.balance && aaa.updateBalance) {
            aaa.updateBalance(data.balance);
        }

        callbackReport(task.requestId, data.rc, data.combinedMessage, {task: task});
    })

}

function topupAdvice(task) {

    let pathParams = {
        noid: config.h2h_out.noid,
        token: config.h2h_out.token,
        trace_id: task.requestId
    }

    const requestOptions = {
        url: config.h2h_out.partner.replace(/\/+$/, '') + '/' + createUrlPathAdvice(pathParams).replace(/^\/+/, '')
    }

    logger.verbose('Requeting advice to partner', {requestOptions: requestOptions});
    request(requestOptions, function(error, response, body) {
        if (error) {
            let rc = '68';

            logger.warn('Error requesting to advice partner', {task: task, rc: rc, error: error});
            callbackReport(task.requestId, rc, 'INTERNAL_MSG: Error requesting advice to partner. ' + error, {task: task});
            return;
        }

        if (response.statusCode != 200) {
            let rc = '68';

            logger.warn('Advice HTTP status code is not 200', {task: task, http_status_code: response.statusCode});
            callbackReport(task.requestId, rc, 'INTERNAL_MSG: Advice HTTP status code ' + response.statusCode, {task: task});
            return;
        }

        if (!body) {
            let rc = '68';

            logger.warn('Missing advice response body', {task: task, responseBody: body});
            callbackReport(task.requestId, rc, 'INTERNAL_MSG: Missing advice response body', {task: task});
            return;
        }

        if (body.trim() == 'invalid specs') {
            let rc = '68';

            logger.warn('Invalid specs', {task: task, responseBody: body});
            callbackReport(task.requestId, rc, body);
            return;
        }

        logger.verbose('Got advice response from partner', {task: task, responseBody: body});

        const responseData = parseResponseBody(body);
        logger.verbose('Advice response body parsed as json value', {responseData: responseData});
        const data = responseDataProcessor(responseData);

        if (data.balance && aaa.updateBalance) {
            aaa.updateBalance(data.balance);
        }

        callbackReport(task.requestId, data.rc, data.combinedMessage, {task: task});
    })

}

function createUrlPath(options) {
    let urlPath = [
        "get",
        options.request_type || "purchase",
        "json",
        options.noid,
        options.token,
        options.product,
        options.product_type,
        options.id_pel,
        options.nominal || 0,
        options.admin || 0,
        options.trace_id
    ].join('/');

    return '/' + urlPath;
}

function createUrlPathAdvice(options) {
    // pattern: /get/advice/json/[noid]/[token]/[traceid]

    let urlPath = [
        "get",
        "advice",
        "json",
        options.noid,
        options.token,
        options.trace_id
    ].join('/');

    return '/' + urlPath;
}

function parseResponseBody(body) {
    let data;

    try {
        data = JSON.parse(body);
    }
    catch(e) {
        if (logger) {
            logger.warn('Exception on parsing result body: ' + e);
            return;
        }
    }

    return data;
}

function responseDataProcessor(responseData) {
    let retval = {
        rc: '68',
        sn: '',
        responseMessage: '',
        combinedMessage: '',
        amount: 0,
        balance: 0,
        saldo: 0,
        ts: '',
        loadTime: 0,
        product: '',
        productType: ''
    }

    let combinedMessage = [];

    if (responseData.response_code == '0000') {
        retval.rc = '00';
    }
    else if (responseData.response_code == '0010') {
        retval.rc = '14';
    }
    else if (responseData.response_code == '0021') {
        retval.rc = '14';
    }
    else if (['9990','0099','0068','0063','0005','9997','9999','9998','9990','0600','1003'].indexOf(responseData.response_code) >= 0) {
        retval.rc = '68';
    }
    else {
        retval.rc = '40';
    }

    if (responseData.response_code) {
        combinedMessage.push(responseData.response_code);
    }

    if (responseData.product) {
        combinedMessage.push(responseData.product)
    }

    if (responseData.produk_tipe) {
        combinedMessage.push(responseData.produk_tipe)
    }

    if (responseData.idpel) {
        combinedMessage.push(responseData.idpel)
    }

    retval.responseMessage = responseData.response_message || '';
    if (retval.responseMessage) {
        combinedMessage.push(retval.responseMessage);
    }

    if (responseData.detail) {
        retval.sn = responseData.detail.voucherSerialNumber || '';
    }

    retval.amount = responseData.amount || 0;
    combinedMessage.push('Amount: ' + retval.amount);

    retval.balance = responseData.saldo || 0;
    retval.saldo = retval.balance;
    combinedMessage.push('Balance: ' + retval.balance);

    retval.ts = responseData.waktu || '';
    combinedMessage.push(retval.ts);

    retval.loadTime = responseData.loadTime || '';
    combinedMessage.push('Load time: ' + retval.loadTime);

    retval.combinedMessage = combinedMessage.join(' | ');

    if (retval.sn) {
        retval.combinedMessage = 'SN=' + retval.sn + '; ' + retval.combinedMessage;
    }

    return retval;
}

exports.start = start;
exports.topupRequest = topupRequest;
exports.topupAdvice = topupAdvice;
exports.createUrlPath = createUrlPath;
exports.createUrlPathAdvice = createUrlPathAdvice;
exports.parseResponseBody = parseResponseBody;
exports.responseDataProcessor = responseDataProcessor;
exports.splitRemoteProduct = splitRemoteProduct;