Commit c875ef02826701652bf6a11ce87768b01b5c8a4c

Authored by Adhidarma Hadiwinoto
1 parent 2e873df939
Exists in master

perapihan parsing

Showing 1 changed file with 7 additions and 2 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 = 4; 12 const seconds_to_wait_before_resend_on_pending = 4;
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(responseDataObj, '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 let serial = _getPropertyFromObjectSafe(responseDataObj, 'serial'); 121 let serial = _getPropertyFromObjectSafe(responseDataObj, 'serial');
122 const info = _getPropertyFromObjectSafe(responseDataObj, 'info'); 122 const info = _getPropertyFromObjectSafe(responseDataObj, 'info');
123 123
124 if (serial) { 124 if (serial) {
125 serial = serial.replace(/ /g, '-'); 125 serial = serial.replace(/ /g, '-');
126 } 126 }
127 127
128 if (!info) { 128 if (!info) {
129 //logger.warn('Undefined data.info on _composeCompleteSn'); 129 //logger.warn('Undefined data.info on _composeCompleteSn');
130 return serial; 130 return serial;
131 } 131 }
132 132
133 const cleanedData = { 133 const cleanedData = {
134 token: serial, 134 token: serial,
135 cust_name: _getPropertyFromObjectSafe(info, 'cust_name'), 135 cust_name: _getPropertyFromObjectSafe(info, 'cust_name'),
136 tariff: _getPropertyFromObjectSafe(info, 'kelas') + 'VA', 136 tariff: _getPropertyFromObjectSafe(info, 'kelas'),
137 total_kwh: _getPropertyFromObjectSafe(info, 'size') 137 total_kwh: _getPropertyFromObjectSafe(info, 'size')
138 } 138 }
139 139
140 if (cleanedData && cleanedData.tariff && typeof cleanedData === 'string' && (cleanedData.tariff.search(/VA$/) < 0) ) {
141 cleanedData.tariff += 'VA';
142
143 }
144
140 if (cleanedData.cust_name) { 145 if (cleanedData.cust_name) {
141 cleanedData.cust_name = cleanedData.cust_name.replace(/\W+/g, ' ').trim().replace(/\W+/g, '-').toUpperCase(); 146 cleanedData.cust_name = cleanedData.cust_name.replace(/\W+/g, ' ').replace(/\W+/g, '-').replace(/-+$/, '').replace(/^-+/, '').toUpperCase();
142 } 147 }
143 148
144 if (cleanedData.total_kwh) { 149 if (cleanedData.total_kwh) {
145 cleanedData.total_kwh = cleanedData.total_kwh.replace(/kWh /g, ''); 150 cleanedData.total_kwh = cleanedData.total_kwh.replace(/kWh /g, '');
146 } 151 }
147 152
148 153
149 logger.verbose('Detail token info extracted', {originalResponseInfo: info, cleanedData: cleanedData}); 154 logger.verbose('Detail token info extracted', {originalResponseInfo: info, cleanedData: cleanedData});
150 155
151 return [ 156 return [
152 cleanedData.token, cleanedData.cust_name, cleanedData.tariff, cleanedData.total_kwh 157 cleanedData.token, cleanedData.cust_name, cleanedData.tariff, cleanedData.total_kwh
153 ].join('/'); 158 ].join('/');
154 } 159 }
155 160
156 function _responseBodyHandler(responseBody, task) { 161 function _responseBodyHandler(responseBody, task) {
157 let rc = '68'; 162 let rc = '68';
158 let response = _decodeResponseBody(responseBody); 163 let response = _decodeResponseBody(responseBody);
159 164
160 logger.verbose('RESPONSE', {response: response}); 165 logger.verbose('RESPONSE', {response: response});
161 166
162 const responseStatus = _getPropertyFromObjectSafe(response, 'status'); 167 const responseStatus = _getPropertyFromObjectSafe(response, 'status');
163 const responseInfo = _getPropertyFromObjectSafe(response, 'info'); 168 const responseInfo = _getPropertyFromObjectSafe(response, 'info');
164 169
165 if (responseStatus == 'Error') { 170 if (responseStatus == 'Error') {
166 if (['insufficient balance', 'System Cut-Off'].indexOf(responseInfo) >= 0) { 171 if (['insufficient balance', 'System Cut-Off'].indexOf(responseInfo) >= 0) {
167 rc = '91'; 172 rc = '91';
168 } 173 }
169 callbackReport(task.requestId, '91', [responseStatus, responseInfo].join(': '), {task: task}); 174 callbackReport(task.requestId, '91', [responseStatus, responseInfo].join(': '), {task: task});
170 return; 175 return;
171 } 176 }
172 177
173 const requestId = _getPropertyFromObjectSafe(response.data, 'request_id'); 178 const requestId = _getPropertyFromObjectSafe(response.data, 'request_id');
174 const trxStatus = _getPropertyFromObjectSafe(response.data, 'trx_status'); 179 const trxStatus = _getPropertyFromObjectSafe(response.data, 'trx_status');
175 const diag = _getPropertyFromObjectSafe(response.data, 'diag'); 180 const diag = _getPropertyFromObjectSafe(response.data, 'diag');
176 let balance = _getPropertyFromObjectSafe(response.data, 'balance'); 181 let balance = _getPropertyFromObjectSafe(response.data, 'balance');
177 182
178 if ((typeof balance === 'string') && balance && aaa.updateBalance) { 183 if ((typeof balance === 'string') && balance && aaa.updateBalance) {
179 balance = balance.replace(/\D/g, ''); 184 balance = balance.replace(/\D/g, '');
180 if (balance) { 185 if (balance) {
181 aaa.updateBalance(balance); 186 aaa.updateBalance(balance);
182 } 187 }
183 } 188 }
184 189
185 let aaaMessage = _composeMessageFromResponseData(response.data); 190 let aaaMessage = _composeMessageFromResponseData(response.data);
186 if (!aaaMessage) { 191 if (!aaaMessage) {
187 aaaMessage = 'Transaksi sedang diproses'; 192 aaaMessage = 'Transaksi sedang diproses';
188 } 193 }
189 194
190 if (trxStatus == 'P') { 195 if (trxStatus == 'P') {
191 logger.verbose('Got pending trx response', {response: response.data}); 196 logger.verbose('Got pending trx response', {response: response.data});
192 rc = '68'; 197 rc = '68';
193 } 198 }
194 else if (trxStatus == 'S') { 199 else if (trxStatus == 'S') {
195 logger.verbose('Got succcess trx response', {response: response.data}); 200 logger.verbose('Got succcess trx response', {response: response.data});
196 201
197 rc = '00'; 202 rc = '00';
198 aaaMessage = 'SN=' + _composeCompleteSn(response.data) + '; ' + aaaMessage; 203 aaaMessage = 'SN=' + _composeCompleteSn(response.data) + '; ' + aaaMessage;
199 } 204 }
200 else if (trxStatus == 'R') { 205 else if (trxStatus == 'R') {
201 logger.verbose('Got rejected trx response', {response: response.data}); 206 logger.verbose('Got rejected trx response', {response: response.data});
202 207
203 const partnerRC = getPartnerRCFromDiagMessage(diag); 208 const partnerRC = getPartnerRCFromDiagMessage(diag);
204 if (partnerRC == '15') { 209 if (partnerRC == '15') {
205 rc = '14'; 210 rc = '14';
206 } 211 }
207 else { 212 else {
208 rc = '40'; 213 rc = '40';
209 } 214 }
210 } 215 }
211 216
212 callbackReport(requestId, rc, aaaMessage, {task: task}); 217 callbackReport(requestId, rc, aaaMessage, {task: task});
213 } 218 }
214 219
215 function getPartnerRCFromDiagMessage(diag) { 220 function getPartnerRCFromDiagMessage(diag) {
216 let matches = diag.match(/^\s*\[(.*)\]/); 221 let matches = diag.match(/^\s*\[(.*)\]/);
217 if (!matches || matches.length < 2) { 222 if (!matches || matches.length < 2) {
218 return; 223 return;
219 } 224 }
220 225
221 return matches[1]; 226 return matches[1];
222 } 227 }
223 228
224 function _hitTopup(task, isCheckStatus) { 229 function _hitTopup(task, isCheckStatus) {
225 230
226 const dt = moment().format('YYYY-MM-DD HH:mm:ss'); 231 const dt = moment().format('YYYY-MM-DD HH:mm:ss');
227 const username = config.h2h_out.username || config.h2h_out.userid; 232 const username = config.h2h_out.username || config.h2h_out.userid;
228 const password = config.h2h_out.password || config.h2h_out.pin; 233 const password = config.h2h_out.password || config.h2h_out.pin;
229 const sign = calculateSign(dt, task.requestId, task.destination, username, password); 234 const sign = calculateSign(dt, task.requestId, task.destination, username, password);
230 235
231 logger.verbose('Sign for ' + dt + ', ' + task.requestId + ', ' + task.destination + ', ' + username + ', ' + password + ' is ' + sign); 236 logger.verbose('Sign for ' + dt + ', ' + task.requestId + ', ' + task.destination + ', ' + username + ', ' + password + ' is ' + sign);
232 const requestOptions = { 237 const requestOptions = {
233 url: config.h2h_out.partner, 238 url: config.h2h_out.partner,
234 form: { 239 form: {
235 username: username, 240 username: username,
236 datetime: dt, 241 datetime: dt,
237 code: task.remoteProduct, 242 code: task.remoteProduct,
238 trx_ref_id: task.requestId, 243 trx_ref_id: task.requestId,
239 cust_num: task.destination, 244 cust_num: task.destination,
240 sign: sign 245 sign: sign
241 } 246 }
242 } 247 }
243 248
244 logger.verbose('Requesting to partner', {requestOptions: requestOptions}); 249 logger.verbose('Requesting to partner', {requestOptions: requestOptions});
245 250
246 request.post(requestOptions, function(error, response, body) { 251 request.post(requestOptions, function(error, response, body) {
247 if (error) { 252 if (error) {
248 let rc = '68'; 253 let rc = '68';
249 254
250 if (!isCheckStatus && (error.syscall == 'connect')) { 255 if (!isCheckStatus && (error.syscall == 'connect')) {
251 rc = '91'; 256 rc = '91';
252 } 257 }
253 258
254 logger.warn('Error requesting to partner', {task: task, rc: rc, error: error, isCheckStatus: isCheckStatus}); 259 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}); 260 callbackReport(task.requestId, rc, 'Error requesting to partner. ' + error, {task: task});
256 return; 261 return;
257 } 262 }
258 263
259 if (response.statusCode != 200) { 264 if (response.statusCode != 200) {
260 let rc = '68'; 265 let rc = '68';
261 266
262 logger.warn('HTTP status code is not 200', {task: task, http_status_code: response.statusCode, isCheckStatus: isCheckStatus}); 267 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}); 268 callbackReport(task.requestId, rc, 'HTTP status code ' + response.statusCode, {task: task});
264 return; 269 return;
265 } 270 }
266 271
267 logger.info('Transaksi sedang diproses', {task: task, response_body: body}); 272 logger.info('Transaksi sedang diproses', {task: task, response_body: body});
268 273
269 _responseBodyHandler(body, task); 274 _responseBodyHandler(body, task);
270 275
271 }) 276 })
272 } 277 }
273 278
274 function _getPropertyFromObjectSafe(obj, property) { 279 function _getPropertyFromObjectSafe(obj, property) {
275 let retval; 280 let retval;
276 281
277 if (!obj) { 282 if (!obj) {
278 logger.warn('Invalid object') 283 logger.warn('Invalid object')
279 return; 284 return;
280 } 285 }
281 286
282 try { 287 try {
283 retval = obj[property]; 288 retval = obj[property];
284 } 289 }
285 catch(e) { 290 catch(e) {
286 logger.warn('Error getting ' + property + ' from object'); 291 logger.warn('Error getting ' + property + ' from object');
287 } 292 }
288 293
289 return retval; 294 return retval;
290 } 295 }
291 296
292 function topupRequest(task) { 297 function topupRequest(task) {
293 aaa.insertTaskToMongoDb(task); 298 aaa.insertTaskToMongoDb(task);
294 _hitTopup(task); 299 _hitTopup(task);
295 } 300 }
296 301
297 function checkStatus(task) { 302 function checkStatus(task) {
298 _hitTopup(task, true); 303 _hitTopup(task, true);
299 } 304 }
300 305
301 306
302 function reverseReportHandler(body) { 307 function reverseReportHandler(body) {
303 logger.info('Got reverse report', {body: body}); 308 logger.info('Got reverse report', {body: body});
304 } 309 }
305 310
306 function createReverseHttpServer() { 311 function createReverseHttpServer() {
307 var httpServer = http.createServer(function(request, response) { 312 var httpServer = http.createServer(function(request, response) {
308 313
309 logger.info('Got request from partner'); 314 logger.info('Got request from partner');
310 315
311 var body = ""; 316 var body = "";
312 request.on('data', function (chunk) { 317 request.on('data', function (chunk) {
313 body += chunk; 318 body += chunk;
314 }); 319 });
315 320
316 request.on('end', function () { 321 request.on('end', function () {
317 response.writeHead(200); 322 response.writeHead(200);
318 response.end('OK'); 323 response.end('OK');
319 324
320 reverseReportHandler(body); 325 reverseReportHandler(body);
321 }); 326 });
322 327
323 }); 328 });
324 329
325 httpServer.listen(config.h2h_out.listen_port, function() { 330 httpServer.listen(config.h2h_out.listen_port, function() {
326 logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port); 331 logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port);
327 }); 332 });
328 } 333 }
329 334
330 335
331 exports.calculateSign = calculateSign; 336 exports.calculateSign = calculateSign;
332 exports.start = start; 337 exports.start = start;
333 exports.topupRequest = topupRequest; 338 exports.topupRequest = topupRequest;
334 exports.checkStatus = checkStatus; 339 exports.checkStatus = checkStatus;
335 340