Commit d11a8cb48ba454a2f9a4995cbaae7aa54830f85b
0 parents
Exists in
master
salin dari datacell
Showing 6 changed files with 421 additions and 0 deletions Side-by-side Diff
.gitignore
... | ... | @@ -0,0 +1 @@ |
1 | +node_modules/ |
README
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('sate24/httpserver.js'); | |
7 | +var httpServer = HttpServer.start(config); | |
8 | + | |
9 | +var aaa = require('sate24/aaa.js'); | |
10 | +var partner = require('./partner-datacell.js'); | |
11 | + | |
12 | +partner.start(config, aaa.callbackReport); | |
13 | +aaa.start(config, partner); |
package.json
... | ... | @@ -0,0 +1,30 @@ |
1 | +{ | |
2 | + "name": "sate24-to-datacell", | |
3 | + "version": "0.0.1", | |
4 | + "description": "ST24 to Datacell", | |
5 | + "main": "index.js", | |
6 | + "scripts": { | |
7 | + "test": "mocha" | |
8 | + }, | |
9 | + "repository": { | |
10 | + "type": "git", | |
11 | + "url": "git@gitlab.kodesumber.com:reload97/sate24-to-datacell.git" | |
12 | + }, | |
13 | + "keywords": [ | |
14 | + "datacell", | |
15 | + "ppob", | |
16 | + "st24" | |
17 | + ], | |
18 | + "author": "Adhidarma Hadiwinoto <gua@adhisimon.org>", | |
19 | + "license": "BSD", | |
20 | + "dependencies": { | |
21 | + "sate24": "git+http://git@gitlab.kodesumber.com/reload97/node-sate24.git", | |
22 | + "iniparser": "~1.0.5", | |
23 | + "base64-xor": "~0.10.0", | |
24 | + "request": "~2.60.0", | |
25 | + "mathjs": "~1.7.1", | |
26 | + "xml": "~1.0.0", | |
27 | + "xml2js": "~0.4.9", | |
28 | + "strftime": "~0.9.2" | |
29 | + } | |
30 | +} |
partner-datacell.js
... | ... | @@ -0,0 +1,328 @@ |
1 | +var http = require('http'); | |
2 | +var url = require('url'); | |
3 | +var math = require('mathjs'); | |
4 | +var xml = require('xml'); | |
5 | +var xml2js = require('xml2js').parseString; | |
6 | +var strftime = require('strftime'); | |
7 | +var xor = require('base64-xor'); | |
8 | +var request = require('request'); | |
9 | + | |
10 | +var config; | |
11 | +var callbackReport; | |
12 | + | |
13 | +var max_retry = 2; | |
14 | +var sleep_before_retry = 2000; | |
15 | + | |
16 | +var trx_balances = {}; | |
17 | +var trx_prices = {}; | |
18 | + | |
19 | +function calculateSignature(userid, password, msisdn, timestamp) { | |
20 | + var a = msisdn.substr(msisdn.length - 4) + timestamp; | |
21 | + var b = userid.substr(0, 4) + password; | |
22 | + | |
23 | + return xor.encode(a,b); | |
24 | +} | |
25 | + | |
26 | +function calculateBalanceSignature(userid, password, timestamp) { | |
27 | + var a = '0000' + timestamp; | |
28 | + var b = userid.substr(0, 4) + password; | |
29 | + | |
30 | + return xor.encode(a,b); | |
31 | +} | |
32 | + | |
33 | + | |
34 | +function createPayload(task) { | |
35 | + var timestamp = strftime('%H%M%S'); | |
36 | + | |
37 | + var payload = { | |
38 | + datacell: [ | |
39 | + { perintah: 'charge'}, | |
40 | + {oprcode: task['remoteProduct']}, | |
41 | + {userid: config.h2h_out.userid}, | |
42 | + {time: timestamp}, | |
43 | + {msisdn: task['destination']}, | |
44 | + {ref_trxid: task['requestId']}, | |
45 | + {sgn: calculateSignature(config.h2h_out.userid, config.h2h_out.password, task['destination'], timestamp)} | |
46 | + ] | |
47 | + }; | |
48 | + | |
49 | + console.log(payload); | |
50 | + return "<?xml version=\"1.0\" ?>\n" + xml(payload); | |
51 | +} | |
52 | + | |
53 | +function topupRequest(task, retry) { | |
54 | + //balanceCheck(); | |
55 | + | |
56 | + var payload_xml = createPayload(task); | |
57 | + //console.log(payload_xml); | |
58 | + | |
59 | + var postRequest = { | |
60 | + host: "202.152.62.2", | |
61 | + path: "/RELOAD97.php", | |
62 | + port: 7713, | |
63 | + method: "POST", | |
64 | + headers: { | |
65 | + 'Content-Type': 'text/xml', | |
66 | + 'Content-Length': Buffer.byteLength(payload_xml) | |
67 | + } | |
68 | + }; | |
69 | + | |
70 | + var buffer = ""; | |
71 | + var req = http.request( postRequest, function( res ) { | |
72 | + | |
73 | + console.log('Status code: ' + res.statusCode ); | |
74 | + var buffer = ""; | |
75 | + res.on( "data", function( data ) { buffer = buffer + data; } ); | |
76 | + res.on( "end", function( data ) { | |
77 | + topupResponseHandler(buffer); | |
78 | + }); | |
79 | + | |
80 | + }); | |
81 | + | |
82 | + req.on('error', function(e) { | |
83 | + console.log('problem with request: ' + e.message); | |
84 | + callbackReport(task['requestId'], '40', e.message); | |
85 | + }); | |
86 | + | |
87 | + req.write( payload_xml ); | |
88 | + req.end(); | |
89 | +} | |
90 | + | |
91 | +function topupResponseHandler(body, request_id) { | |
92 | + xml2js(body, function (err, result) { | |
93 | + if (err) { | |
94 | + console.log(body); | |
95 | + callbackReport(request_id, '40', buffer); | |
96 | + return; | |
97 | + } | |
98 | + | |
99 | + console.log(result); | |
100 | + | |
101 | + request_id = result.datacell.ref_trxid[0].trim(); | |
102 | + | |
103 | + var response_code = '68'; | |
104 | + | |
105 | + var message = ''; | |
106 | + try { | |
107 | + if (result.datacell.message && result.datacell.message.length > 0) { | |
108 | + message = result.datacell.message[0].trim(); | |
109 | + } else if (result.datacell.msg && result.datacell.msg.length > 0) { | |
110 | + message = result.datacell.msg[0].trim(); | |
111 | + } | |
112 | + } | |
113 | + catch(err) { | |
114 | + message = 'exception saat parsing message'; | |
115 | + } | |
116 | + | |
117 | + if (result.datacell.resultcode && result.datacell.resultcode[0] == '999') { | |
118 | + response_code = '40'; | |
119 | + } | |
120 | + | |
121 | + if (message.indexOf('Nomor tujuan salah') >= 0) { | |
122 | + response_code = '14'; | |
123 | + } else if (message.indexOf('*GAGAL, transaksi yang sama sudah ada dalam 10 menit') >= 0) { | |
124 | + response_code = '55'; | |
125 | + } else if (message.indexOf('saldo sdh dikembalikan') >= 0) { | |
126 | + response_code = '40' | |
127 | + } else if (message.indexOf('Trx dpt diulang') >= 0) { | |
128 | + response_code = '40' | |
129 | + } else if (message.indexOf('SUKSES SN Operator:') >= 0) { | |
130 | + response_code = '00'; | |
131 | + | |
132 | + var sn = parseSN(message); | |
133 | + console.log ('SN Operator: ' + sn); | |
134 | + | |
135 | + /* | |
136 | + if (!sn) { | |
137 | + | |
138 | + console.log('Missing real operator SN, using SN from suplier'); | |
139 | + try { | |
140 | + sn = result.datacell.trxid[0].trim(); | |
141 | + } | |
142 | + catch(err) { | |
143 | + sn = ''; | |
144 | + } | |
145 | + } | |
146 | + */ | |
147 | + | |
148 | + if (sn) { | |
149 | + message = 'SN=' + sn + '; ' + message; | |
150 | + } else { | |
151 | + message = 'SN belum didapat. ' + message; | |
152 | + response_code = '68'; | |
153 | + } | |
154 | + } | |
155 | + | |
156 | + | |
157 | + var price = priceFromMessage(message); | |
158 | + if (price != null) { | |
159 | + console.log('Harga: ' + price); | |
160 | + trx_prices[request_id] = price; | |
161 | + setTimeout(deleteTrxPrice, 3 * 24 * 3600 * 1000, request_id); | |
162 | + } else if (response_code == '00' && trx_prices[request_id] !== undefined) { | |
163 | + price = trx_prices[request_id]; | |
164 | + console.log('Harga: ' + price); | |
165 | + message = message + ' -- Harga: ' + price; | |
166 | + } | |
167 | + | |
168 | + var balance = balanceFromMessage(message); | |
169 | + if (balance != null) { | |
170 | + console.log('Saldo: ' + balance); | |
171 | + trx_balances[request_id] = balance; | |
172 | + setTimeout(deleteTrxBalance, 3 * 24 * 3600 * 1000, request_id); | |
173 | + } else if (response_code == '00' && trx_balances[request_id] !== undefined) { | |
174 | + balance = trx_balances[request_id]; | |
175 | + console.log('Saldo: ' + balance); | |
176 | + message = message + ' -- Saldo: ' + balance; | |
177 | + } | |
178 | + | |
179 | + callbackReport(request_id, response_code, message); | |
180 | + }); | |
181 | +} | |
182 | + | |
183 | +function deleteTrxPrice(request_id) { | |
184 | + delete trx_prices[request_id]; | |
185 | +} | |
186 | + | |
187 | +function deleteTrxBalance(request_id) { | |
188 | + delete trx_balances[request_id]; | |
189 | +} | |
190 | + | |
191 | +function parseSN(message) { | |
192 | + var results = message.match(/SN Operator: .+ SN Kami/); | |
193 | + if (!results || results.length <= 0) { | |
194 | + return ''; | |
195 | + } | |
196 | + | |
197 | + var result = results[0]; | |
198 | + result = result.replace('SN Operator:', ''); | |
199 | + result = result.replace('SN Kami', ''); | |
200 | + result = result.trim(); | |
201 | + | |
202 | + if (result == '00') { | |
203 | + result = ''; | |
204 | + } | |
205 | + | |
206 | + return result; | |
207 | +} | |
208 | + | |
209 | +function createServer() { | |
210 | + | |
211 | + var httpServer = http.createServer(function(req, res) { | |
212 | + var parsed_url = url.parse(req.url, true, true); | |
213 | + | |
214 | + console.log('Got request from partner ("' + req.url + '")'); | |
215 | + | |
216 | + var body = ""; | |
217 | + req.on('data', function (chunk) { | |
218 | + body += chunk; | |
219 | + }); | |
220 | + | |
221 | + req.on('end', function () { | |
222 | + res.writeHead(200); | |
223 | + res.end('OK'); | |
224 | + | |
225 | + //console.log(body); | |
226 | + | |
227 | + if (parsed_url.pathname == '/sn') { | |
228 | + console.log('Reverse report -- SN'); | |
229 | + topupResponseHandler(body); | |
230 | + | |
231 | + } else if (parsed_url.pathname = '/refund') { | |
232 | + console.log('Reverse report -- REFUND'); | |
233 | + callbackReport(parsed_url.query.ref_trxid, '40', parsed_url.query.message); | |
234 | + | |
235 | + } else { | |
236 | + console.log('Reverse report -- UNKNOWN'); | |
237 | + console.log('Ignore unknown request on reverse url'); | |
238 | + } | |
239 | + }); | |
240 | + }); | |
241 | + | |
242 | + httpServer.listen(config.h2h_out.listen_port, function() { | |
243 | + console.log('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port); | |
244 | + }); | |
245 | +} | |
246 | + | |
247 | +function balanceCheck() { | |
248 | + var timestamp = strftime('%H%M%S'); | |
249 | + | |
250 | + var payload = { | |
251 | + datacell: [ | |
252 | + {perintah: 'saldo'}, | |
253 | + {userid: config.h2h_out.userid}, | |
254 | + {time: timestamp}, | |
255 | + {sgn: calculateBalanceSignature(config.h2h_out.userid, config.h2h_out.password, timestamp)} | |
256 | + ] | |
257 | + }; | |
258 | + | |
259 | + var postRequest = { | |
260 | + host: "202.152.62.2", | |
261 | + path: "/RELOAD97.php", | |
262 | + port: 7713, | |
263 | + method: "POST", | |
264 | + headers: { | |
265 | + 'Content-Type': 'text/xml', | |
266 | + 'Content-Length': Buffer.byteLength(payload_xml) | |
267 | + } | |
268 | + }; | |
269 | + | |
270 | + var buffer = ""; | |
271 | + var req = http.request( postRequest, function( res ) { | |
272 | + | |
273 | + console.log('Status code: ' + res.statusCode ); | |
274 | + var buffer = ""; | |
275 | + res.on( "data", function( data ) { buffer = buffer + data; } ); | |
276 | + res.on( "end", function( data ) { | |
277 | + console.log('CHECK BALANCE RESULT:'); | |
278 | + console.log(buffer); | |
279 | + }); | |
280 | + | |
281 | + }); | |
282 | + | |
283 | + req.on('error', function(e) { | |
284 | + console.log('problem with request: ' + e.message); | |
285 | + }); | |
286 | + | |
287 | + req.write( payload_xml ); | |
288 | + req.end(); | |
289 | + | |
290 | +} | |
291 | + | |
292 | +function balanceFromMessage(message) { | |
293 | + var matches = message.match(/Saldo: Rp (\d+)/); | |
294 | + | |
295 | + if (!matches) { | |
296 | + return null; | |
297 | + } | |
298 | + if (matches.length < 2) { | |
299 | + return null; | |
300 | + } | |
301 | + | |
302 | + return matches[1]; | |
303 | +} | |
304 | + | |
305 | +function priceFromMessage(message) { | |
306 | + var matches = message.match(/Harga: (\d+)/); | |
307 | + | |
308 | + if (!matches) { | |
309 | + return null; | |
310 | + } | |
311 | + if (matches.length < 2) { | |
312 | + return null; | |
313 | + } | |
314 | + | |
315 | + return matches[1]; | |
316 | +} | |
317 | + | |
318 | +function start(_config, _callbackReport) { | |
319 | + config = _config; | |
320 | + callbackReport = _callbackReport | |
321 | + | |
322 | + createServer(); | |
323 | +} | |
324 | + | |
325 | +exports.start = start; | |
326 | +exports.topupRequest = topupRequest; | |
327 | +exports.balanceFromMessage = balanceFromMessage; | |
328 | +exports.priceFromMessage = priceFromMessage; |
test.js
... | ... | @@ -0,0 +1,49 @@ |
1 | +var assert = require("assert"); | |
2 | + | |
3 | +describe('partner-datacell', function() { | |
4 | + var partner = require('./partner-datacell'); | |
5 | + | |
6 | + describe('#balanceFromMessage', function() { | |
7 | + var message; | |
8 | + | |
9 | + it('should return 8306874', function() { | |
10 | + assert.equal(8306874, partner.balanceFromMessage('IR25 No: 085697273881 sudah diterima dan sdg diproses. SN Kami :243588112. Harga: 24750. Saldo: Rp 8306874.')); | |
11 | + }); | |
12 | + | |
13 | + it('should return 8351574', function() { | |
14 | + assert.equal(8351574, partner.balanceFromMessage('TEL20 No: 085372113774 sudah diterima dan sdg diproses. SN Kami :243586975. Harga: 19950. Saldo: Rp 8351574.')); | |
15 | + }); | |
16 | + | |
17 | + it('should return 0', function() { | |
18 | + assert.equal(0, partner.balanceFromMessage('TEL20 No: 085372113774 sudah diterima dan sdg diproses. SN Kami :243586975. Harga: 19950. Saldo: Rp 0.')); | |
19 | + }); | |
20 | + | |
21 | + it('should return null', function() { | |
22 | + assert.equal(null, partner.balanceFromMessage('XL25 No: 08174945541 SUKSES SN Operator: 970729963933 SN Kami: 243591297.')); | |
23 | + }); | |
24 | + }); | |
25 | + | |
26 | + describe('#priceFromMessage', function() { | |
27 | + var message; | |
28 | + | |
29 | + it('should return 24750', function() { | |
30 | + assert.equal(24750, partner.priceFromMessage('IR25 No: 085697273881 sudah diterima dan sdg diproses. SN Kami :243588112. Harga: 24750. Saldo: Rp 8306874.')); | |
31 | + }); | |
32 | + | |
33 | + it('should return 19950', function() { | |
34 | + assert.equal(19950, partner.priceFromMessage('TEL20 No: 085372113774 sudah diterima dan sdg diproses. SN Kami :243586975. Harga: 19950. Saldo: Rp 8351574.')); | |
35 | + }); | |
36 | + | |
37 | + it('should return 0', function() { | |
38 | + assert.equal(0, partner.priceFromMessage('TEL20 No: 085372113774 sudah diterima dan sdg diproses. SN Kami :243586975. Harga: 0. Saldo: Rp 8351574.')); | |
39 | + }); | |
40 | + | |
41 | + it('should return null', function() { | |
42 | + assert.equal(null, partner.priceFromMessage('XL25 No: 08174945541 SUKSES SN Operator: 970729963933 SN Kami: 243591297.')); | |
43 | + }); | |
44 | + }); | |
45 | + | |
46 | + | |
47 | + | |
48 | + | |
49 | +}); |