partner.js 5.42 KB
"use strict";

const HTTP = require('http');
const URL = require('url');
const request = require('request');
const uuidv4 = require('uuid/v4');

const config = require('komodo-sdk/config');
const logger = require('komodo-sdk/logger');
const matrix = require('komodo-sdk/matrix');
const pull = require('komodo-sdk/gateway/pull');

const komodoClient = require('./komodo-client');
const partnerRc = require('./komodo-rc');

if (!matrix.pending_tasks) {
    matrix.pending_tasks = {};
}

function deleteFromPendingTasks(trx_id) {
    if (matrix.pending_tasks && matrix.pending_tasks[trx_id]) {
        delete matrix.pending_tasks[trx_id];
    }
}

function report(data) {
    pull.report(data);

    if (data.rc !== '68' && data.rc !== '96') {
        deleteFromPendingTasks(data.trx_id);
    }
}

function _hit(task, is_advice) {

    if (matrix.pending_tasks && !matrix.pending_tasks[task.trx_id]) {
        matrix.pending_tasks[task.trx_id] = task;
    }

    const request_options = {
        url: config.partner.url,
        qs: {
            request_id: task.trx_id,
            terminal_name: config.partner.terminal_name || config.partner.username,
            password: config.partner.password,
            reverse_url: config.reverse_report_url,
            product_name: task.remote_product,
            destination: task.destination
        }
    }

    if (!request_options.qs.request_id || !request_options.qs.terminal_name || !request_options.qs.password || !request_options.qs.reverse_url || !request_options.qs.product_name || !request_options.qs.destination) {
        logger.verbose('Missing parameter on request', request_options.qs);
        return;
    }

    logger.info('Requesting to partner', {is_advice: is_advice, trx_id: task.trx_id, destination: task.destination, product: task.product, remote_product: task.remote_product});
    request(request_options, function(err, res, body) {
        if (err) {
            logger.warn('Error requesting to partner', {err: err, trx_id: task.trx_id, destination: task.destination, is_advice: is_advice});
            report({
                trx_id: task.trx_id,
                rc: (!is_advice && (err.syscall === 'connect')) ? '91' : '68',
                message: 'INTERNAL: REQUEST ERROR: ' + err.toString(),
                misc: {
                    task: task
                }
            });
            return;
        }

        if (res.statusCode != 200) {
            logger.warn('Partner returning non 200 HTTP status code', {trx_id: task.trx_id, destination: task.destination, is_advice: is_advice, http_status_code: res.statusCode, response_body: body});
            report({
                trx_id: task.trx_id,
                rc: '68',
                message: ('INTERNAL: Got non 200 HTTP status code: ' + res.statusCode + '\n\n' + body).trim(),
                raw: body,
                misc: {
                    task: task
                }
            });
            return;
        }

        logger.verbose('Got response from partner', {trx_id: task.trx_id, destination: task.destination, is_advice: is_advice, response_body: body});

        const result = komodoClient.parseResponse(body);
        if (!result) {
            logger.warn('Partner returning invalid JSON', {trx_id: task.trx_id, destination: task.destination, is_advice: is_advice, response_body: body});
            report({
                trx_id: task.trx_id,
                rc: is_advice ? '68' : '90',
                message: 'INTERNAL: Partner return invalid JSON:\n' + body,
                raw: body,
                misc: {
                    task: task
                }
            });
            return;
        }

        report({
            trx_id: task.trx_id,
            rc: result.rc ? partnerRc[result.rc] || '40' : '68',
            message: result.message,
            sn: result.sn,
            amount: Number(result.amount) || null,
            balance: Number(result.ending_balance) || null,
            raw: body,
            misc: {
                task: task
            }
        });
    })
}

function buy(task) {
    _hit(task, false);
}

function advice(task) {
    _hit(task, true);
}

function reverseReportHandler(req, res) {
    const report_id = uuidv4();

    logger.verbose('Incoming reverse report', {report_id: report_id, url: req.url});
    res.end('OK');

    const qs = URL.parse(req.url, true).query;

    if (!qs.request_id || !qs.rc) {
        logger.verbose('No request_id and rc on reverse report message', {report_id: report_id, url: req.url, qs: qs});
        return;
    }

    const task = {
        trx_id: qs.request_id,
        destination: qs.destination,
        remote_product: qs.product_name
    }

    report({
        trx_id: qs.request_id,
        rc: partnerRc[qs.rc] || '40',
        message: qs.message,
        sn: qs.sn,
        amount: qs.amount || null,
        balance: Number(qs.ending_balance) || null,
        raw: req.url,
        misc: {
            task: task
        }
    });
}

function createReverseReportHttpServer() {
    const http_server = HTTP.createServer(reverseReportHandler);
    http_server.listen(config.reverse_report_port, function(err) {
        if (err) {
            logger.warn('Error creating reverse report HTTP server: ' + err.toString());
            process.exit(1);
            return;
        }

        logger.info('Reverse report HTTP server listening on port ' + config.reverse_report_port);
    })
}

createReverseReportHttpServer();

exports.buy = buy;
exports.advice = advice;