Commit 69f39c3cf16b89f212f95904bb45ce72e80c304e

Authored by Adhidarma Hadiwinoto
1 parent e91d661eff
Exists in master

log lebih teratur, i hope so

Showing 1 changed file with 8 additions and 9 deletions Inline Diff

1 var xmlrpc = require('xmlrpc'); 1 var xmlrpc = require('xmlrpc');
2 var request = require('request'); 2 var request = require('request');
3 var neoxmlinutil = require('./neoxmlinutil'); 3 var neoxmlinutil = require('./neoxmlinutil');
4 var http = require('http'); 4 var http = require('http');
5 var https = require('https'); 5 var https = require('https');
6 var fs = require('fs'); 6 var fs = require('fs');
7 var xml2js = new require('xml2js'); 7 var xml2js = new require('xml2js');
8 var xml2jsParser = require('xml2js').parseString; 8 var xml2jsParser = require('xml2js').parseString;
9 var xml2jsBuilder = new xml2js.Builder(); 9 var xml2jsBuilder = new xml2js.Builder();
10 var url = require("url"); 10 var url = require("url");
11 var strftime = require('strftime'); 11 var strftime = require('strftime');
12 12
13 var options; 13 var options;
14 var config; 14 var config;
15 var logger; 15 var logger;
16 16
17 var server; 17 var server;
18 18
19 function start(_options) { 19 function start(_options) {
20 options = _options; 20 options = _options;
21 21
22 if (options.config) { 22 if (options.config) {
23 config = options.config; 23 config = options.config;
24 } 24 }
25 else { 25 else {
26 config = require("./config.json"); 26 config = require("./config.json");
27 } 27 }
28 28
29 if (options.logger) { 29 if (options.logger) {
30 logger = options.logger; 30 logger = options.logger;
31 } 31 }
32 else { 32 else {
33 logger = console; 33 logger = console;
34 } 34 }
35 35
36 createResponseServer(); 36 createResponseServer();
37 37
38 createDiyHttpXmlRpcServer(); 38 createDiyHttpXmlRpcServer();
39 //createXmlRpcServer() 39 //createXmlRpcServer()
40 //createExpressXmlRpcServer(); 40 //createExpressXmlRpcServer();
41 } 41 }
42 42
43 function getXmlRpcParam(values) { 43 function getXmlRpcParam(values) {
44 try { 44 try {
45 45
46 var count = values.length 46 var count = values.length
47 var result = {}; 47 var result = {};
48 for (var i = 0; i < count; i++) { 48 for (var i = 0; i < count; i++) {
49 var value = values[i]; 49 var value = values[i];
50 50
51 var keys = Object.keys(value.value[0]); 51 var keys = Object.keys(value.value[0]);
52 var firstKey = keys[0]; 52 var firstKey = keys[0];
53 result[value.name[0]] = value.value[0][firstKey][0]; 53 result[value.name[0]] = value.value[0][firstKey][0];
54 } 54 }
55 55
56 return result; 56 return result;
57 57
58 } 58 }
59 catch(err) { 59 catch(err) {
60 return null; 60 return null;
61 } 61 }
62 } 62 }
63 63
64 function createDiyHttpXmlRpcServer() { 64 function createDiyHttpXmlRpcServer() {
65 var serverOptions = { 65 var serverOptions = {
66 key: fs.readFileSync('./server.key'), 66 key: fs.readFileSync('./server.key'),
67 cert: fs.readFileSync('./server.crt') 67 cert: fs.readFileSync('./server.crt')
68 } 68 }
69 69
70 var httpServer = https.createServer(serverOptions, function(req, res) { 70 var httpServer = https.createServer(serverOptions, function(req, res) {
71 71
72 logger.info("Incoming connection from " + req.connection.remoteAddress); 72 logger.info("Incoming connection from " + req.connection.remoteAddress);
73 73
74 var body = ""; 74 var body = "";
75 75
76 req.on('data', function (chunk) { 76 req.on('data', function (chunk) {
77 body += chunk; 77 body += chunk;
78 }); 78 });
79 79
80 req.on('end', function () { 80 req.on('end', function () {
81 81
82 xml2jsParser(body, function(err, message) { 82 xml2jsParser(body, function(err, message) {
83 83
84 if (err) { 84 if (err) {
85 res.end('Unknown xml'); 85 res.end('Unknown xml');
86 return; 86 return;
87 } 87 }
88 88
89 var method; 89 var method;
90 var _params; 90 var _params;
91 91
92 try { 92 try {
93 method = message.methodCall.methodName[0]; 93 method = message.methodCall.methodName[0];
94 _params = message.methodCall.params[0].param[0].value[0].struct[0].member; 94 _params = message.methodCall.params[0].param[0].value[0].struct[0].member;
95 } 95 }
96 catch(errSelectMethod) { 96 catch(errSelectMethod) {
97 logger.warn('Failed to get method and _params'); 97 logger.warn('Failed to get method and _params');
98 res.end('Invalid XMLRPC message') 98 res.end('Invalid XMLRPC message')
99 return; 99 return;
100 } 100 }
101 101
102 params = getXmlRpcParam(_params); 102 params = getXmlRpcParam(_params);
103 logger.verbose('Got XMLRPC request', {method: method, params: params}); 103 logger.verbose('Got XMLRPC request', {method: method, params: params});
104 104
105 sendToMaster(params, req.connection.remoteAddress, function(forwardError) { 105 sendToMaster(params, req.connection.remoteAddress, function(forwardError) {
106 if (forwardError) { 106 if (forwardError) {
107 immediateReply(params, res, '40', forwardError.toString()); 107 immediateReply(params, res, '40', forwardError.toString());
108 } else { 108 } else {
109 immediateReply(params, res, '68'); 109 immediateReply(params, res, '68');
110 } 110 }
111 }); 111 });
112 112
113 }) 113 })
114 }); 114 });
115 115
116 }); 116 });
117 117
118 httpServer.listen(config.server_options.port, function() { 118 httpServer.listen(config.server_options.port, function() {
119 logger.info('HTTP XMLRPC listen on port ' + config.server_options.port); 119 logger.info('HTTP XMLRPC listen on port ' + config.server_options.port);
120 }); 120 });
121 } 121 }
122 122
123 function composeXmlRpcResponse(param) { 123 function composeXmlRpcResponse(param) {
124 124
125 var values = []; 125 var values = [];
126 var keys = Object.keys(param); 126 var keys = Object.keys(param);
127 var keysCount = keys.length; 127 var keysCount = keys.length;
128 128
129 for (var i = 0; i < keysCount; i++) { 129 for (var i = 0; i < keysCount; i++) {
130 var key = keys[i]; 130 var key = keys[i];
131 var value = { 131 var value = {
132 name: key, 132 name: key,
133 value: { 133 value: {
134 string: param[key] 134 string: param[key]
135 } 135 }
136 } 136 }
137 values.push(value); 137 values.push(value);
138 } 138 }
139 139
140 var data = { 140 var data = {
141 methodResponse: { 141 methodResponse: {
142 params: { 142 params: {
143 param: { 143 param: {
144 value: { 144 value: {
145 struct: { 145 struct: {
146 member: values 146 member: values
147 } 147 }
148 } 148 }
149 } 149 }
150 } 150 }
151 } 151 }
152 } 152 }
153 153
154 logger.info(JSON.stringify(data)); 154 logger.info(JSON.stringify(data));
155 155
156 return xml2jsBuilder.buildObject(data); 156 return xml2jsBuilder.buildObject(data);
157 } 157 }
158 158
159 function immediateReply(param, requestResponse, responseCode, message) { 159 function immediateReply(param, requestResponse, responseCode, message) {
160 160
161 if (!responseCode) { 161 if (!responseCode) {
162 responseCode = '68'; 162 responseCode = '68';
163 } 163 }
164 164
165 if (!message) { 165 if (!message) {
166 message = "Sementara tidak dapat diproses."; 166 message = "Sementara tidak dapat diproses.";
167 } 167 }
168 168
169 if (responseCode == '68') { 169 if (responseCode == '68') {
170 message = 'ISI ' 170 message = 'ISI '
171 + param.NOM.toUpperCase() 171 + param.NOM.toUpperCase()
172 + ' KE ' 172 + ' KE '
173 + param.NOHP 173 + param.NOHP
174 + ', Transaksi anda sedang diproses' 174 + ', Transaksi anda sedang diproses'
175 } 175 }
176 176
177 var responseData = { 177 var responseData = {
178 'RESPONSECODE': responseCode, 178 'RESPONSECODE': responseCode,
179 'REQUESTID': param.REQUESTID, 179 'REQUESTID': param.REQUESTID,
180 'MESSAGE': message, 180 'MESSAGE': message,
181 'TRANSACTIONID': '0', 181 'TRANSACTIONID': '0',
182 } 182 }
183 183
184 184
185 var responseBody = composeXmlRpcResponse(responseData) 185 var responseBody = composeXmlRpcResponse(responseData)
186 logger.info("Sending immediateReply response", {responseData: responseData}); 186 logger.info("Sending immediateReply response", {responseData: responseData});
187 187
188 requestResponse.writeHead(200, {'Content-Type': 'text/xml'}); 188 requestResponse.writeHead(200, {'Content-Type': 'text/xml'});
189 requestResponse.end(responseBody); 189 requestResponse.end(responseBody);
190 } 190 }
191 191
192 function composeMessage(params, remoteAddress) { 192 function composeMessage(params, remoteAddress) {
193 try { 193 try {
194 var nom = params.NOM.replace(/\./g, '').trim(); 194 var nom = params.NOM.replace(/\./g, '').trim();
195 var destination = params.NOHP.replace(/\./g, '').trim(); 195 var destination = params.NOHP.replace(/\./g, '').trim();
196 var pin = params.PIN.replace(/\./g, '').trim(); 196 var pin = params.PIN.replace(/\./g, '').trim();
197 var requestId = params.REQUESTID.replace(/\./g, '').trim(); 197 var requestId = params.REQUESTID.replace(/\./g, '').trim();
198 198
199 return 'MI.' + nom + '."' + destination + '".' + pin + '.' + requestId + '.NOTRUST."' + remoteAddress + '"'; 199 return 'MI.' + nom + '."' + destination + '".' + pin + '.' + requestId + '.NOTRUST."' + remoteAddress + '"';
200 } 200 }
201 catch(err) { 201 catch(err) {
202 return; 202 return;
203 } 203 }
204 204
205 205
206 } 206 }
207 207
208 function sendToMaster(param, remoteAddress, callback) { 208 function sendToMaster(param, remoteAddress, callback) {
209 209
210 /* 210 /*
211 var smscidSuffix = '99999999999999' + String(Math.round(Math.random() * 99999999999999)); 211 var smscidSuffix = '99999999999999' + String(Math.round(Math.random() * 99999999999999));
212 var smscid = 'XML1' + smscidSuffix.slice(-13); 212 var smscid = 'XML1' + smscidSuffix.slice(-13);
213 */ 213 */
214 214
215 //var smscid = 'XML' + '12345' + strftime('%H%M%S%L'); 215 //var smscid = 'XML' + '12345' + strftime('%H%M%S%L');
216 var smscid = config.smscid;// + Math.round(Math.random() * 9999999999999); 216 var smscid = config.smscid;// + Math.round(Math.random() * 9999999999999);
217 217
218 var message = composeMessage(param, remoteAddress); 218 var message = composeMessage(param, remoteAddress);
219 if (!message) { 219 if (!message) {
220 logger.warn('Invalid message for sendToMaster', {param: param}); 220 logger.warn('Invalid message for sendToMaster', {param: param});
221 return; 221 return;
222 } 222 }
223 223
224 var requestOpts = { 224 var requestOpts = {
225 url: config.master_url, 225 url: config.master_url,
226 qs: { 226 qs: {
227 PhoneNumber: param.MSISDN, 227 PhoneNumber: param.MSISDN,
228 'Text': message, 228 'Text': message,
229 Res_Port: config.res_port, 229 Res_Port: config.res_port,
230 Smscid: smscid 230 Smscid: smscid
231 } 231 }
232 }; 232 };
233 233
234 if (config.res_ip) { 234 if (config.res_ip) {
235 requestOpts.qs.ip_addr = config.res_ip; 235 requestOpts.qs.ip_addr = config.res_ip;
236 } 236 }
237 237
238 logger.info('Forward request to master', {requestOpts: requestOpts}); 238 logger.info('Forward request to master', {requestOpts: requestOpts});
239 239
240 request(requestOpts, function(requestError, response, body) { 240 request(requestOpts, function(requestError, response, body) {
241 241
242 if (requestError) { 242 if (requestError) {
243 logger.warn('Error requesting to master: ' + requestError); 243 logger.warn('Error requesting to master: ' + requestError);
244 } else { 244 } else {
245 logger.info('Got response from master', {body: body, requestOpts: requestOpts}); 245 logger.info('Got response from master', {body: body, requestOpts: requestOpts});
246 } 246 }
247 247
248 callback(requestError); 248 callback(requestError);
249 }) 249 })
250 250
251 } 251 }
252 252
253 function sendReply(response) { 253 function sendReply(response) {
254 /* 254 /*
255 var requestId = neoxmlinutil.getRequestIdFromResponseMessage(response.text); 255 var requestId = neoxmlinutil.getRequestIdFromResponseMessage(response.text);
256 256
257 if (!requestId) { 257 if (!requestId) {
258 logger.warn('No request id found, skipping'); 258 logger.warn('No request id found, skipping');
259 return; 259 return;
260 } 260 }
261 */ 261 */
262 262
263 var params = { 263 var params = {
264 REQUESTID: response.requestid, 264 REQUESTID: response.requestid,
265 MSISDN: response.PhoneNumber, 265 MSISDN: response.PhoneNumber,
266 RESPONSECODE: response.resp_code, 266 RESPONSECODE: response.resp_code,
267 MESSAGE: response.text, 267 MESSAGE: response.text,
268 } 268 }
269 269
270 if (response.new_sn) { 270 if (response.new_sn) {
271 params.SN = response.new_sn.replace(/^SN=/, ''); 271 params.SN = response.new_sn.replace(/^SN=/, '');
272 } 272 }
273 273
274 if (response.new_ending_balance) { 274 if (response.new_ending_balance) {
275 params.BALANCE = response.new_ending_balance; 275 params.BALANCE = response.new_ending_balance;
276 } 276 }
277 277
278 if (response.new_price) { 278 if (response.new_price) {
279 params.PRICE = response.new_price; 279 params.PRICE = response.new_price;
280 } 280 }
281 281
282 logger.info('sendReply', {params: params}); 282 logger.info('sendReply', {params: params});
283 283
284 neoxmlinutil.getReverseUrl(response.PhoneNumber, function(err, reverseUrls) { 284 neoxmlinutil.getReverseUrl(response.PhoneNumber, function(err, reverseUrls) {
285 if (err) { 285 if (err) {
286 logger.warn('Fail to get reverse urls, skipping'); 286 logger.warn('Fail to get reverse urls, skipping');
287 return; 287 return;
288 } 288 }
289 289
290 if (reverseUrls.length <= 0) { 290 if (reverseUrls.length <= 0) {
291 logger.warn('No reverse urls found, skipping'); 291 logger.warn('No reverse urls found, skipping');
292 return; 292 return;
293 } 293 }
294 294
295 sendTopUpReport(reverseUrls, params, 0, 4); 295 sendTopUpReport(reverseUrls, params, 0, 4);
296 }); 296 });
297 } 297 }
298 298
299 function errorOnTopUpReport(topUpReportError) { 299 function errorOnTopUpReport(topUpReportError) {
300 var hasError = false; 300 var hasError = false;
301 301
302 if (!topUpReportError) { 302 if (!topUpReportError) {
303 return false; 303 return false;
304 } 304 }
305 305
306 try { 306 try {
307 logger.verbose('errorOnTopUpReport', {error: topUpReportError.toString()});
308 } catch(e) { }
309
310 try {
311 if (topUpReportError.body) { 307 if (topUpReportError.body) {
312 logger.verbose('errorOnTopUpReport response body: ' + topUpReportError.body); 308 logger.verbose('errorOnTopUpReport response body: ' + topUpReportError.body);
313 } 309 }
314 310
315 } catch(e) { } 311 if (topUpReportError.res && topUpReportError.res.statusCode) {
312 logger.verbose('Partner response with status code:' + topUpReportError.res.statusCode);
313 }
314 } catch(e) {}
316 315
317 try { 316 try {
318 if (topUpReportError.toString().indexOf('Invalid XML-RPC message') < 0) { 317 if (topUpReportError.toString().indexOf('Invalid XML-RPC message') < 0) {
318 logger.verbose('errorOnTopUpReport', {error: topUpReportError.toString()});
319 return true; 319 return true;
320 } 320 }
321 } 321 }
322 catch(e) { 322 catch(e) {}
323 }
324 323
325 return false; 324 return false;
326 325
327 } 326 }
328 327
329 function sendTopUpReport(reverseUrls, params, urlIdx, retry) { 328 function sendTopUpReport(reverseUrls, params, urlIdx, retry) {
330 if (retry === null || retry === undefined) { 329 if (retry === null || retry === undefined) {
331 retry = 4; 330 retry = 4;
332 } 331 }
333 332
334 if (urlIdx === null || urlIdx === undefined) { 333 if (urlIdx === null || urlIdx === undefined) {
335 urlIdx = 0; 334 urlIdx = 0;
336 } 335 }
337 336
338 if (urlIdx >= reverseUrls.length) { 337 if (urlIdx >= reverseUrls.length) {
339 logger.warn('No other reverse urls for partner available'); 338 logger.warn('No other reverse urls for partner available');
340 339
341 if (retry > 0) { 340 if (retry > 0) {
342 logger.warn('Retrying to send topUpReport to partner first url'); 341 logger.warn('Retrying to send topUpReport to partner first url');
343 setTimeout( 342 setTimeout(
344 sendTopUpReport, 343 sendTopUpReport,
345 5000, 344 5000,
346 reverseUrls, 345 reverseUrls,
347 params, 346 params,
348 0, 347 0,
349 --retry 348 --retry
350 ) 349 )
351 return; 350 return;
352 } 351 }
353 352
354 logger.warn('topUpReport retry exceed'); 353 logger.warn('topUpReport retry exceed');
355 return; 354 return;
356 } 355 }
357 356
358 var partnerUrl = url.parse(reverseUrls[urlIdx]); 357 var partnerUrl = url.parse(reverseUrls[urlIdx]);
359 358
360 var clientOptions = { 359 var clientOptions = {
361 host: partnerUrl.hostname 360 host: partnerUrl.hostname
362 , port: partnerUrl.port 361 , port: partnerUrl.port
363 , path: partnerUrl.pathname 362 , path: partnerUrl.pathname
364 }; 363 };
365 logger.info('Preparing to send topUpReport', {clientOptions: clientOptions}); 364 logger.info('Preparing to send topUpReport', {clientOptions: clientOptions});
366 365
367 var client; 366 var client;
368 if (partnerUrl.protocol == 'https:') { 367 if (partnerUrl.protocol == 'https:') {
369 client = xmlrpc.createSecureClient(clientOptions); 368 client = xmlrpc.createSecureClient(clientOptions);
370 } else { 369 } else {
371 client = xmlrpc.createClient(clientOptions); 370 client = xmlrpc.createClient(clientOptions);
372 } 371 }
373 372
374 var methodName = 'topUpReport'; 373 var methodName = 'topUpReport';
375 logger.info('Requesting topUpReport', {params: params}); 374 logger.info('Requesting topUpReport', {params: params});
376 375
377 client.methodCall(methodName, [ params ], function (topUpReportError, value) { 376 client.methodCall(methodName, [ params ], function (topUpReportError, value) {
378 377
379 if (errorOnTopUpReport(topUpReportError)) { 378 if (errorOnTopUpReport(topUpReportError)) {
380 379
381 logger.warn('Error sending topUpReport retrying another url (if available): ' + topUpReportError, {error: topUpReportError}); 380 logger.warn('Error sending topUpReport retrying another url (if available): ' + topUpReportError, {error: topUpReportError.toString()});
382 sendTopUpReport(reverseUrls, params, ++urlIdx, retry); 381 sendTopUpReport(reverseUrls, params, ++urlIdx, retry);
383 return; 382 return;
384 383
385 } 384 }
386 385
387 logger.verbose("topUpReport ACK", {value: value}); 386 logger.verbose("topUpReport ACK", {params: params, clientOptions: clientOptions});
388 387
389 }); 388 });
390 } 389 }
391 390
392 function createResponseServer() { 391 function createResponseServer() {
393 var httpServer = http.createServer(function(req, res) { 392 var httpServer = http.createServer(function(req, res) {
394 393
395 res.end(); 394 res.end();
396 395
397 var parsed_url = url.parse(req.url, true, true); 396 var parsed_url = url.parse(req.url, true, true);
398 logger.info("Hit on response server", {qs: parsed_url.query}); 397 logger.info("Hit on response server", {qs: parsed_url.query});
399 398
400 sendReply(parsed_url.query); 399 sendReply(parsed_url.query);
401 400
402 }); 401 });
403 402
404 httpServer.listen(config.res_port, function() { 403 httpServer.listen(config.res_port, function() {
405 logger.info('HTTP Response server listen on port ' + config.res_port); 404 logger.info('HTTP Response server listen on port ' + config.res_port);
406 }); 405 });