Commit 2c73365ae40ffd910a0402af671fbccbe9a349d3

Authored by Adhidarma Hadiwinoto
1 parent f2bf78e38a
Exists in master

ready to test

Showing 2 changed files with 287 additions and 0 deletions Side-by-side Diff

... ... @@ -21,8 +21,11 @@
21 21 "author": "Adhidarma Hadiwinoto <gua@adhisimon.org>",
22 22 "license": "ISC",
23 23 "dependencies": {
  24 + "redis": "^2.6.0-2",
  25 + "request": "^2.72.0",
24 26 "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git",
25 27 "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git",
  28 + "strftime": "^0.9.2",
26 29 "winston": "^2.2.0"
27 30 }
28 31 }
partner-masterpulsa-voucher.js
... ... @@ -0,0 +1,284 @@
  1 +var winston = require('winston');
  2 +var request = require('request');
  3 +var strftime = require('strftime');
  4 +var crypto = require('crypto');
  5 +var redis = require('redis');
  6 +
  7 +var config;
  8 +var callbackReport;
  9 +var aaa;
  10 +var logger;
  11 +var options;
  12 +var redisClient;
  13 +
  14 +var adviceDelay = 10000;
  15 +
  16 +function createRedisClient() {
  17 + redisClient = redis.createClient(config.globals.redis_port, config.globals.redis_host);
  18 +}
  19 +
  20 +function start(_config, _callbackReport, options) {
  21 + config = _config;
  22 + callbackReport = _callbackReport
  23 +
  24 + if (options && options.aaa) {
  25 + aaa = options.aaa;
  26 + }
  27 +
  28 + if (options && options.logger) {
  29 + logger = options.logger;
  30 + } else {
  31 + logger = new winston.Logger({
  32 + transports: [
  33 + new (winston.transports.Console)()
  34 + ]
  35 + });
  36 + }
  37 +
  38 + createRedisClient();
  39 +}
  40 +
  41 +function getRedisKey(task) {
  42 + return config.globals.gateway_name + '.tid:' + task.requestId;
  43 +}
  44 +
  45 +function putTaskToRedis(task) {
  46 + var redisKey = getRedisKey(task);
  47 + var taskInJSON = JSON.stringify(task);
  48 +
  49 + redisClient.set(redisKey, taskInJSON);
  50 + redisClient.expire(redisKey, 3600*24*7);
  51 +}
  52 +
  53 +function topupRequest(task, retry) {
  54 + var redisKey = getRedisKey(task);
  55 +
  56 + redisClient.get(redisKey, function(err, result) {
  57 + if (err || !result) {
  58 +
  59 + //logger.info('Redis error or not found', {redisKey: redisKey, error: err, result: result});
  60 + putTaskToRedis(task);
  61 + payVoucher(task);
  62 +
  63 + } else {
  64 + advice(task);
  65 + }
  66 + });
  67 +}
  68 +
  69 +function calculateSignature(cid, secret, dt) {
  70 + return crypto.createHash('sha256').update(cid + dt + secret).digest().toString('hex');
  71 +}
  72 +
  73 +function parsePaymentResponse(message) {
  74 + var data = message.split('#');
  75 + var retval = {
  76 + raw: message
  77 + };
  78 +
  79 + if (data[0] == 'ERROR') {
  80 + retval = {
  81 + status: data[0],
  82 + rc: data[1],
  83 + rcmessage: data[2],
  84 + }
  85 +
  86 + } else {
  87 +
  88 + var i = 0;
  89 + retval = {
  90 + status: data[i++],
  91 + rc: data[i++],
  92 + rcmessage: data[i++],
  93 + resptext: data[i++],
  94 + dt: data[i++],
  95 + refnum: data[i++],
  96 + voucherid: data[i++],
  97 + nominal: data[i++]
  98 + }
  99 + }
  100 +
  101 + return retval;
  102 +}
  103 +
  104 +function reportPaymentSuccess(task, response) {
  105 + var message = 'SN=' + response.refnum + '; ' + response.raw;
  106 +
  107 + logger.info('Report payment success to ST24', {task: task, response: response});
  108 +
  109 + callbackReport(task.requestId, '00', message);
  110 +}
  111 +
  112 +function reportPaymentError(task, response) {
  113 + var errorCode = getErrorCode(response.rcmessage);
  114 + var st24rc = getST24ResponseCode(errorCode);
  115 +
  116 + if (st24rc == '68') {
  117 + logger.info('Got pending response, requesting advice in ' + adviceDelay + 'ms', {task: task, response: response});
  118 + setTimeout(advice, adviceDelay, task);
  119 + }
  120 +
  121 + logger.info('Report payment error/pending to ST24', {supplier_rc: errorCode, st24_rc: st24rc, task: task, response: response});
  122 +
  123 + callbackReport(
  124 + task.requestId,
  125 + getST24ResponseCode(errorCode),
  126 + response.raw
  127 + );
  128 +}
  129 +
  130 +function getST24ResponseCode(supplierResponseCode) {
  131 + var st24rc = '40';
  132 +
  133 + if (supplierResponseCode.length == 1) {
  134 + supplierResponseCode = '0' + supplierResponseCode;
  135 + }
  136 +
  137 + if (['00', '13', '14', '47', '68'].indexOf(supplierResponseCode) >= 0) {
  138 + st24rc = supplierResponseCode;
  139 + }
  140 + else if (supplierResponseCode == '15') {
  141 + st24rc = '14';
  142 + }
  143 + else if (['05', '18', '63', '68'].indexOf(supplierResponseCode) >= 0) {
  144 + st24rc = '68';
  145 + }
  146 + else if (supplierResponseCode == '67') {
  147 + st24rc = '91'
  148 + }
  149 + else if (supplierResponseCode == '46') {
  150 + st24rc = '40'
  151 +
  152 + if (aaa && config && config.globals && config.globals.pause_on_not_enough_balance
  153 + && (config.globals.pause_on_not_enough_balance == '1')) {
  154 +
  155 + logger.warn('Not enough balance detected. Going to pause the system.');
  156 + aaa.pause();
  157 + }
  158 + }
  159 +
  160 + return st24rc;
  161 +}
  162 +
  163 +function getErrorCode(rcmessage) {
  164 + try {
  165 + var errorCode = rcmessage.match(/\[(\d+)\]/);
  166 + return errorCode[1];
  167 + }
  168 + catch(err) {
  169 + logger.warn('Empty RCMESSAGE, returning 68 as RC for safety');
  170 + return '68';
  171 + }
  172 +}
  173 +
  174 +function generateDt(taskTimestamp) {
  175 + if (!taskTimestamp) {
  176 + return strftime('%Y%m%d', new Date());
  177 + }
  178 +
  179 + return taskTimestamp.slice(0, 8);
  180 +}
  181 +
  182 +function generateRequestOptions(userid, password, partnerUrl, task) {
  183 + var dt = generateDt(task.timestamp);
  184 + var sign = calculateSignature(userid, password, dt);
  185 +
  186 + var requestOptions = {
  187 + method: 'GET',
  188 + url: partnerUrl,
  189 + qs: {
  190 + modul: '',
  191 + command: '',
  192 + tujuan: task['destination'],
  193 + voucherid: task['remoteProduct'],
  194 + cid: userid,
  195 + dt: dt,
  196 + hc: sign,
  197 + trxid: task['requestId'],
  198 + }
  199 + }
  200 +
  201 + return requestOptions;
  202 +}
  203 +
  204 +function advice(task, retry) {
  205 +
  206 + if (retry === null || retry === undefined) {
  207 + retry = 10;
  208 + }
  209 +
  210 + var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task);
  211 +
  212 + requestOptions.qs.modul = 'ISI';
  213 + requestOptions.qs.command = 'ADV';
  214 +
  215 + logger.info('Requesting advice to supplier', {requestOptions: requestOptions});
  216 + request(requestOptions, function(requestError, requestResponse, requestResponseBody) {
  217 + if (requestError || requestResponse.statusCode != 200) {
  218 + logger.warn('Advice error', {error: request_error, http_response: requestResponse.statusCode});
  219 +
  220 + if (retry > 0) {
  221 + logger.warn('Going to retry advice in ' + adviceDelay + 'ms', {task: task, retry: retry});
  222 + setTimeout(advice, adviceDelay, task, --retry);
  223 + }
  224 +
  225 + return;
  226 + }
  227 +
  228 + var paymentResponse = parsePaymentResponse(requestResponseBody);
  229 + logger.info('Got advice payment response', {paymentResponse: paymentResponse});
  230 +
  231 + if (paymentResponse.status == 'SUCCESS') {
  232 + reportPaymentSuccess(task, paymentResponse);
  233 + }
  234 + else {
  235 + reportPaymentError(task, paymentResponse);
  236 + }
  237 + });
  238 +
  239 +}
  240 +
  241 +function voucherPay(task) {
  242 + var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task);
  243 +
  244 + requestOptions.qs.modul = 'ISI';
  245 + requestOptions.qs.command = 'PAY';
  246 +
  247 + logger.info('Requesting auto payment to supplier', {requestOptions: requestOptions});
  248 + request(requestOptions, function(requestError, requestResponse, requestResponseBody) {
  249 + if (requestError) {
  250 + logger.warn('Request error', {error: requestError});
  251 +
  252 + setTimeout(advice, adviceDelay, task);
  253 + return;
  254 + }
  255 +
  256 + if (requestResponse.statusCode != 200) {
  257 + logger.warn('HTTP response status code is not 200', {http_response: requestResponse.statusCode});
  258 +
  259 + setTimeout(advice, adviceDelay, task);
  260 + return;
  261 + }
  262 +
  263 + logger.info('Supplier response: ' + requestResponseBody);
  264 +
  265 + var paymentResponse = parsePaymentResponse(requestResponseBody);
  266 + logger.info('Got payment response', {paymentResponse: paymentResponse});
  267 +
  268 + if (paymentResponse.status == 'SUCCESS') {
  269 + reportPaymentSuccess(task, paymentResponse);
  270 + }
  271 + else {
  272 + reportPaymentError(task, paymentResponse);
  273 + }
  274 + });
  275 +}
  276 +
  277 +exports.start = start;
  278 +exports.topupRequest = topupRequest;
  279 +exports.calculateSignature = calculateSignature;
  280 +exports.parsePaymentResponse = parsePaymentResponse;
  281 +exports.getErrorCode = getErrorCode;
  282 +exports.getST24ResponseCode = getST24ResponseCode;
  283 +exports.generateSN = generateSN;
  284 +exports.generateDt = generateDt;