Commit eae16d143fcfe51522b982038b0fef2b064e370c

Authored by Adhidarma Hadiwinoto
1 parent 593ada1205
Exists in master

replace space in token to dash

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