Commit 05158bd37278cc8f8405d3f03158f091ce635e1d
0 parents
Exists in
master
initial commit
Showing 9 changed files with 570 additions and 0 deletions Inline Diff
.gitignore
File was created | 1 | node_modules/ | |
2 | config.ini | ||
3 | log.txt | ||
4 | logs/log* | ||
5 |
aaa.js
File was created | 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; | ||
160 |
config.sample.ini
File was created | 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 | |||
19 |
httpserver.js
File was created | 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; | ||
91 |
index.js
File was created | 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); | ||
14 |
logs/empty
package.json
File was created | 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 | } | ||
32 |
test.js
File was created | 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 | }); | ||
34 |
xmlout.js
File was created | 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; | ||
223 |