Commit 421130545630c512eafdba54a913f4bc00f6749c
0 parents
Exists in
master
copy dari pulsakita
Showing 9 changed files with 524 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 | + |
gentong.js
... | ... | @@ -0,0 +1,159 @@ |
1 | +var url = require('url'); | |
2 | +var math = require('mathjs'); | |
3 | +var request = require('request'); | |
4 | +var xml = require("xml2js").parseString; | |
5 | + | |
6 | +var config; | |
7 | +var callbackReport; | |
8 | + | |
9 | +var max_retry = 3; | |
10 | +var sleep_before_retry = 3000; | |
11 | + | |
12 | +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; | |
13 | + | |
14 | +function topupRequest(task, retry) { | |
15 | + if (config.globals.requests_count == undefined) { | |
16 | + config.globals.requests_count = 1; | |
17 | + } else { | |
18 | + config.globals.requests_count++; | |
19 | + } | |
20 | + | |
21 | + if (config.globals.active_requests_count == undefined) { | |
22 | + config.globals.active_requests_count = 1; | |
23 | + } else { | |
24 | + config.globals.active_requests_count++; | |
25 | + } | |
26 | + | |
27 | + if (config.globals.max_active_requests_count == undefined) { | |
28 | + config.globals.max_active_requests_count = config.globals.active_requests_count; | |
29 | + } else { | |
30 | + config.globals.max_active_requests_count = math.max(config.globals.max_active_requests_count, config.globals.active_requests_count); | |
31 | + } | |
32 | + | |
33 | + | |
34 | + if (retry === undefined) { | |
35 | + retry = max_retry; | |
36 | + } | |
37 | + | |
38 | + var params = { | |
39 | + userid: config.h2h_out.userid, | |
40 | + pwd: config.h2h_out.password, | |
41 | + memberreff: task['requestId'], | |
42 | + produk: task['remoteProduct'], | |
43 | + tujuan: task['destination'] | |
44 | + }; | |
45 | + console.log('PARAMS:'); | |
46 | + console.log(params); | |
47 | + | |
48 | + request.post({url: config.h2h_out.partner, form: params}, function(err, httpResponse, httpResponseBody) { | |
49 | + | |
50 | + config.globals.active_requests_count--; | |
51 | + | |
52 | + if (err) { | |
53 | + console.log('HTTP Request Error (' + task['requestId'] + '): '); | |
54 | + console.log(err); | |
55 | + | |
56 | + if (retry) { | |
57 | + | |
58 | + console.log('Retrying trx hit (' + retry + ')'); | |
59 | + setTimeout(function() { | |
60 | + topupRequest(task, retry - 1); | |
61 | + }, sleep_before_retry); | |
62 | + | |
63 | + } else { | |
64 | + callbackReport(task['requestId'], '40', 'Gangguan koneksi ke suplier'); | |
65 | + } | |
66 | + return; | |
67 | + } | |
68 | + | |
69 | + console.log('Server Response: '); | |
70 | + console.log(httpResponseBody); | |
71 | + | |
72 | + if (httpResponseBody.indexOf('502 Proxy Error') >= 0) { | |
73 | + callbackReport(task['requestId'], '40', '502 Proxy error'); | |
74 | + return; | |
75 | + } | |
76 | + | |
77 | + xml(httpResponseBody, function(err, result) { | |
78 | + if (err) { | |
79 | + console.log('Gagal parsing XML respon server'); | |
80 | + topupRequest(task, retry - 1); | |
81 | + return; | |
82 | + } | |
83 | + | |
84 | + console.log('Response Data:'); | |
85 | + console.log(result); | |
86 | + | |
87 | + var response_code = result.respon.rc[0].replace(/^00/, ''); | |
88 | + | |
89 | + var token = ''; | |
90 | + if (result.respon.token) { | |
91 | + token = result.respon.token[0]; | |
92 | + } | |
93 | + | |
94 | + var data_pelanggan = ''; | |
95 | + if (result.respon.data) { | |
96 | + data_pelanggan = result.respon.data[0].replace(/ /g, '-'); | |
97 | + } | |
98 | + | |
99 | + var message = result.respon.pesan[0].replace(/\n/g, ' '); | |
100 | + | |
101 | + if (token && data_pelanggan) { | |
102 | + var sn = token + '/' + data_pelanggan; | |
103 | + message = 'SN=' + sn + '; ' + message; | |
104 | + } | |
105 | + | |
106 | + callbackReport(task['requestId'], response_code, message); | |
107 | + | |
108 | + if (response_code == '54' || response_code == '68') { | |
109 | + setTimeout(function() { | |
110 | + topupRequest(task, 5); | |
111 | + }, 60000); | |
112 | + } | |
113 | + | |
114 | + }); | |
115 | + }); | |
116 | +} | |
117 | + | |
118 | +function start(_config, _callbackReport) { | |
119 | + config = _config; | |
120 | + callbackReport = _callbackReport | |
121 | +} | |
122 | + | |
123 | +function parseSN(message) { | |
124 | + var sn_regex = new RegExp(config.h2h_out.sn_pattern); | |
125 | + var sn_match = message.match(sn_regex); | |
126 | + | |
127 | + //console.log('SN MATCH:'); | |
128 | + //console.log(sn_match); | |
129 | + | |
130 | + if (sn_match <= 0) { | |
131 | + console.log('SN Not found: ' + message); | |
132 | + return ''; | |
133 | + } | |
134 | + | |
135 | + var sn = sn_match[0]; | |
136 | + var sn_remove_patterns = config.h2h_out.sn_remove_patterns.split(config.h2h_out.sn_remove_patterns_separator); | |
137 | + //console.log('SN REMOVE PATTERNS:'); | |
138 | + //console.log (sn_remove_patterns); | |
139 | + | |
140 | + var count = sn_remove_patterns.length; | |
141 | + | |
142 | + for(var i = 0; i < count; i++) { | |
143 | + sn = sn.replace(sn_remove_patterns[i], ''); | |
144 | + } | |
145 | + | |
146 | + return sn.trim(); | |
147 | +} | |
148 | + | |
149 | +function parseResult(message) { | |
150 | + var data; | |
151 | + xml(message, function(err, result) { | |
152 | + data = result; | |
153 | + }); | |
154 | + return data; | |
155 | +} | |
156 | + | |
157 | +exports.start = start; | |
158 | +exports.topupRequest = topupRequest; | |
159 | +exports.parseResult = parseResult; |
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 partner = require('./httppulsakita.js'); | |
11 | + | |
12 | +partner.start(config, aaa.callbackReport); | |
13 | +aaa.start(config, partner); |
logs/empty
package.json
... | ... | @@ -0,0 +1,32 @@ |
1 | +{ | |
2 | + "name": "sate24-to-gentong", | |
3 | + "version": "0.0.1", | |
4 | + "description": "ST24 to Gentong 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-gentong.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 | + "xml2js": "~0.4.9" | |
31 | + } | |
32 | +} |
test.js
... | ... | @@ -0,0 +1,49 @@ |
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 | + | |
35 | +describe('aaa', function() { | |
36 | + var partner = require('./httppulsakita'); | |
37 | + | |
38 | + describe("#parseResult()", function() { | |
39 | + message = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><respon><tanggal>2015/6/16 15:43:35</tanggal><idagen>P0039</idagen><refid>AEE15B32987941D89FFF4BC7EF676C13</refid><produk>PLN20</produk><tujuan>14204279369</tujuan><rc>0000</rc><data> </data><token> </token><pesan>#14836 PLN20 ke:14204279369 SUKSES. SN:3520-2887-6627-6699-4826/TestDummyPanjang6955555/P1/7000VA/32,4. \ | |
40 | +Sisa saldo Rp. 5,000,000 - Rp. 18,700 = Rp. 4,981,300</pesan></respon>'; | |
41 | + | |
42 | + data = partner.parseResult(message); | |
43 | + console.log(data); | |
44 | + | |
45 | + it('should return 2015/6/16 15:43:35', function() { | |
46 | + assert.equal('2015/6/16 15:43:35', data.respon.tanggal); | |
47 | + }); | |
48 | + }); | |
49 | +}); |