Commit 4c6bd69790eccd1714826babe302b5f45f0a0731

Authored by Adhidarma Hadiwinoto
1 parent 816ab0c381
Exists in master

topupStatus

Showing 2 changed files with 176 additions and 10 deletions Inline Diff

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