"use strict"; process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; const fs = require('fs'); const url = require('url'); const https = require('https'); const xmlrpc = require('xmlrpc'); const moment = require('moment'); const stringify = require("json-stringify-pretty-compact"); 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 resendDelay = require('komodo-sdk/gateway/resend-delay'); const st24 = require('./st24'); if (config.partner.use_sslv3) { https.globalAgent.options.secureProtocol = 'SSLv3_method'; } const partnerRc = fs.existsSync(__dirname + '/../rc-local.json') ? require('../rc-local.json') : require('./partner-rc.json'); logger.verbose('Partner RC dictionary loaded', {partner_rc: partnerRc}); const RESPONSECODE_TAG = config.responsecode_tag ? config.responsecode_tag : 'RESPONSECODE'; function komodoRc(rc) { return (config.partner.override_rc ? config.partner.override_rc[rc] : null) || partnerRc[rc] || '40'; } function createXmlRpcClient(endpoint) { const partnerUrl = url.parse(endpoint); const clientOptions = { host: partnerUrl.hostname, port: partnerUrl.port, path: partnerUrl.pathname }; logger.verbose('Creating XML-RPC client using ' + partnerUrl.protocol, clientOptions); return (partnerUrl.protocol === 'https:') ? xmlrpc.createSecureClient(clientOptions) : xmlrpc.createClient(clientOptions); } function buy(task) { _topUpRequest(task); } function _topUpRequest(task, isAdvice) { const params = { MSISDN: config.partner.msisdn || config.partner.userid, REQUESTID: task.trx_id.toString(), PIN: config.partner.pin || config.partner.password, NOHP: task.destination, NOM: task.remote_product }; const xmlrpcMethod = 'topUpRequest'; logger.info('Preparing XMLRPC request', {method: xmlrpcMethod, params: params, partnerUrl: config.partner.url}); const client = createXmlRpcClient(config.partner.url); client.methodCall(xmlrpcMethod, [ params ], function (err, value) { if (err) { const msg = `XMLRPC Client Error: ${err}. HTTP status code: ${err && err.res && err.res.statusCode ? err.res.statusCode : '-'}. Raw response body: ${err.body}`; let rc = '68'; if ( !isAdvice && ( err.code === 'ECONNREFUSED' || err.code === 'EHOSTUNREACH' || (err.code === 'ETIMEDOUT' && err.syscall === "connect") || (err.code === 'EPROTO' && err.syscall === "write") ) ) { rc = '91'; } logger.warn(msg, {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, err: err}); report({ trx_id: task.trx_id, rc: rc, message: 'topUpRequest INTERNAL ERROR: ' + msg, misc: { task: task } }); return; } logger.info('Got XMLRPC response from partner for', {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, response: value}); matrix.last_topupRequest_ack = value; report({ trx_id: task.trx_id, rc: komodoRc(value[RESPONSECODE_TAG]) || '40', message: 'topUpRequest: ' + stringify(value), sn: (value.SN || '').replace(/;$/, '') || st24.extractSnFromMessage(value.MESSAGE, config.sn_pattern), amount: value.PRICE || st24.extractPriceFromMsg(value.MESSAGE, config.amount_pattern), balance: st24.extractBalanceFromMsg(value.MESSAGE, config.balance_pattern), raw: value, misc: { task: task } }); }); } function _topUpInquiry(task) { const params = { REQUESTID: task.trx_id.toString(), MSISDN: config.partner.msisdn || config.partner.userid, PIN: config.partner.pin || config.partner.password, NOHP: task.destination }; const xmlrpcMethod = 'topUpInquiry'; logger.info('Preparing XMLRPC request', {method: xmlrpcMethod, params: params, partnerUrl: config.partner.url}); const client = createXmlRpcClient(config.partner.url); client.methodCall(xmlrpcMethod, [ params ], function (err, value) { if (err) { const msg = 'XMLRPC Client Error: ' + err; logger.warn(msg, {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, err: err}); report({ trx_id: task.trx_id, rc: '68', message: 'topUpInquiry INTERNAL ERROR: ' + msg, misc: { task: task } }); return; } logger.info('Got XMLRPC response from partner for', {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, response: value}); //matrix.last_topupRequest_ack = value; report({ trx_id: task.trx_id, rc: (value[RESPONSECODE_TAG] && (komodoRc(value[RESPONSECODE_TAG]) || '40')) || '68', message: 'topUpInquiry: ' + stringify(value), sn: (value.SN || '').replace(/;$/, '') || st24.extractSnFromMessage(value.MESSAGE, config.sn_pattern), amount: value.PRICE || st24.extractPriceFromMsg(value.MESSAGE, config.amount_pattern), balance: st24.extractBalanceFromMsg(value.MESSAGE, config.balance_pattern), raw: value, misc: { task: task } }); }); } function advice(task) { if (config && config.advice_is_not_allowed) { return; } if (config && config.advice_max_age_ms) { if (moment() - moment(task.created) > config.advice_max_age_ms) { logger.verbose('Ignoring advice request because of expired task', {trx_id: task.trx_id, destination: task.destination, product: task.product, created: task.created, max_age: config.advice_max_age_ms}); return; } } if (config && config.advice_is_topuprequest) { _topUpRequest(task, true); } else { _topUpInquiry(task); } } function report(data) { if (!data) { return; } if (config && config.force_all_to_pending) { data.rc = '68'; } matrix.last_report_to_core = data; pull.report(data); if (!resendDelay.isEnabled()) { //logger.verbose('Skipping resend delay because resend delay has not configured yet', {trx_id: task.trx_id, destination: task.destination, product: task.product}); return; } if (data.rc !== '68') { logger.verbose('Canceling resend delay', {trx_id: data.trx_id}) resendDelay.cancel(data.trx_id); return; } if (!data.misc || !data.misc.task || typeof data.misc.task !== 'object') { return; } const task = data.misc.task; logger.verbose('Registering resend delay', {trx_id: task.trx_id, destination: task.destination, product: task.product}) resendDelay.register(task, advice); } exports.buy = buy; exports.advice = advice; exports.report = report; exports.komodoRc = komodoRc; exports.RESPONSECODE_TAG = RESPONSECODE_TAG;