Commit 52fa01ba174d5c1d5f4c6624d58907348ea12b01

Authored by Adhidarma Hadiwinoto
1 parent dff0c67ee7
Exists in master

alpha

Showing 4 changed files with 309 additions and 1 deletions Side-by-side Diff

... ... @@ -0,0 +1,11 @@
  1 +"use strict";
  2 +process.chdir(__dirname);
  3 +
  4 +const config = require('komodo-sdk/config');
  5 +const logger = require('komodo-sdk/logger');
  6 +const matrix = require('komodo-sdk/matrix');
  7 +
  8 +const pullgw = require('komodo-sdk/gateway/pull');
  9 +const partner = require('./partner');
  10 +
  11 +pullgw.setPartner(partner);
... ... @@ -18,5 +18,10 @@
18 18 "simplepay"
19 19 ],
20 20 "author": "Adhidarma Hadiwinoto <me@adhisimon.org>",
21   - "license": "ISC"
  21 + "license": "ISC",
  22 + "dependencies": {
  23 + "komodo-sdk": "git+http://gitlab.kodesumber.com/komodo/komodo-sdk.git",
  24 + "moment": "^2.18.1",
  25 + "request": "^2.81.0"
  26 + }
22 27 }
... ... @@ -0,0 +1,209 @@
  1 +"use strict";
  2 +
  3 +const crypto = require('crypto');
  4 +const moment = require('moment');
  5 +
  6 +const config = require('komodo-sdk/config');
  7 +const logger = require('komodo-sdk/logger');
  8 +const pull = require('komodo-sdk/gateway/pull');
  9 +
  10 +const partner = require('./partner');
  11 +
  12 +function calculateSign(dt, trx_ref_id, cust_num, username, password) {
  13 + const cryptoHashPassword = crypto.createHash('sha1');
  14 + const cryptoHashUsernamePassword = crypto.createHash('sha1');
  15 + const cryptoHashOutest = crypto.createHash('sha1');
  16 +
  17 + cryptoHashPassword.update(password);
  18 + const hashPassword = cryptoHashPassword.digest('hex');
  19 +
  20 + cryptoHashUsernamePassword.update(username + hashPassword);
  21 + const hashUsernamePassword = cryptoHashUsernamePassword.digest('hex');
  22 +
  23 + cryptoHashOutest.update(dt + trx_ref_id + cust_num + hashUsernamePassword);
  24 + return cryptoHashOutest.digest('hex');
  25 +}
  26 +
  27 +function createRequestOptions(task, config) {
  28 + const dt = moment().format('YYYY-MM-DD HH:mm:ss');
  29 + const sign = calculateSign(dt, task.trx_id, task.destination, config.partner.username, config.partner.password);
  30 +
  31 + return = {
  32 + url: config.partner.url,
  33 + form: {
  34 + username: config.partner.username,
  35 + datetime: dt,
  36 + code: task.remote_product,
  37 + trx_ref_id: task.trx_id,
  38 + cust_num: task.destination,
  39 + sign: sign
  40 + }
  41 + }
  42 +}
  43 +
  44 +function decodeResponseBody(responseBody) {
  45 + let response;
  46 +
  47 + try {
  48 + response = JSON.parse(responseBody)
  49 + }
  50 + catch(e) {
  51 + logger.warn('Error parsing response body');
  52 + }
  53 +
  54 + return response;
  55 +}
  56 +
  57 +function cleanBalance(balance) {
  58 + try {
  59 + balance = balance.replace(/\D/g, '');
  60 + }
  61 + catch(e) { };
  62 +
  63 + return balance;
  64 +
  65 +}
  66 +
  67 +function processPartnerResponseBody(body, task) {
  68 + let response = decodeResponseBody(responseBody);
  69 + let messages = [];
  70 +
  71 + if (!response) {
  72 + return;
  73 + }
  74 +
  75 + logger.verbose('RESPONSE', {response: response});
  76 +
  77 + const responseStatus = response.status;
  78 + const responseInfo = response.info;
  79 +
  80 + let taskTrxId;
  81 + if (task && task.trx_id) {
  82 + taskTrxId = task.trx_id;
  83 + }
  84 +
  85 + let responseRequestId;
  86 + if (response.data && response.data.request_id) {
  87 + responseRequestId = response.data.request_id;
  88 + }
  89 +
  90 +
  91 + let responseTrxStatus;
  92 + if (response.data && response.data.trx_status) {
  93 + responseTrxStatus = response.data.trx_status;
  94 + }
  95 +
  96 + let responseTimestamp;
  97 + if (response.data && response.data.timestamp) {
  98 + responseTimestamp = response.data.timestamp;
  99 + messages.push(responseTimestamp);
  100 + }
  101 +
  102 + let responseDiag;
  103 + if (response.data && response.data.diag) {
  104 + responseDiag = response.data.diag;
  105 + messages.push(responseDiag);
  106 + }
  107 +
  108 + let responseMessage;
  109 + if (response.data && response.data.message) {
  110 + responseMessage = response.data.message;
  111 + messages.push(responseMessage);
  112 + }
  113 +
  114 + let responseBalance;
  115 + if (response.data && response.data.balance) {
  116 + responseBalance = response.data.balance;
  117 + messages.push('Balance: ' + responseBalance);
  118 + if (responseBalance) {
  119 + responseBalance = cleanBalance(responseBalance);
  120 + }
  121 + }
  122 +
  123 + if (messages.length <= 0) { messages.push('Transaksi anda sedang diproses'); }
  124 +
  125 + let coreReportData = {
  126 + trx_id: taskTrxId || responseRequestId,
  127 + rc: '68',
  128 + message: messages.join('. ') + '.',
  129 + sn: '',
  130 + handler: config.handler_name,
  131 + balance: responseBalance,
  132 + core_task: task,
  133 + raw: body
  134 + }
  135 +
  136 + if (responseStatus == 'Error') {
  137 + if (responseInfo == 'insufficient balance') {
  138 + coreReportData.rc = '91';
  139 + }
  140 +
  141 + coreReportData.message = [responseStatus, responseInfo].join(': ');
  142 +
  143 + partner.reportToCore(coreReportData);
  144 + return;
  145 + }
  146 +
  147 +
  148 + if (responseTrxStatus == 'P') {
  149 + logger.verbose('Got pending trx response', {response: response.data});
  150 + coreReportData.rc = '68';
  151 + }
  152 + else if (responseTrxStatus == 'S') {
  153 + logger.verbose('Got succcess trx response', {response: response.data});
  154 +
  155 + coreReportData.rc = '00'
  156 +
  157 + coreReportData.sn = composeSn(response);
  158 + coreReportData.message += ' SN=' + coreReportData.sn + '.';
  159 + }
  160 + else if (trxStatus == 'R') {
  161 + logger.verbose('Got rejected trx response', {response: response.data});
  162 +
  163 + const partnerRC = getPartnerRCFromDiagMessage(responseDiag);
  164 + if (partnerRC == '15') {
  165 + coreReportData.rc = '14';
  166 + }
  167 + else {
  168 + coreReportData.rc = '40';
  169 + }
  170 + }
  171 +
  172 + partner.reportToCore(coreReportData);
  173 +}
  174 +
  175 +function composeSn(response) {
  176 + if (!response && !response.data) { return; }
  177 +
  178 + if (!response.data.info) {
  179 + return response.data.serial;
  180 + }
  181 +
  182 + let token = response.data.serial;
  183 + let cust_name = response.data.info.cust_name;
  184 + let tariff = response.data.info.kelas;
  185 + let total_kwh = response.data.info.size;
  186 +
  187 + if (tariff.indexOf('VA') < 0) {
  188 + tariff += 'VA';
  189 + }
  190 +
  191 + if (cust_name) {
  192 + cust_name = cust_name.replace(/\W+/g, ' ').trim().replace(/\W+/g, '-').toUpperCase();
  193 + }
  194 +
  195 + return [ token, cust_name, tariff, total_kwh ].join('/');
  196 +}
  197 +
  198 +function getPartnerRCFromDiagMessage(diag) {
  199 + let matches = diag.match(/^\s*\[(.*)\]/);
  200 + if (!matches || matches.length < 2) {
  201 + return;
  202 + }
  203 +
  204 + return matches[1];
  205 +}
  206 +
  207 +
  208 +exports.calculateSign = calculateSign;
  209 +exports.createRequestOptions = createRequestOptions;
