Commit 05158bd37278cc8f8405d3f03158f091ce635e1d
0 parents
Exists in
master
initial commit
Showing 9 changed files with 570 additions and 0 deletions Side-by-side Diff
.gitignore
aaa.js
... | ... | @@ -0,0 +1,159 @@ |
1 | +var request = require('request'); | |
2 | +var strftime = require('strftime'); | |
3 | + | |
4 | +var max_retry = 10; | |
5 | +var sleep_before_retry = 3000; | |
6 | + | |
7 | +var config; | |
8 | +var partner; | |
9 | + | |
10 | +var available_products = []; | |
11 | + | |
12 | +function unaliasResponseCode(response_code, config_responsecode_alias) { | |
13 | + if (config_responsecode_alias == undefined && config && config.h2h_out && config.h2h_out.responsecode_alias) { | |
14 | + config_responsecode_alias = config.h2h_out.responsecode_alias; | |
15 | + } | |
16 | + | |
17 | + if (!config_responsecode_alias) { | |
18 | + return response_code; | |
19 | + } | |
20 | + | |
21 | + items = config_responsecode_alias.split(','); | |
22 | + items_count = items.length; | |
23 | + | |
24 | + for (var i=0; i < items_count; i++) { | |
25 | + codes = items[i].split(':'); | |
26 | + | |
27 | + if (codes.length <= 0) { continue; } | |
28 | + | |
29 | + if (response_code == codes[0]) { | |
30 | + console.log('Change response code from ' + codes[0] + ' to ' + codes[1]); | |
31 | + return codes[1]; | |
32 | + } | |
33 | + } | |
34 | + | |
35 | + return response_code; | |
36 | +} | |
37 | + | |
38 | +function pullCity() { | |
39 | + var url = config.globals.aaa_host + '/pull_city'; | |
40 | + console.log('Pull cities from AAA - ' + url); | |
41 | + request(url, function (error, response, body) { | |
42 | + if (!error && response.statusCode == 200) { | |
43 | + //console.log('city=' + body); | |
44 | + } else { | |
45 | + console.log('Error in pull city'); | |
46 | + } | |
47 | + }); | |
48 | +} | |
49 | + | |
50 | +function pullProduct() { | |
51 | + var url = config.globals.aaa_host + '/pull_product?opr_name=' + config.globals.operators; | |
52 | + console.log('Pull products from AAA - ' + url); | |
53 | + | |
54 | + request(url, function (error, response, body) { | |
55 | + if (error || response.statusCode != 200) { | |
56 | + console.log('Error in pull products'); | |
57 | + return; | |
58 | + } | |
59 | + | |
60 | + var productsAndOperators = body.split(';'); | |
61 | + var productsCount = productsAndOperators.length; | |
62 | + | |
63 | + for (var i=0; i < productsCount; i++) { | |
64 | + var product = productsAndOperators[i].split(',', 1)[0]; | |
65 | + available_products.push(product); | |
66 | + } | |
67 | + //console.log(available_products); | |
68 | + }); | |
69 | +} | |
70 | + | |
71 | +function pull() { | |
72 | + var url = config.globals.aaa_host | |
73 | + + '/pull?city=ALL&nom=' + config.globals.products | |
74 | + + '&chip_info=' + config.globals.gateway_name; | |
75 | + | |
76 | + //console.log('AAA PULL - ' + url); | |
77 | + request(url, function (error, response, body) { | |
78 | + if (!error && response.statusCode == 200) { | |
79 | + if (body == 'NONE') { | |
80 | + return; | |
81 | + } | |
82 | + console.log(body); | |
83 | + | |
84 | + var result = body.split(';'); | |
85 | + if (result[0] != 'OK') { | |
86 | + return; | |
87 | + } | |
88 | + | |
89 | + var task = []; | |
90 | + task['requestId'] = result[1]; | |
91 | + task['timestamp'] = result[3]; | |
92 | + task['destination'] = result[4]; | |
93 | + task['product'] = result[7]; | |
94 | + | |
95 | + if (config.products[task['product']] !== undefined) { | |
96 | + task['remoteProduct'] = config.products[task['product']]; | |
97 | + } else { | |
98 | + task['remoteProduct'] = task['product']; | |
99 | + } | |
100 | + | |
101 | + partner.topupRequest(task); | |
102 | + | |
103 | + } else { | |
104 | + console.log('Error in pull task'); | |
105 | + return; | |
106 | + } | |
107 | + }); | |
108 | +} | |
109 | + | |
110 | +function pullLoop() { | |
111 | + if (!config.globals.pause) { | |
112 | + pull(); | |
113 | + } | |
114 | + | |
115 | + setTimeout(pullLoop, config.globals.interval); | |
116 | +} | |
117 | + | |
118 | +function callbackReport(requestId, responseCode, message, retry) { | |
119 | + if (retry === undefined) { | |
120 | + retry = max_retry; | |
121 | + } | |
122 | + | |
123 | + responseCode = unaliasResponseCode(responseCode); | |
124 | + | |
125 | + timestamp = strftime('%Y%m%d%H%M%S'); | |
126 | + var url = config.globals.aaa_host | |
127 | + + '/topup?trans_id=' + requestId | |
128 | + + '&trans_date' + timestamp | |
129 | + + '&trans_date=' + timestamp | |
130 | + + '&resp_code=' + responseCode | |
131 | + + '&ussd_msg=' + config.globals.gateway_name | |
132 | + + '$' + encodeURIComponent(message); | |
133 | + | |
134 | + console.log('Report to AAA - ' + url); | |
135 | + request(url, function (error, response, body) { | |
136 | + if (error || response.statusCode != 200) { | |
137 | + console.log('Error report to AAA'); | |
138 | + | |
139 | + if (retry) { | |
140 | + console.log('Retrying to report to AAA (' + retry + ')'); | |
141 | + callbackReport(requestId, responseCode, message, retry - 1); | |
142 | + } | |
143 | + } | |
144 | + }); | |
145 | +} | |
146 | + | |
147 | +function start(_config, _partner) { | |
148 | + config = _config; | |
149 | + partner = _partner; | |
150 | + | |
151 | + pullCity(); | |
152 | + pullProduct(); | |
153 | + | |
154 | + setTimeout(pullLoop, 10 * 1000); | |
155 | +} | |
156 | + | |
157 | +exports.start = start; | |
158 | +exports.callbackReport = callbackReport; | |
159 | +exports.unaliasResponseCode = unaliasResponseCode; |
config.sample.ini
... | ... | @@ -0,0 +1,18 @@ |
1 | +[globals] | |
2 | +operators=TEST,XL_ALTERNATIF | |
3 | +products=TST1 | |
4 | +gateway_name=NODEJS-DEV | |
5 | +aaa_host=http://172.23.0.12:4250 | |
6 | +interval=1000 | |
7 | +admin_port=17456 | |
8 | + | |
9 | +[h2h_out] | |
10 | +partner=https://172.23.0.12:6789 | |
11 | +userid=R97DEV | |
12 | +password=czcb | |
13 | +listen_port=13522 | |
14 | +check_interval=2000 | |
15 | + | |
16 | +[products] | |
17 | +TST1=TR1 | |
18 | + |
httpserver.js
... | ... | @@ -0,0 +1,90 @@ |
1 | +var http = require('http'); | |
2 | +var nsr = require('node-simple-router'); | |
3 | +var router = nsr(); | |
4 | + | |
5 | +var config; | |
6 | +var httpServer; | |
7 | + | |
8 | +function start(_config) { | |
9 | + if (_config != undefined) { | |
10 | + config = _config; | |
11 | + } | |
12 | + listenPort = config.globals.admin_port; | |
13 | + | |
14 | + router.get("/info", function(request, response) { | |
15 | + response.setHeader("Content-Type", "text/plain"); | |
16 | + response.write('CHIPINFO / GATEWAY NAME: ' + config.globals.gateway_name + "\n"); | |
17 | + response.write('PRODUCTS: ' + config.globals.products + "\n"); | |
18 | + response.write('AAA HOST: ' + config.globals.aaa_host + "\n"); | |
19 | + response.write('PARTNER: ' + config.h2h_out.partner + "\n"); | |
20 | + response.write('PAUSED: ' + config.globals.pause + "\n"); | |
21 | + response.write('UPTIME: ' + process.uptime() + "\n"); | |
22 | + response.write('REQUESTS COUNT: ' + config.globals.requests_count + "\n"); | |
23 | + response.write('ACTIVE REQUESTS COUNT: ' + config.globals.active_requests_count + "\n"); | |
24 | + response.write('MAX ACTIVE REQUESTS COUNT: ' + config.globals.max_active_requests_count + "\n"); | |
25 | + | |
26 | + response.end(); | |
27 | + }); | |
28 | + | |
29 | + router.get("/pause/:apikey", function(request, response) { | |
30 | + if (!config.globals.apikey) { | |
31 | + response.end('Undefined APIKEY on config'); | |
32 | + return; | |
33 | + } | |
34 | + | |
35 | + if (request.params.apikey != config.globals.apikey) { | |
36 | + response.end('Invalid APIKEY'); | |
37 | + return; | |
38 | + } | |
39 | + | |
40 | + config.globals.pause = 1; | |
41 | + response.end('Paused'); | |
42 | + }); | |
43 | + | |
44 | + router.get("/resume/:apikey", function(request, response) { | |
45 | + if (!config.globals.apikey) { | |
46 | + response.end('Undefined APIKEY on config'); | |
47 | + return; | |
48 | + } | |
49 | + | |
50 | + if (request.params.apikey != config.globals.apikey) { | |
51 | + response.end('Invalid APIKEY'); | |
52 | + return; | |
53 | + } | |
54 | + | |
55 | + delete config.globals.pause; | |
56 | + response.end('Resume'); | |
57 | + }); | |
58 | + | |
59 | + router.get("/reset-stats/:apikey", function(request, response) { | |
60 | + if (!config.globals.apikey) { | |
61 | + response.end('Undefined APIKEY on config'); | |
62 | + return; | |
63 | + } | |
64 | + | |
65 | + if (request.params.apikey != config.globals.apikey) { | |
66 | + response.end('Invalid APIKEY'); | |
67 | + return; | |
68 | + } | |
69 | + | |
70 | + config.globals.max_active_requests_count = 0; | |
71 | + | |
72 | + response.writeHead(307, { | |
73 | + 'Location': '/info' | |
74 | + }); | |
75 | + | |
76 | + response.end(); | |
77 | + }); | |
78 | + | |
79 | + httpServer = http.createServer(router).listen(listenPort); | |
80 | + console.log('HTTP server listens on port ' + listenPort); | |
81 | + | |
82 | + return httpServer; | |
83 | +} | |
84 | + | |
85 | +function setConfig(_config) { | |
86 | + config = _config; | |
87 | +} | |
88 | + | |
89 | +exports.start = start; | |
90 | +exports.setConfig = setConfig; |
index.js
... | ... | @@ -0,0 +1,13 @@ |
1 | +var iniparser = require('iniparser'); | |
2 | +var config = iniparser.parseSync('./config.ini'); | |
3 | + | |
4 | +var aaaHost = config.globals.aaa_host; | |
5 | + | |
6 | +HttpServer = require('./httpserver.js'); | |
7 | +var httpServer = HttpServer.start(config); | |
8 | + | |
9 | +var aaa = require('./aaa.js'); | |
10 | +var xmlout = require('./xmlout.js'); | |
11 | + | |
12 | +xmlout.start(config, aaa.callbackReport); | |
13 | +aaa.start(config, xmlout); |
logs/empty
package.json
... | ... | @@ -0,0 +1,31 @@ |
1 | +{ | |
2 | + "name": "sate24-to-sate24", | |
3 | + "version": "0.0.1", | |
4 | + "description": "ST24 to ST24 H2H OUT", | |
5 | + "main": "index.js", | |
6 | + "scripts": { | |
7 | + "test": "mocha" | |
8 | + }, | |
9 | + "repository": { | |
10 | + "type": "git", | |
11 | + "url": "git@gitlab.kodesumber.com:reload97/sate24-to-sate24.git" | |
12 | + }, | |
13 | + "keywords": [ | |
14 | + "st24", | |
15 | + "reload97", | |
16 | + "ppob", | |
17 | + "h2h", | |
18 | + "m2m", | |
19 | + "xmlrpc" | |
20 | + ], | |
21 | + "author": "Adhidarma Hadiwinoto <gua@adhisimon.org>", | |
22 | + "license": "BSD", | |
23 | + "dependencies": { | |
24 | + "mocha": "~2.2.5", | |
25 | + "request": "~2.57.0", | |
26 | + "strftime": "~0.9.2", | |
27 | + "iniparser": "~1.0.5", | |
28 | + "mathjs": "~1.7.0", | |
29 | + "xmlrpc": "~1.3.1" | |
30 | + } | |
31 | +} |
test.js
... | ... | @@ -0,0 +1,33 @@ |
1 | +var assert = require("assert"); | |
2 | + | |
3 | + | |
4 | +describe('aaa', function() { | |
5 | + var aaa = require('./aaa'); | |
6 | + | |
7 | + describe("#unaliasResponseCode()", function() { | |
8 | + it('should return 68', function() { | |
9 | + assert.equal('68', aaa.unaliasResponseCode('01', '01:68')); | |
10 | + }); | |
11 | + | |
12 | + it('should return 68', function() { | |
13 | + assert.equal('68', aaa.unaliasResponseCode('68', '01:68')); | |
14 | + }); | |
15 | + | |
16 | + it('should return 00', function() { | |
17 | + assert.equal('00', aaa.unaliasResponseCode('00', '01:68')); | |
18 | + }); | |
19 | + | |
20 | + it('should return 40', function() { | |
21 | + assert.equal('40', aaa.unaliasResponseCode('40', '')); | |
22 | + }); | |
23 | + | |
24 | + it('should return 40', function() { | |
25 | + assert.equal('40', aaa.unaliasResponseCode('40', '')); | |
26 | + }); | |
27 | + | |
28 | + it('should return 40', function() { | |
29 | + assert.equal('40', aaa.unaliasResponseCode('40')); | |
30 | + }); | |
31 | + | |
32 | + }); | |
33 | +}); |
xmlout.js
... | ... | @@ -0,0 +1,222 @@ |
1 | +var xmlrpc = require('xmlrpc'); | |
2 | +var url = require('url'); | |
3 | +var math = require('mathjs'); | |
4 | + | |
5 | +var config; | |
6 | +var callbackReport; | |
7 | + | |
8 | +var max_retry = 2; | |
9 | +var sleep_before_retry = 2000; | |
10 | + | |
11 | +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; | |
12 | + | |
13 | +function topupRequest(task, retry) { | |
14 | + if (config.globals.requests_count == undefined) { | |
15 | + config.globals.requests_count = 1; | |
16 | + } else { | |
17 | + config.globals.requests_count++; | |
18 | + } | |
19 | + | |
20 | + if (config.globals.active_requests_count == undefined) { | |
21 | + config.globals.active_requests_count = 1; | |
22 | + } else { | |
23 | + config.globals.active_requests_count++; | |
24 | + } | |
25 | + | |
26 | + if (config.globals.max_active_requests_count == undefined) { | |
27 | + config.globals.max_active_requests_count = config.globals.active_requests_count; | |
28 | + } else { | |
29 | + config.globals.max_active_requests_count = math.max(config.globals.max_active_requests_count, config.globals.active_requests_count); | |
30 | + } | |
31 | + | |
32 | + | |
33 | + if (retry === undefined) { | |
34 | + retry = max_retry; | |
35 | + } | |
36 | + | |
37 | + var partnerUrl = url.parse(config.h2h_out.partner); | |
38 | + var clientOptions = { | |
39 | + host: partnerUrl.hostname | |
40 | + , port: partnerUrl.port | |
41 | + , path: partnerUrl.pathname | |
42 | + }; | |
43 | + console.log('XMLRPC client options:'); | |
44 | + console.log(clientOptions); | |
45 | + | |
46 | + var client; | |
47 | + if (partnerUrl.protocol == 'https:') { | |
48 | + console.log('Use SSL'); | |
49 | + client = xmlrpc.createSecureClient(clientOptions); | |
50 | + } else { | |
51 | + console.log('Not using SSL'); | |
52 | + client = xmlrpc.createClient(clientOptions); | |
53 | + } | |
54 | + | |
55 | + var methodName = 'topUpRequest'; | |
56 | + console.log('methodName: ' + methodName); | |
57 | + | |
58 | + var params = { | |
59 | + MSISDN: config.h2h_out.userid, | |
60 | + REQUESTID: task['requestId'], | |
61 | + PIN: config.h2h_out.password, | |
62 | + NOHP: task['destination'], | |
63 | + NOM: task['remoteProduct'] | |
64 | + }; | |
65 | + console.log('PARAMS:'); | |
66 | + console.log(params); | |
67 | + | |
68 | + | |
69 | + client.methodCall(methodName, [ params ], function (error, value) { | |
70 | + | |
71 | + if (config.globals.active_requests_count == undefined) { | |
72 | + config.globals.active_requests_count = 0; | |
73 | + } else { | |
74 | + config.globals.active_requests_count--; | |
75 | + } | |
76 | + | |
77 | + // Results of the method response | |
78 | + if (error) { | |
79 | + console.log('XMLRPC Client Error (' + task['requestId'] + '): '); | |
80 | + console.log(error); | |
81 | + | |
82 | + if (retry) { | |
83 | + | |
84 | + console.log('Retrying topUpRequest (' + retry + ')'); | |
85 | + setTimeout(function() { | |
86 | + topupRequest(task, retry - 1); | |
87 | + }, sleep_before_retry); | |
88 | + | |
89 | + } else { | |
90 | + callbackReport(task['requestId'], '40', 'Gangguan koneksi ke suplier'); | |
91 | + } | |
92 | + return; | |
93 | + } | |
94 | + | |
95 | + console.log('Method response for \'' + methodName + '\': ') | |
96 | + console.log(value); | |
97 | + | |
98 | + if (value['RESPONSECODE'] == '00' && config.h2h_out.parse_sn == 'YES') { | |
99 | + value['MESSAGE'] = 'SN=' + parseSN(value['MESSAGE']) + '; ' + value['MESSAGE']; | |
100 | + //console.log('Message with SN: ' + value['MESSAGE']); | |
101 | + } | |
102 | + | |
103 | + callbackReport(value['REQUESTID'], value['RESPONSECODE'], value['MESSAGE']); | |
104 | + }); | |
105 | +} | |
106 | + | |
107 | +function createServer() { | |
108 | + | |
109 | + console.log('Creating XML-RPC server on port ' + config.h2h_out.listen_port); | |
110 | + var serverOptions = { | |
111 | + port: config.h2h_out.listen_port | |
112 | + }; | |
113 | + | |
114 | + var server = xmlrpc.createServer(serverOptions); | |
115 | + | |
116 | + server.on('NotFound', function (method, params) { | |
117 | + console.log(method + ' is not found on our XML-RPC server'); | |
118 | + console.log('params:'); | |
119 | + console.log(params); | |
120 | + }); | |
121 | + | |
122 | + server.on('topUpReport', function (err, params, callback) { | |
123 | + console.log('RECEIVE topUpReport, params:'); | |
124 | + console.log(params); | |
125 | + | |
126 | + var paramscount = params.length; | |
127 | + for (var i = 0; i < paramscount; i++) { | |
128 | + var value = params[i]; | |
129 | + | |
130 | + if (value['RESPONSECODE'] == '00' && config.h2h_out.parse_sn == 'YES') { | |
131 | + value['MESSAGE'] = 'SN=' + parseSN(value['MESSAGE']) + '; ' + value['MESSAGE']; | |
132 | + //console.log('Message with SN: ' + value['MESSAGE']); | |
133 | + } | |
134 | + | |
135 | + callbackReport(value['REQUESTID'], value['RESPONSECODE'], value['MESSAGE']); | |
136 | + } | |
137 | + | |
138 | + callback(null, 'ACK REPORT OK'); | |
139 | + }) | |
140 | + | |
141 | +} | |
142 | + | |
143 | +function checkStatus(task) { | |
144 | + var partnerUrl = url.parse(config.h2h_out.partner); | |
145 | + var clientOptions = { | |
146 | + host: partnerUrl.hostname | |
147 | + , port: partnerUrl.port | |
148 | + , path: partnerUrl.pathname | |
149 | + }; | |
150 | + console.log('XMLRPC client options:'); | |
151 | + console.log(clientOptions); | |
152 | + | |
153 | + var client; | |
154 | + if (partnerUrl.protocol == 'https:') { | |
155 | + console.log('Use SSL'); | |
156 | + client = xmlrpc.createSecureClient(clientOptions); | |
157 | + } else { | |
158 | + console.log('Not using SSL'); | |
159 | + client = xmlrpc.createClient(clientOptions); | |
160 | + } | |
161 | + | |
162 | + var methodName = 'topUpInquiry'; | |
163 | + console.log('methodName: ' + methodName); | |
164 | + | |
165 | + var params = { | |
166 | + MSISDN: config.h2h_out.userid, | |
167 | + REQUESTID: task['requestId'], | |
168 | + PIN: config.h2h_out.password, | |
169 | + NOHP: task['destination'] | |
170 | + }; | |
171 | + console.log('PARAMS:'); | |
172 | + console.log(params); | |
173 | + | |
174 | + client.methodCall(methodName, [ params ], function (error, value) { | |
175 | + // Results of the method response | |
176 | + if (error) { | |
177 | + console.log('Error: '); | |
178 | + console.log(error); | |
179 | + return; | |
180 | + } | |
181 | + console.log('Method response for \'' + methodName + '\': ') | |
182 | + console.log(value); | |
183 | + | |
184 | + callbackReport(value['REQUESTID'], value['RESPONSECODE'], value['MESSAGE']); | |
185 | + }); | |
186 | +} | |
187 | + | |
188 | +function start(_config, _callbackReport) { | |
189 | + config = _config; | |
190 | + callbackReport = _callbackReport | |
191 | + | |
192 | + createServer(); | |
193 | +} | |
194 | + | |
195 | +function parseSN(message) { | |
196 | + var sn_regex = new RegExp(config.h2h_out.sn_pattern); | |
197 | + var sn_match = message.match(sn_regex); | |
198 | + | |
199 | + //console.log('SN MATCH:'); | |
200 | + //console.log(sn_match); | |
201 | + | |
202 | + if (sn_match <= 0) { | |
203 | + console.log('SN Not found: ' + message); | |
204 | + return ''; | |
205 | + } | |
206 | + | |
207 | + var sn = sn_match[0]; | |
208 | + var sn_remove_patterns = config.h2h_out.sn_remove_patterns.split(config.h2h_out.sn_remove_patterns_separator); | |
209 | + //console.log('SN REMOVE PATTERNS:'); | |
210 | + //console.log (sn_remove_patterns); | |
211 | + | |
212 | + var count = sn_remove_patterns.length; | |
213 | + | |
214 | + for(var i = 0; i < count; i++) { | |
215 | + sn = sn.replace(sn_remove_patterns[i], ''); | |
216 | + } | |
217 | + | |
218 | + return sn.trim(); | |
219 | +} | |
220 | + | |
221 | +exports.start = start; | |
222 | +exports.topupRequest = topupRequest; |