Commit cee96410a35b8ef01e0cb73099c37747f0419822

Authored by Adhidarma Hadiwinoto
1 parent ad9e196970
Exists in master

parsing querystring pada reverse

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