... ... @@ -0,0 +1,83 @@
  1 +"use strict";
  2 +
  3 +const config = require('komodo-sdk/config');
  4 +const logger = require('komodo-sdk/logger');
  5 +const matrix = require('komodo-sdk/matrix');
  6 +const pull = require('komodo-sdk/gateway/pull');
  7 +
  8 +const misc = require('./partner-misc');
  9 +
  10 +/**
  11 + * Pembelian ke partner
  12 + *
  13 + * Merupakan fungsi mandatory yang harus dimiliki oleh setiap gateway.
  14 + */
  15 +function buy(task) {
  16 + _requestToPartner(task, false);
  17 +}
  18 +
  19 +/**
  20 + * Pemeriksaan status transaksi ke partner
  21 + * Merupakan fungsi mandatory yang harus dimiliki oleh setiap gateway.
  22 + */
  23 +function statusCheck(task) {
  24 + _requestToPartner(task, true);
  25 +}
  26 +
  27 +/**
  28 + * Mengirim request ke partner.
  29 + *
  30 + * Untuk digunakan oleh buy dan checkStatus.
  31 + */
  32 +function _requestToPartner(task, pendingOnError) {
  33 +
  34 + const requestOptions = misc.createRequestOptions(task, config);
  35 +
  36 + logger.verbose('Requeting to partner', {requestOptions: requestOptions});
  37 + request.post(requestOptions, function(err, res, body) {
  38 +
  39 + if (err) {
  40 +
  41 + let rc = '68';
  42 + if (!pendingOnError) { rc = '91'; }
  43 +
  44 + logger.warn('Error requesting to partner', {task: task, err: err})
  45 +
  46 + _reportToCore({
  47 + trx_id: task.trx_id,
  48 + rc: rc,
  49 + message: 'Error requesting to partner: ' + err,
  50 + handler: config.handler_name
  51 + })
  52 +
  53 + return;
  54 + }
  55 +
  56 + if (res.statusCode != 200) {
  57 + let rc = '68';
  58 + logger.warn('Partner returning non 200 HTTP STATUS CODE', {task: task, http_status_code: res.statusCode})
  59 +
  60 + _reportToCore({
  61 + trx_id: task.trx_id,
  62 + rc: rc,
  63 + message: 'Partner returning HTTP STATUS CODE ' + res.statusCode + ' instead of 200';
  64 + handler: config.handler_name
  65 + })
  66 +
  67 + return;
  68 + }
  69 +
  70 + misc.processPartnerResponseBody(body, task);
  71 + })
  72 +}
  73 +
  74 +/**
  75 + * Mengirim report hasil transaksi ke CORE
  76 + */
  77 +function reportToCore(data) {
  78 + pull.report(data);
  79 +}
  80 +
  81 +exports.buy = buy;
  82 +exports.statusCheck = statusCheck;
  83 +exports.reportToCore = reportToCore;