Compare View

switch
from
...
to
 
Commits (7)

Changes

Showing 3 changed files Inline Diff

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