Commit c36f8503f6a07bc816f6f83923e5da549c6c44fd

Authored by Adhidarma Hadiwinoto
1 parent 9989604292
Exists in master

typo

Showing 1 changed file with 1 additions and 1 deletions Inline Diff

partner-simplepay.js
1 "use strict"; 1 "use strict";
2 2
3 process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 3 process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
4 4
5 const request = require('request'); 5 const request = require('request');
6 const crypto = require('crypto'); 6 const crypto = require('crypto');
7 const moment = require('moment'); 7 const moment = require('moment');
8 8
9 const http = require('http'); 9 const http = require('http');
10 http.globalAgent.maxSockets = Infinity; 10 http.globalAgent.maxSockets = Infinity;
11 11
12 const seconds_to_wait_before_resend_on_pending = 30; 12 const seconds_to_wait_before_resend_on_pending = 30;
13 13
14 var config; 14 var config;
15 var aaa; 15 var aaa;
16 var logger; 16 var logger;
17 17
18 function start(options) { 18 function start(options) {
19 if (!options) { 19 if (!options) {
20 console.log('Undefined options, terminating....'); 20 console.log('Undefined options, terminating....');
21 process.exit(1); 21 process.exit(1);
22 } 22 }
23 23
24 if (options.config) { 24 if (options.config) {
25 config = options.config; 25 config = options.config;
26 } else { 26 } else {
27 console.log('Undefined options.config, terminating....') 27 console.log('Undefined options.config, terminating....')
28 process.exit(1); 28 process.exit(1);
29 } 29 }
30 30
31 if (options.aaa) { 31 if (options.aaa) {
32 aaa = options.aaa; 32 aaa = options.aaa;
33 } else { 33 } else {
34 console.log('Undefined options.aaa, terminating....') 34 console.log('Undefined options.aaa, terminating....')
35 process.exit(1); 35 process.exit(1);
36 } 36 }
37 37
38 if (options && options.logger) { 38 if (options && options.logger) {
39 logger = options.logger; 39 logger = options.logger;
40 } else { 40 } else {
41 console.log('Undefined options.logger, terminating....') 41 console.log('Undefined options.logger, terminating....')
42 process.exit(1); 42 process.exit(1);
43 } 43 }
44 44
45 createReverseHttpServer(); 45 createReverseHttpServer();
46 } 46 }
47 47
48 function callbackReport(requestId, rc, message, options) { 48 function callbackReport(requestId, rc, message, options) {
49 aaa.callbackReportWithPushToMongoDb(requestId, rc, message); 49 aaa.callbackReportWithPushToMongoDb(requestId, rc, message);
50 50
51 // resend trx as status check if rc 68 and task is defined 51 // resend trx as status check if rc 68 and task is defined
52 if (options && options.task && (rc == '68')) { 52 if (options && options.task && (rc == '68')) {
53 logger.verbose('Got pending trx, requesting in ' + seconds_to_wait_before_resend_on_pending + ' secs'); 53 logger.verbose('Got pending trx, requesting in ' + seconds_to_wait_before_resend_on_pending + ' secs');
54 setTimeout(function() { 54 setTimeout(function() {
55 checkStatus(options.task); 55 checkStatus(options.task);
56 }, seconds_to_wait_before_resend_on_pending * 1000) 56 }, seconds_to_wait_before_resend_on_pending * 1000)
57 } 57 }
58 } 58 }
59 59
60 function calculateSign(dt, trx_ref_id, cust_num, username, password) { 60 function calculateSign(dt, trx_ref_id, cust_num, username, password) {
61 const cryptoHashPassword = crypto.createHash('sha1'); 61 const cryptoHashPassword = crypto.createHash('sha1');
62 const cryptoHashUsernamePassword = crypto.createHash('sha1'); 62 const cryptoHashUsernamePassword = crypto.createHash('sha1');
63 const cryptoHashOutest = crypto.createHash('sha1'); 63 const cryptoHashOutest = crypto.createHash('sha1');
64 64
65 cryptoHashPassword.update(password); 65 cryptoHashPassword.update(password);
66 const hashPassword = cryptoHashPassword.digest('hex'); 66 const hashPassword = cryptoHashPassword.digest('hex');
67 67
68 cryptoHashUsernamePassword.update(username + hashPassword); 68 cryptoHashUsernamePassword.update(username + hashPassword);
69 const hashUsernamePassword = cryptoHashUsernamePassword.digest('hex'); 69 const hashUsernamePassword = cryptoHashUsernamePassword.digest('hex');
70 70
71 cryptoHashOutest.update(dt + trx_ref_id + cust_num + hashUsernamePassword); 71 cryptoHashOutest.update(dt + trx_ref_id + cust_num + hashUsernamePassword);
72 return cryptoHashOutest.digest('hex'); 72 return cryptoHashOutest.digest('hex');
73 } 73 }
74 74
75 function _decodeResponseBody(responseBody) { 75 function _decodeResponseBody(responseBody) {
76 let response; 76 let response;
77 77
78 try { 78 try {
79 response = JSON.parse(responseBody); 79 response = JSON.parse(responseBody);
80 } 80 }
81 catch(e) { 81 catch(e) {
82 logger.warn('Error parsing response body'); 82 logger.warn('Error parsing response body');
83 } 83 }
84 84
85 return response; 85 return response;
86 } 86 }
87 87
88 function _composeMessageFromResponseData(responseDataObj) { 88 function _composeMessageFromResponseData(responseDataObj) {
89 const diag = _getPropertyFromObjectSafe(responseDataObj, 'diag'); 89 const diag = _getPropertyFromObjectSafe(responseDataObj, 'diag');
90 const msg = _getPropertyFromObjectSafe(responseDataObj, 'message'); 90 const msg = _getPropertyFromObjectSafe(responseDataObj, 'message');
91 const balance = _getPropertyFromObjectSafe(responseDataObj, 'balance'); 91 const balance = _getPropertyFromObjectSafe(responseDataObj, 'balance');
92 const timestamp = _getPropertyFromObjectSafe(responseDataObj, 'timestamp'); 92 const timestamp = _getPropertyFromObjectSafe(responseDataObj, 'timestamp');
93 const price = _getPropertyFromObjectSafe(response.data, 'harga'); 93 const price = _getPropertyFromObjectSafe(responseDataObj, 'harga');
94 94
95 let messages = []; 95 let messages = [];
96 96
97 if (timestamp) { 97 if (timestamp) {
98 messages.push(timestamp); 98 messages.push(timestamp);
99 } 99 }
100 100
101 if (diag) { 101 if (diag) {
102 messages.push(diag); 102 messages.push(diag);
103 } 103 }
104 104
105 if (msg) { 105 if (msg) {
106 messages.push(msg); 106 messages.push(msg);
107 } 107 }
108 108
109 if (balance) { 109 if (balance) {
110 messages.push('Balance: ' + balance); 110 messages.push('Balance: ' + balance);
111 } 111 }
112 112
113 if (price) { 113 if (price) {
114 messages.push('Price: ' + price); 114 messages.push('Price: ' + price);
115 } 115 }
116 116
117 return messages.join('. ') + '.'; 117 return messages.join('. ') + '.';
118 } 118 }
119 119
120 function _composeCompleteSn(responseDataObj) { 120 function _composeCompleteSn(responseDataObj) {
121 const serial = _getPropertyFromObjectSafe(responseDataObj, 'serial'); 121 const serial = _getPropertyFromObjectSafe(responseDataObj, 'serial');
122 const info = _getPropertyFromObjectSafe(responseDataObj, 'info'); 122 const info = _getPropertyFromObjectSafe(responseDataObj, 'info');
123 123
124 if (!info) { 124 if (!info) {
125 //logger.warn('Undefined data.info on _composeCompleteSn'); 125 //logger.warn('Undefined data.info on _composeCompleteSn');
126 return serial; 126 return serial;
127 } 127 }
128 128
129 const cleanedData = { 129 const cleanedData = {
130 token: serial, 130 token: serial,
131 cust_name: _getPropertyFromObjectSafe(info, 'cust_name'), 131 cust_name: _getPropertyFromObjectSafe(info, 'cust_name'),
132 tariff: _getPropertyFromObjectSafe(info, 'kelas') + 'VA', 132 tariff: _getPropertyFromObjectSafe(info, 'kelas') + 'VA',
133 total_kwh: _getPropertyFromObjectSafe(info, 'size') 133 total_kwh: _getPropertyFromObjectSafe(info, 'size')
134 } 134 }
135 135
136 if (cleanedData.token) { 136 if (cleanedData.token) {
137 cleanedData.token = cleanedData.token.replace(/ /g, '-'); 137 cleanedData.token = cleanedData.token.replace(/ /g, '-');
138 } 138 }
139 139
140 if (cleanedData.cust_name) { 140 if (cleanedData.cust_name) {
141 cleanedData.cust_name = cleanedData.cust_name.replace(/\W+/g, ' ').trim().replace(/\W+/g, '-').toUpperCase(); 141 cleanedData.cust_name = cleanedData.cust_name.replace(/\W+/g, ' ').trim().replace(/\W+/g, '-').toUpperCase();
142 } 142 }
143 143
144 if (cleanedData.total_kwh) { 144 if (cleanedData.total_kwh) {
145 cleanedData.total_kwh = cleanedData.total_kwh.replace(/kWh /g, ''); 145 cleanedData.total_kwh = cleanedData.total_kwh.replace(/kWh /g, '');
146 } 146 }
147 147
148 148
149 logger.verbose('Detail token info extracted', {originalResponseInfo: info, cleanedData: cleanedData}); 149 logger.verbose('Detail token info extracted', {originalResponseInfo: info, cleanedData: cleanedData});
150 150
151 return [ 151 return [
152 cleanedData.token, cleanedData.cust_name, cleanedData.tariff, cleanedData.total_kwh 152 cleanedData.token, cleanedData.cust_name, cleanedData.tariff, cleanedData.total_kwh
153 ].join('/'); 153 ].join('/');
154 } 154 }
155 155
156 function _responseBodyHandler(responseBody, task) { 156 function _responseBodyHandler(responseBody, task) {
157 let rc = '68'; 157 let rc = '68';
158 let response = _decodeResponseBody(responseBody); 158 let response = _decodeResponseBody(responseBody);
159 159
160 logger.verbose('RESPONSE', {response: response}); 160 logger.verbose('RESPONSE', {response: response});
161 161
162 const responseStatus = _getPropertyFromObjectSafe(response, 'status'); 162 const responseStatus = _getPropertyFromObjectSafe(response, 'status');
163 const responseInfo = _getPropertyFromObjectSafe(response, 'info'); 163 const responseInfo = _getPropertyFromObjectSafe(response, 'info');
164 164
165 if (responseStatus == 'Error') { 165 if (responseStatus == 'Error') {
166 if (['insufficient balance', 'System Cut-Off'].indexOf(responseInfo) >= 0) { 166 if (['insufficient balance', 'System Cut-Off'].indexOf(responseInfo) >= 0) {
167 rc = '91'; 167 rc = '91';
168 } 168 }
169 callbackReport(task.requestId, '91', [responseStatus, responseInfo].join(': '), {task: task}); 169 callbackReport(task.requestId, '91', [responseStatus, responseInfo].join(': '), {task: task});
170 return; 170 return;
171 } 171 }
172 172
173 const requestId = _getPropertyFromObjectSafe(response.data, 'request_id'); 173 const requestId = _getPropertyFromObjectSafe(response.data, 'request_id');
174 const trxStatus = _getPropertyFromObjectSafe(response.data, 'trx_status'); 174 const trxStatus = _getPropertyFromObjectSafe(response.data, 'trx_status');
175 const diag = _getPropertyFromObjectSafe(response.data, 'diag'); 175 const diag = _getPropertyFromObjectSafe(response.data, 'diag');
176 let balance = _getPropertyFromObjectSafe(response.data, 'balance'); 176 let balance = _getPropertyFromObjectSafe(response.data, 'balance');
177 177
178 if (balance && aaa.updateBalance) { 178 if (balance && aaa.updateBalance) {
179 balance = balance.replace(/\D/g, ''); 179 balance = balance.replace(/\D/g, '');
180 if (balance) { 180 if (balance) {
181 aaa.updateBalance(balance); 181 aaa.updateBalance(balance);
182 } 182 }
183 } 183 }
184 184
185 let aaaMessage = _composeMessageFromResponseData(response.data); 185 let aaaMessage = _composeMessageFromResponseData(response.data);
186 if (!aaaMessage) { 186 if (!aaaMessage) {
187 aaaMessage = 'Transaksi sedang diproses'; 187 aaaMessage = 'Transaksi sedang diproses';
188 } 188 }
189 189
190 if (trxStatus == 'P') { 190 if (trxStatus == 'P') {
191 logger.verbose('Got pending trx response', {response: response.data}); 191 logger.verbose('Got pending trx response', {response: response.data});
192 rc = '68'; 192 rc = '68';
193 } 193 }
194 else if (trxStatus == 'S') { 194 else if (trxStatus == 'S') {
195 logger.verbose('Got succcess trx response', {response: response.data}); 195 logger.verbose('Got succcess trx response', {response: response.data});
196 196
197 rc = '00'; 197 rc = '00';
198 aaaMessage = 'SN=' + _composeCompleteSn(response.data) + '; ' + aaaMessage; 198 aaaMessage = 'SN=' + _composeCompleteSn(response.data) + '; ' + aaaMessage;
199 } 199 }
200 else if (trxStatus == 'R') { 200 else if (trxStatus == 'R') {
201 logger.verbose('Got rejected trx response', {response: response.data}); 201 logger.verbose('Got rejected trx response', {response: response.data});
202 202
203 const partnerRC = getPartnerRCFromDiagMessage(diag); 203 const partnerRC = getPartnerRCFromDiagMessage(diag);
204 if (partnerRC == '15') { 204 if (partnerRC == '15') {
205 rc = '14'; 205 rc = '14';
206 } 206 }
207 else { 207 else {
208 rc = '40'; 208 rc = '40';
209 } 209 }
210 } 210 }
211 211
212 callbackReport(requestId, rc, aaaMessage, {task: task}); 212 callbackReport(requestId, rc, aaaMessage, {task: task});
213 } 213 }
214 214
215 function getPartnerRCFromDiagMessage(diag) { 215 function getPartnerRCFromDiagMessage(diag) {
216 let matches = diag.match(/^\s*\[(.*)\]/); 216 let matches = diag.match(/^\s*\[(.*)\]/);
217 if (!matches || matches.length < 2) { 217 if (!matches || matches.length < 2) {
218 return; 218 return;
219 } 219 }
220 220
221 return matches[1]; 221 return matches[1];
222 } 222 }
223 223
224 function _hitTopup(task, isCheckStatus) { 224 function _hitTopup(task, isCheckStatus) {
225 225
226 const dt = moment().format('YYYY-MM-DD HH:mm:ss'); 226 const dt = moment().format('YYYY-MM-DD HH:mm:ss');
227 const username = config.h2h_out.username || config.h2h_out.userid; 227 const username = config.h2h_out.username || config.h2h_out.userid;
228 const password = config.h2h_out.password || config.h2h_out.pin; 228 const password = config.h2h_out.password || config.h2h_out.pin;
229 const sign = calculateSign(dt, task.requestId, task.destination, username, password); 229 const sign = calculateSign(dt, task.requestId, task.destination, username, password);
230 230
231 logger.verbose('Sign for ' + dt + ', ' + task.requestId + ', ' + task.destination + ', ' + username + ', ' + password + ' is ' + sign); 231 logger.verbose('Sign for ' + dt + ', ' + task.requestId + ', ' + task.destination + ', ' + username + ', ' + password + ' is ' + sign);
232 const requestOptions = { 232 const requestOptions = {
233 url: config.h2h_out.partner, 233 url: config.h2h_out.partner,
234 form: { 234 form: {
235 username: username, 235 username: username,
236 datetime: dt, 236 datetime: dt,
237 code: task.remoteProduct, 237 code: task.remoteProduct,
238 trx_ref_id: task.requestId, 238 trx_ref_id: task.requestId,
239 cust_num: task.destination, 239 cust_num: task.destination,
240 sign: sign 240 sign: sign
241 } 241 }
242 } 242 }
243 243
244 logger.verbose('Requesting to partner', {requestOptions: requestOptions}); 244 logger.verbose('Requesting to partner', {requestOptions: requestOptions});
245 245
246 request.post(requestOptions, function(error, response, body) { 246 request.post(requestOptions, function(error, response, body) {
247 if (error) { 247 if (error) {
248 let rc = '68'; 248 let rc = '68';
249 249
250 if (!isCheckStatus && (error.syscall == 'connect')) { 250 if (!isCheckStatus && (error.syscall == 'connect')) {
251 rc = '91'; 251 rc = '91';
252 } 252 }
253 253
254 logger.warn('Error requesting to partner', {task: task, rc: rc, error: error, isCheckStatus: isCheckStatus}); 254 logger.warn('Error requesting to partner', {task: task, rc: rc, error: error, isCheckStatus: isCheckStatus});
255 callbackReport(task.requestId, rc, 'Error requesting to partner. ' + error, {task: task}); 255 callbackReport(task.requestId, rc, 'Error requesting to partner. ' + error, {task: task});
256 return; 256 return;
257 } 257 }
258 258
259 if (response.statusCode != 200) { 259 if (response.statusCode != 200) {
260 let rc = '68'; 260 let rc = '68';
261 261
262 logger.warn('HTTP status code is not 200', {task: task, http_status_code: response.statusCode, isCheckStatus: isCheckStatus}); 262 logger.warn('HTTP status code is not 200', {task: task, http_status_code: response.statusCode, isCheckStatus: isCheckStatus});
263 callbackReport(task.requestId, rc, 'HTTP status code ' + response.statusCode, {task: task}); 263 callbackReport(task.requestId, rc, 'HTTP status code ' + response.statusCode, {task: task});
264 return; 264 return;
265 } 265 }
266 266
267 logger.info('Transaksi sedang diproses', {task: task, response_body: body}); 267 logger.info('Transaksi sedang diproses', {task: task, response_body: body});
268 268
269 _responseBodyHandler(body, task); 269 _responseBodyHandler(body, task);
270 270
271 }) 271 })
272 } 272 }
273 273
274 function _getPropertyFromObjectSafe(obj, property) { 274 function _getPropertyFromObjectSafe(obj, property) {
275 let retval; 275 let retval;
276 276
277 if (!obj) { 277 if (!obj) {
278 logger.warn('Invalid object') 278 logger.warn('Invalid object')
279 return; 279 return;
280 } 280 }
281 281
282 try { 282 try {
283 retval = obj[property]; 283 retval = obj[property];
284 } 284 }
285 catch(e) { 285 catch(e) {
286 logger.warn('Error getting ' + property + ' from object'); 286 logger.warn('Error getting ' + property + ' from object');
287 } 287 }
288 288
289 return retval; 289 return retval;
290 } 290 }
291 291
292 function topupRequest(task) { 292 function topupRequest(task) {
293 aaa.insertTaskToMongoDb(task); 293 aaa.insertTaskToMongoDb(task);
294 _hitTopup(task); 294 _hitTopup(task);
295 } 295 }
296 296
297 function checkStatus(task) { 297 function checkStatus(task) {
298 _hitTopup(task, true); 298 _hitTopup(task, true);
299 } 299 }
300 300
301 301
302 function reverseReportHandler(body) { 302 function reverseReportHandler(body) {
303 logger.info('Got reverse report', {body: body}); 303 logger.info('Got reverse report', {body: body});
304 } 304 }
305 305
306 function createReverseHttpServer() { 306 function createReverseHttpServer() {
307 var httpServer = http.createServer(function(request, response) { 307 var httpServer = http.createServer(function(request, response) {
308 308
309 logger.info('Got request from partner'); 309 logger.info('Got request from partner');
310 310
311 var body = ""; 311 var body = "";
312 request.on('data', function (chunk) { 312 request.on('data', function (chunk) {
313 body += chunk; 313 body += chunk;
314 }); 314 });
315 315
316 request.on('end', function () { 316 request.on('end', function () {
317 response.writeHead(200); 317 response.writeHead(200);
318 response.end('OK'); 318 response.end('OK');
319 319
320 reverseReportHandler(body); 320 reverseReportHandler(body);
321 }); 321 });
322 322
323 }); 323 });
324 324
325 httpServer.listen(config.h2h_out.listen_port, function() { 325 httpServer.listen(config.h2h_out.listen_port, function() {
326 logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port); 326 logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port);
327 }); 327 });
328 } 328 }
329 329
330 330
331 exports.calculateSign = calculateSign; 331 exports.calculateSign = calculateSign;
332 exports.start = start; 332 exports.start = start;
333 exports.topupRequest = topupRequest; 333 exports.topupRequest = topupRequest;
334 exports.checkStatus = checkStatus; 334 exports.checkStatus = checkStatus;
335 335