Commit 5aee0d23625a6a3e76d940b73859193dfee71bee

Authored by Adhidarma Hadiwinoto
1 parent 0d4216a3e9
Exists in master

coba lagi bro

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

1 var xml2js = require('xml2js'); 1 var xml2js = require('xml2js');
2 var request = require('request'); 2 var request = require('request');
3 var http = require('http'); 3 var http = require('http');
4 var redis = require('redis'); 4 var redis = require('redis');
5 var resendDelay = require('sate24/resend-delay.js'); 5 var resendDelay = require('sate24/resend-delay.js');
6 var LRU = require('lru-cache'); 6 var LRU = require('lru-cache');
7 7
8 var aaa; 8 var aaa;
9 var _callbackReport; 9 var _callbackReport;
10 var config; 10 var config;
11 var logger; 11 var logger;
12 var redisClient; 12 var redisClient;
13 13
14 var xmlBuilder = new xml2js.Builder(); 14 var xmlBuilder = new xml2js.Builder();
15 var taskHistory = LRU({max: 500, maxAge: 1000 * 3600 * 2}); 15 var taskHistory = LRU({max: 500, maxAge: 1000 * 3600 * 2});
16 var alreadyPending = LRU({max: 500, maxAge: 1000 * 3600 * 2});
16 17
17 function start(options) { 18 function start(options) {
18 if (!options) { 19 if (!options) {
19 console.log('Undefined options, terminating....'); 20 console.log('Undefined options, terminating....');
20 process.exit(1); 21 process.exit(1);
21 } 22 }
22 23
23 if (options.config) { 24 if (options.config) {
24 config = options.config; 25 config = options.config;
25 } else { 26 } else {
26 console.log('Undefined options.config, terminating....') 27 console.log('Undefined options.config, terminating....')
27 process.exit(1); 28 process.exit(1);
28 } 29 }
29 30
30 if (options.aaa) { 31 if (options.aaa) {
31 aaa = options.aaa; 32 aaa = options.aaa;
32 _callbackReport = options.aaa.callbackReportWithPushToMongoDb; 33 _callbackReport = options.aaa.callbackReportWithPushToMongoDb;
33 } else { 34 } else {
34 console.log('Undefined options.aaa, terminating....') 35 console.log('Undefined options.aaa, terminating....')
35 process.exit(1); 36 process.exit(1);
36 } 37 }
37 38
38 if (options && options.logger) { 39 if (options && options.logger) {
39 logger = options.logger; 40 logger = options.logger;
40 } else { 41 } else {
41 console.log('Undefined options.logger, terminating....') 42 console.log('Undefined options.logger, terminating....')
42 process.exit(1); 43 process.exit(1);
43 } 44 }
44 45
45 createRedisClient(config.globals.redis_host, config.globals.redis_port); 46 createRedisClient(config.globals.redis_host, config.globals.redis_port);
46 createServer(); 47 createServer();
47 48
48 resendDelay.init({ 49 resendDelay.init({
49 config: config, 50 config: config,
50 topupRequest: topupStatus, 51 topupRequest: topupStatus,
51 logger: logger 52 logger: logger
52 }); 53 });
53 } 54 }
54 55
55 function topupRequest(task) { 56 function topupRequest(task) {
56 aaa.insertTaskToMongoDb(task); 57 aaa.insertTaskToMongoDb(task);
57 58
58 getTaskFromHistory(task, function(err, archivedTask) { 59 getTaskFromHistory(task, function(err, archivedTask) {
59 putTaskToHistory(task); 60 putTaskToHistory(task);
60 61
61 if (archivedTask) { 62 if (archivedTask) {
62 logger.info('Task has been executed before, going to checkStatus', {task: task, archivedTask: archivedTask}); 63 logger.info('Task has been executed before, going to checkStatus', {task: task, archivedTask: archivedTask});
63 topupStatus(task); 64 topupStatus(task);
64 } else { 65 } else {
65 _topupRequest(task); 66 _topupRequest(task);
66 } 67 }
67 }); 68 });
68 } 69 }
69 70
70 function _topupRequest(task) { 71 function _topupRequest(task) {
71 72
72 var payload = composeTopupMessage( 73 var payload = composeTopupMessage(
73 config.h2h_out.pin, 74 config.h2h_out.pin,
74 task.remoteProduct, 75 task.remoteProduct,
75 task.destination, 76 task.destination,
76 task.requestId 77 task.requestId
77 ); 78 );
78 79
79 var reqOpts = { 80 var reqOpts = {
80 url: config.h2h_out.partner, 81 url: config.h2h_out.partner,
81 method: "POST", 82 method: "POST",
82 body: payload, 83 body: payload,
83 headers: { 84 headers: {
84 'Content-Type': 'text/xml', 85 'Content-Type': 'text/xml',
85 } 86 }
86 }; 87 };
87 88
88 logger.verbose('Requesting TOPUP to partner', {reqOpts: reqOpts, payload: payload}); 89 logger.verbose('Requesting TOPUP to partner', {reqOpts: reqOpts, payload: payload});
89 request(reqOpts, function (err, response, body) { 90 request(reqOpts, function (err, response, body) {
90 if (err) { 91 if (err) {
91 var msg = 'Error requesting TOPUP to partner: ' + err; 92 var msg = 'Error requesting TOPUP to partner: ' + err;
92 logger.warn(msg, {task: task, err: err}); 93 logger.warn(msg, {task: task, err: err});
93 callbackReport(task.requestId, '68', msg); 94 callbackReport(task.requestId, '68', msg);
94 return; 95 return;
95 } 96 }
96 97
97 logger.verbose('Got a direct response from TOPUP', {response: body, task: task}); 98 logger.verbose('Got a direct response from TOPUP', {response: body, task: task});
98 topupResponseHandler(body, task.requestId, callbackReport); 99 topupResponseHandler(body, task.requestId, callbackReport);
99 }); 100 });
100 } 101 }
101 102
102 function topupStatus(task) { 103 function topupStatus(task) {
103 var payload = composeTopupStatusMessage( 104 var payload = composeTopupStatusMessage(
104 config.h2h_out.pin, 105 config.h2h_out.pin,
105 task.requestId 106 task.requestId
106 ); 107 );
107 108
108 var reqOpts = { 109 var reqOpts = {
109 url: config.h2h_out.partner, 110 url: config.h2h_out.partner,
110 method: "POST", 111 method: "POST",
111 body: payload, 112 body: payload,
112 headers: { 113 headers: {
113 'Content-Type': 'text/xml', 114 'Content-Type': 'text/xml',
114 } 115 }
115 }; 116 };
116 117
117 logger.verbose('Requesting TOPUPSTATUS to partner', {reqOpts: reqOpts, payload: payload}); 118 logger.verbose('Requesting TOPUPSTATUS to partner', {reqOpts: reqOpts, payload: payload});
118 request(reqOpts, function (err, response, body) { 119 request(reqOpts, function (err, response, body) {
119 if (err) { 120 if (err) {
120 var msg = 'Error requesting TOPUPSTATUS to partner: ' + err; 121 var msg = 'Error requesting TOPUPSTATUS to partner: ' + err;
121 logger.warn(msg, {task: task, err: err}); 122 logger.warn(msg, {task: task, err: err});
122 callbackReport(task.requestId, '68', msg); 123 callbackReport(task.requestId, '68', msg);
123 return; 124 return;
124 } 125 }
125 126
126 logger.verbose('Got a direct response from TOPUPSTATUS', {response: body, task: task}); 127 logger.verbose('Got a direct response from TOPUPSTATUS', {response: body, task: task});
127 topupResponseHandler(body, task.requestId, callbackReport); 128 topupResponseHandler(body, task.requestId, callbackReport);
128 }); 129 });
129 } 130 }
130 131
131 function topupResponseHandler(xmlResponse, _requestId, cb) { 132 function topupResponseHandler(xmlResponse, _requestId, cb) {
132 var xmlParser = xml2js.parseString; 133 var xmlParser = xml2js.parseString;
133 xmlParser(xmlResponse, function(err, data) { 134 xmlParser(xmlResponse, function(err, data) {
134 var msg; 135 var msg;
135 var requestId; 136 var requestId;
136 var rc = '68'; 137 var rc = '68';
137 138
138 if (_requestId) { 139 if (_requestId) {
139 requestId = _requestId; 140 requestId = _requestId;
140 } 141 }
141 142
142 if (err) { 143 if (err) {
143 msg = 'Error parsing xml response: ' + err; 144 msg = 'Error parsing xml response: ' + err;
144 145
145 if (logger) { 146 if (logger) {
146 logger.warn(msg, {err: err, response: xmlResponse, task: task}); 147 logger.warn(msg, {err: err, response: xmlResponse, task: task});
147 } else { 148 } else {
148 console.log(msg); 149 console.log(msg);
149 } 150 }
150 } else { 151 } else {
151 152
152 try { 153 try {
153 msg = data.fm.message 154 msg = data.fm.message
154 } 155 }
155 catch(e) { 156 catch(e) {
156 msg = 'Unknown message' 157 msg = 'Unknown message'
157 } 158 }
158 159
159 if (data.fm.status == '0') { 160 if (data.fm.status == '0') {
160 161
161 rc = '00'; 162 rc = '00';
162 msg = modifyMessageWithSn(msg); 163 msg = modifyMessageWithSn(msg);
163 logger.verbose('Modify message on success message', {msg: msg}); 164 logger.verbose('Modify message on success message', {msg: msg});
164 165
165 } else if (data.fm.status == '1') { 166 } else if (data.fm.status == '1') {
166 rc = '68'; 167 rc = '68';
167 } else if (data.fm.status == '2') { 168 } else if (data.fm.status == '2') {
168 rc = '40'; 169 rc = '40';
169 } else if (data.fm.status == '3') { 170 } else if (data.fm.status == '3') {
170 rc = '40'; 171 rc = '40';
171 } else { 172 } else {
172 rc = '68'; 173 rc = '68';
173 } 174 }
174 175
175 if (data.fm.refTrxid) { 176 if (data.fm.refTrxid) {
176 requestId = data.fm.refTrxid; 177 requestId = data.fm.refTrxid;
177 } 178 }
178 179
179 } 180 }
180 181
181 cb(requestId, rc, msg, xmlResponse) 182 cb(requestId, rc, msg, xmlResponse)
182 }); 183 });
183 } 184 }
184 185
185 function callbackReport(requestId, responseCode, msg, rawResponse, dontResendDelay) { 186 function callbackReport(requestId, responseCode, msg, rawResponse, dontResendDelay) {
186 if (!requestId) { 187 if (!requestId) {
187 logger.warn('Undefined requestId, not sending callbackReport', {rc: responseCode, msg: msg, rawResponse: rawResponse}); 188 logger.warn('Undefined requestId, not sending callbackReport', {rc: responseCode, msg: msg, rawResponse: rawResponse});
188 return; 189 return;
189 } 190 }
190 191
191 if (responseCode != '68' || dontResendDelay) { 192 if (responseCode != '68' || dontResendDelay) {
192 resendDelay.cancel(requestId); 193 resendDelay.cancel(requestId);
193 } else { 194 } else {
194 getTaskFromHistory(requestId, function(err, archivedTask) { 195 getTaskFromHistory(requestId, function(err, archivedTask) {
195 if (archivedTask) { 196 if (archivedTask) {
196 resendDelay.register(archivedTask); 197 resendDelay.register(archivedTask);
197 } 198 }
198 }); 199 });
199 } 200 }
200 201
201 _callbackReport(requestId, responseCode, msg, null, rawResponse); 202 if (!alreadyPending.get(requestId) || responseCode != '68') {
203 _callbackReport(requestId, responseCode, msg, null, rawResponse);
204 }
205 alreadyPending.set(requestId, '68');
202 } 206 }
203 207
204 function getSnFromMessage(msg) { 208 function getSnFromMessage(msg) {
205 try { 209 try {
206 var matches = msg.match(/SN:\s*(\d+)/); 210 var matches = msg.match(/SN:\s*(\d+)/);
211 console.log(JSON.stringify(matches));
207 return matches[1]; 212 return matches[1];
208 } 213 }
209 catch(e) { 214 catch(e) {
210 return; 215 return;
211 } 216 }
212 } 217 }
213 218
214 function modifyMessageWithSn(msg) { 219 function modifyMessageWithSn(msg) {
215 var sn = getSnFromMessage(msg); 220 var sn = getSnFromMessage(msg);
216 221
217 if (logger) { 222 if (logger) {
218 logger.verbose('SN=' + sn); 223 logger.verbose('SN=' + sn);
219 } 224 }
220 225
221 if (sn) { 226 if (sn) {
222 msg = 'SN=' + sn + '; ' + msg; 227 msg = 'SN=' + sn + '; ' + msg;
223 } 228 }
224 return msg; 229 return msg;
225 } 230 }
226 231
227 function composeTopupMessage(pin, product, destination, requestId) { 232 function composeTopupMessage(pin, product, destination, requestId) {
228 var data = {fm: { 233 var data = {fm: {
229 command: 'TOPUP', 234 command: 'TOPUP',
230 pin: pin, 235 pin: pin,
231 product: product, 236 product: product,
232 msisdn: destination, 237 msisdn: destination,
233 refTrxid: requestId 238 refTrxid: requestId
234 }}; 239 }};
235 240
236 return xmlBuilder.buildObject(data); 241 return xmlBuilder.buildObject(data);
237 } 242 }
238 243
239 function composeTopupStatusMessage(pin, requestId) { 244 function composeTopupStatusMessage(pin, requestId) {
240 var data = {fm: { 245 var data = {fm: {
241 command: 'TOPUPSTATUS', 246 command: 'TOPUPSTATUS',
242 pin: pin, 247 pin: pin,
243 refTrxid: requestId 248 refTrxid: requestId
244 }} 249 }}
245 250
246 return xmlBuilder.buildObject(data); 251 return xmlBuilder.buildObject(data);
247 } 252 }
248 253
249 function createServer() { 254 function createServer() {
250 var httpServer = http.createServer(function(request, response) { 255 var httpServer = http.createServer(function(request, response) {
251 256
252 logger.info('Got request from partner'); 257 logger.info('Got request from partner');
253 258
254 var body = ""; 259 var body = "";
255 req.on('data', function (chunk) { 260 req.on('data', function (chunk) {
256 body += chunk; 261 body += chunk;
257 }); 262 });
258 263
259 req.on('end', function () { 264 req.on('end', function () {
260 res.writeHead(200); 265 res.writeHead(200);
261 res.end('OK'); 266 res.end('OK');
262 267
263 topupResponseHandler(body, null, callbackReport); 268 topupResponseHandler(body, null, callbackReport);
264 269
265 }); 270 });
266 271
267 }); 272 });
268 273
269 httpServer.listen(config.h2h_out.listen_port, function() { 274 httpServer.listen(config.h2h_out.listen_port, function() {
270 logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port); 275 logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port);
271 }); 276 });
272 } 277 }
273 278
274 function createRedisClient(host, port) { 279 function createRedisClient(host, port) {
275 if (!host && !port) { 280 if (!host && !port) {
276 logger.info('Not creating redis client because unspecified host or port'); 281 logger.info('Not creating redis client because unspecified host or port');
277 return; 282 return;
278 } 283 }
279 284
280 try { 285 try {
281 redisClient = redis.createClient(port, host); 286 redisClient = redis.createClient(port, host);
282 } catch(err) { 287 } catch(err) {
283 logger.warn("Error creating redis client to " + host + ':' + port); 288 logger.warn("Error creating redis client to " + host + ':' + port);
284 } 289 }
285 } 290 }
286 291
287 function getTaskKey(task, chipInfo) { 292 function getTaskKey(task, chipInfo) {
288 var requestId; 293 var requestId;
289 294
290 if (typeof task === 'string') { 295 if (typeof task === 'string') {
291 requestId = task; 296 requestId = task;
292 } else { 297 } else {
293 try { 298 try {
294 requestId = task.requestId; 299 requestId = task.requestId;
295 } 300 }
296 catch(e) { 301 catch(e) {
297 logger.warn('Something wrong', {task: task}); 302 logger.warn('Something wrong', {task: task});
298 console.trace('Cekidot'); 303 console.trace('Cekidot');
299 process.exit(1); 304 process.exit(1);
300 } 305 }
301 306
302 } 307 }
303 308
304 if (!chipInfo && config && config.globals && config.globals.gateway_name) { 309 if (!chipInfo && config && config.globals && config.globals.gateway_name) {
305 chipInfo = config.globals.gateway_name; 310 chipInfo = config.globals.gateway_name;
306 } 311 }
307 312
308 return chipInfo + '.hitachi.rid:' + requestId; 313 return chipInfo + '.hitachi.rid:' + requestId;
309 } 314 }
310 315
311 316
312 function putTaskToHistory(task, cb) { 317 function putTaskToHistory(task, cb) {
313 if (Number(config.globals.no_dupe_check)) { 318 if (Number(config.globals.no_dupe_check)) {
314 if (cb) { cb(); } 319 if (cb) { cb(); }
315 return; 320 return;
316 } 321 }
317 var key = getTaskKey(task, config.globals.gateway_name); 322 var key = getTaskKey(task, config.globals.gateway_name);
318 logger.verbose('Saving task to history LRU', {key: key, task: task}); 323 logger.verbose('Saving task to history LRU', {key: key, task: task});
319 324
320 try { 325 try {
321 taskHistory.set(key, JSON.parse(JSON.stringify(task))); 326 taskHistory.set(key, JSON.parse(JSON.stringify(task)));
322 } catch (e) { } 327 } catch (e) { }
323 328
324 putTaskToRedis(task, cb); 329 putTaskToRedis(task, cb);
325 } 330 }
326 331
327 function putTaskToRedis(task, cb) { 332 function putTaskToRedis(task, cb) {
328 if (!redisClient) { 333 if (!redisClient) {
329 logger.verbose('Not saving to redis because of undefined redisClient') 334 logger.verbose('Not saving to redis because of undefined redisClient')
330 if (cb) { cb(); } 335 if (cb) { cb(); }
331 return; 336 return;
332 } 337 }
333 338
334 var key = getTaskKey(task, config.globals.gateway_name); 339 var key = getTaskKey(task, config.globals.gateway_name);
335 logger.verbose('Saving task to redis', {key: key, task: task}); 340 logger.verbose('Saving task to redis', {key: key, task: task});
336 341
337 redisClient.set(key, JSON.stringify(task), function() { 342 redisClient.set(key, JSON.stringify(task), function() {
338 redisClient.expire(key, 3600*24*30); 343 redisClient.expire(key, 3600*24*30);
339 if (cb) { 344 if (cb) {
340 cb(); 345 cb();
341 } 346 }
342 }); 347 });
343 } 348 }
344 349
345 function getTaskFromHistory(task, cb) { 350 function getTaskFromHistory(task, cb) {
346 logger.verbose('Getting task from history', {task: task}); 351 logger.verbose('Getting task from history', {task: task});
347 var key = getTaskKey(task, config.globals.gateway_name); 352 var key = getTaskKey(task, config.globals.gateway_name);
348 var archive = taskHistory.get(key); 353 var archive = taskHistory.get(key);
349 354
350 if (archive) { 355 if (archive) {
351 if (cb) { cb(null, archive); } 356 if (cb) { cb(null, archive); }
352 } 357 }
353 else { 358 else {
354 getTaskFromRedis(task, cb); 359 getTaskFromRedis(task, cb);
355 } 360 }
356 } 361 }
357 362
358 function getTaskFromRedis(task, cb) { 363 function getTaskFromRedis(task, cb) {
359 if (!redisClient) { 364 if (!redisClient) {
360 if (cb) { cb(null, null); } 365 if (cb) { cb(null, null); }
361 return; 366 return;
362 } 367 }
363 368
364 var key = getTaskKey(task, config.globals.gateway_name); 369 var key = getTaskKey(task, config.globals.gateway_name);
365 redisClient.get(key, function(err, result) { 370 redisClient.get(key, function(err, result) {
366 if (err) { 371 if (err) {
367 logger.warn('Error retrieving task from redis', {err: err}); 372 logger.warn('Error retrieving task from redis', {err: err});
368 cb(err, null); 373 cb(err, null);
369 return; 374 return;
370 } 375 }
371 376
372 var task; 377 var task;
373 try { 378 try {
374 task = JSON.parse(result); 379 task = JSON.parse(result);
375 } 380 }
376 catch(e) { 381 catch(e) {
377 logger.warn('Exception on parsing redis result as a json', {err: e}); 382 logger.warn('Exception on parsing redis result as a json', {err: e});
378 } 383 }
379 384
380 cb(null, task); 385 cb(null, task);
381 }) 386 })
382 } 387 }
383 388
384 389
385 exports.start = start; 390 exports.start = start;
386 exports.topupRequest = topupRequest; 391 exports.topupRequest = topupRequest;
387 exports.composeTopupMessage = composeTopupMessage; 392 exports.composeTopupMessage = composeTopupMessage;
388 exports.getSnFromMessage = getSnFromMessage; 393 exports.getSnFromMessage = getSnFromMessage;
389 exports.modifyMessageWithSn = modifyMessageWithSn; 394 exports.modifyMessageWithSn = modifyMessageWithSn;
390 395