Commit ea5da433c9f268de6752a3620496464dced1f55f

Authored by Adhidarma Hadiwinoto
1 parent 200f359d8a
Exists in master

moment

Showing 2 changed files with 3 additions and 1 deletions Inline Diff

1 { 1 {
2 "name": "sate24-to-cjk", 2 "name": "sate24-to-cjk",
3 "version": "1.0.0", 3 "version": "1.0.0",
4 "description": "ST24 to CJK", 4 "description": "ST24 to CJK",
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-cjk.git" 11 "url": "git@gitlab.kodesumber.com:reload97/sate24-to-cjk.git"
12 }, 12 },
13 "keywords": [ 13 "keywords": [
14 "st24", 14 "st24",
15 "ciwaru", 15 "ciwaru",
16 "cjk", 16 "cjk",
17 "ppob" 17 "ppob"
18 ], 18 ],
19 "author": "Adhidarma Hadiwinoto <adhisimon@gmail.com>", 19 "author": "Adhidarma Hadiwinoto <adhisimon@gmail.com>",
20 "license": "ISC", 20 "license": "ISC",
21 "dependencies": { 21 "dependencies": {
22 "crypto": "0.0.3", 22 "crypto": "0.0.3",
23 "ini": "^1.3.4", 23 "ini": "^1.3.4",
24 "moment": "^2.13.0",
24 "mongodb": "^2.1.18", 25 "mongodb": "^2.1.18",
25 "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git", 26 "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git",
26 "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git", 27 "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git",
27 "strftime": "^0.9.2", 28 "strftime": "^0.9.2",
28 "winston": "^2.2.0", 29 "winston": "^2.2.0",
29 "xml": "^1.0.1", 30 "xml": "^1.0.1",
30 "xml2js": "^0.4.16" 31 "xml2js": "^0.4.16"
31 }, 32 },
32 "devDependencies": { 33 "devDependencies": {
33 "should": "^8.3.1" 34 "should": "^8.3.1"
34 } 35 }
35 } 36 }
36 37
1 var winston = require('winston'); 1 var winston = require('winston');
2 var crypto = require('crypto'); 2 var crypto = require('crypto');
3 var xml = require('xml'); 3 var xml = require('xml');
4 var url = require('url'); 4 var url = require('url');
5 var http = require('http'); 5 var http = require('http');
6 var xml2js = require('xml2js').parseString; 6 var xml2js = require('xml2js').parseString;
7 var mongoClient = require('mongodb').MongoClient; 7 var mongoClient = require('mongodb').MongoClient;
8 var strftime = require('strftime'); 8 var strftime = require('strftime');
9 var moment = require('moment');
9 10
10 var config; 11 var config;
11 var callbackReport; 12 var callbackReport;
12 var aaa; 13 var aaa;
13 var logger; 14 var logger;
14 var options; 15 var options;
15 var mongodb; 16 var mongodb;
16 17
17 function initMongoClient() { 18 function initMongoClient() {
18 if (!config.mongodb || !config.mongodb.url) { 19 if (!config.mongodb || !config.mongodb.url) {
19 return; 20 return;
20 } 21 }
21 22
22 try { 23 try {
23 var url = config.mongodb.url; 24 var url = config.mongodb.url;
24 25
25 mongoClient.connect(url, function(err, db) { 26 mongoClient.connect(url, function(err, db) {
26 if (err) { 27 if (err) {
27 logger.warn('Failed to connect to mongodb', {err: err}); 28 logger.warn('Failed to connect to mongodb', {err: err});
28 return; 29 return;
29 } 30 }
30 mongodb = db; 31 mongodb = db;
31 logger.info('MongoDB connected'); 32 logger.info('MongoDB connected');
32 }); 33 });
33 } 34 }
34 catch(err) { 35 catch(err) {
35 logger.warn('Exception when connecting to mongodb', {err: err, url: url}); 36 logger.warn('Exception when connecting to mongodb', {err: err, url: url});
36 } 37 }
37 } 38 }
38 39
39 var maxRetry = 10; 40 var maxRetry = 10;
40 var sleepBeforeRetry = 30; 41 var sleepBeforeRetry = 30;
41 42
42 function start(_config, _callbackReport, options) { 43 function start(_config, _callbackReport, options) {
43 config = _config; 44 config = _config;
44 callbackReport = _callbackReport 45 callbackReport = _callbackReport
45 46
46 if (options && options.aaa) { 47 if (options && options.aaa) {
47 aaa = options.aaa; 48 aaa = options.aaa;
48 } 49 }
49 50
50 if (options && options.logger) { 51 if (options && options.logger) {
51 logger = options.logger; 52 logger = options.logger;
52 } else { 53 } else {
53 logger = new winston.Logger({ 54 logger = new winston.Logger({
54 transports: [ 55 transports: [
55 new (winston.transports.Console)() 56 new (winston.transports.Console)()
56 ] 57 ]
57 }); 58 });
58 } 59 }
59 60
60 initMongoClient(); 61 initMongoClient();
61 } 62 }
62 63
63 function calculateSignature(params) { 64 function calculateSignature(params) {
64 var passwordHash = crypto.createHash('sha1').update(params.password).digest().toString('hex'); 65 var passwordHash = crypto.createHash('sha1').update(params.password).digest().toString('hex');
65 var plain = params.trxtype + params.prdcode + params.value + params.msisdn + params.trxid + params.uid + passwordHash; 66 var plain = params.trxtype + params.prdcode + params.value + params.msisdn + params.trxid + params.uid + passwordHash;
66 var signature = crypto.createHash('sha256').update(plain).digest().toString('hex'); 67 var signature = crypto.createHash('sha256').update(plain).digest().toString('hex');
67 68
68 try { 69 try {
69 logger.verbose('Signature calculated', {plain: plain, signature: signature}); 70 logger.verbose('Signature calculated', {plain: plain, signature: signature});
70 } 71 }
71 catch(err) {} 72 catch(err) {}
72 73
73 return signature; 74 return signature;
74 } 75 }
75 76
76 function createXmlPayload(params) { 77 function createXmlPayload(params) {
77 var payload = "<?xml version=\"1.0\" ?>\n" + xml({ 78 var payload = "<?xml version=\"1.0\" ?>\n" + xml({
78 ciwaru: [ 79 ciwaru: [
79 {trxtype: params.trxtype}, 80 {trxtype: params.trxtype},
80 {prdcode: params.prdcode}, 81 {prdcode: params.prdcode},
81 {value: params.value}, 82 {value: params.value},
82 {msisdn: params.msisdn}, 83 {msisdn: params.msisdn},
83 {trxid: params.trxid}, 84 {trxid: params.trxid},
84 {uid: params.uid}, 85 {uid: params.uid},
85 {hash: calculateSignature(params)} 86 {hash: calculateSignature(params)}
86 ] 87 ]
87 }); 88 });
88 89
89 try { logger.verbose("Payload: " + payload); } 90 try { logger.verbose("Payload: " + payload); }
90 catch(errLog) {} 91 catch(errLog) {}
91 92
92 return payload; 93 return payload;
93 } 94 }
94 95
95 function insertTaskToMongoDb(task) { 96 function insertTaskToMongoDb(task) {
96 if (!isMongoReady()) { return; } 97 if (!isMongoReady()) { return; }
97 98
98 try { 99 try {
99 mongodb.collection(config.mongodb.collection).insertOne(task); 100 mongodb.collection(config.mongodb.collection).insertOne(task);
100 } 101 }
101 catch(err) { 102 catch(err) {
102 //logger.warn('Exception when inserting document to mongodb', {err: err, task: task}); 103 //logger.warn('Exception when inserting document to mongodb', {err: err, task: task});
103 } 104 }
104 } 105 }
105 106
106 function pushResponseToMongoDb(task, response) { 107 function pushResponseToMongoDb(task, response) {
107 if (!isMongoReady()) { return; } 108 if (!isMongoReady()) { return; }
108 109
109 try { 110 try {
110 mongodb.collection(config.mongodb.collection).updateOne( 111 mongodb.collection(config.mongodb.collection).updateOne(
111 {requestId: task.requestId}, 112 {requestId: task.requestId},
112 {$push: {responses: response}}, 113 {$push: {responses: response}},
113 function(err, result) { 114 function(err, result) {
114 if (err) { 115 if (err) {
115 logger.warn('Error when pushing response to mongodb', {err: err, task: task, response: response}); 116 logger.warn('Error when pushing response to mongodb', {err: err, task: task, response: response});
116 return; 117 return;
117 } 118 }
118 } 119 }
119 ); 120 );
120 } 121 }
121 catch(err) { 122 catch(err) {
122 logger.warn('Exception when pushing response to mongodb', {err: err, task: task, response: response}); 123 logger.warn('Exception when pushing response to mongodb', {err: err, task: task, response: response});
123 } 124 }
124 } 125 }
125 126
126 function isMongoReady() { 127 function isMongoReady() {
127 if (!config.mongodb) { return; } 128 if (!config.mongodb) { return; }
128 if (!config.mongodb.collection) { return; } 129 if (!config.mongodb.collection) { return; }
129 if (!mongodb) { return; } 130 if (!mongodb) { return; }
130 131
131 return true; 132 return true;
132 } 133 }
133 134
134 function getSNFromMessage(message) { 135 function getSNFromMessage(message) {
135 try { 136 try {
136 var sn_match = message.match(/SN: (\w+)/); 137 var sn_match = message.match(/SN: (\w+)/);
137 logger.verbose('Got SN: ' + sn_match[1]); 138 logger.verbose('Got SN: ' + sn_match[1]);
138 return sn_match[1].trim(); 139 return sn_match[1].trim();
139 } 140 }
140 catch(err) { 141 catch(err) {
141 logger.verbose('Exception on getting sn from message', {err: err}); 142 logger.verbose('Exception on getting sn from message', {err: err});
142 return ''; 143 return '';
143 } 144 }
144 } 145 }
145 146
146 function hasSuccessKeywords(message) { 147 function hasSuccessKeywords(message) {
147 var keywords = ['SUKSES', 'Finish']; 148 var keywords = ['SUKSES', 'Finish'];
148 149
149 var count = keywords.length; 150 var count = keywords.length;
150 for (var i=0; i < count; i++) { 151 for (var i=0; i < count; i++) {
151 if (message.indexOf(keywords[i]) >= 0) { 152 if (message.indexOf(keywords[i]) >= 0) {
152 return true; 153 return true;
153 } 154 }
154 } 155 }
155 return false; 156 return false;
156 } 157 }
157 158
158 function supplierRcToST24Rc(rc) { 159 function supplierRcToST24Rc(rc) {
159 var rcs = { 160 var rcs = {
160 '0001': '40', 161 '0001': '40',
161 '0019': '13', // produk tidak tersedia 162 '0019': '13', // produk tidak tersedia
162 } 163 }
163 164
164 if (rcs[rc]) { 165 if (rcs[rc]) {
165 return rcs[rc]; 166 return rcs[rc];
166 } else { 167 } else {
167 return; 168 return;
168 } 169 }
169 } 170 }
170 171
171 function getSNFromResponseObject(respObj) { 172 function getSNFromResponseObject(respObj) {
172 try { 173 try {
173 return respObj.ciwaru.sn[0].trim(); 174 return respObj.ciwaru.sn[0].trim();
174 } 175 }
175 catch(err) { 176 catch(err) {
176 return; 177 return;
177 } 178 }
178 } 179 }
179 180
180 function topupResponseHandler(body, task) { 181 function topupResponseHandler(body, task) {
181 182
182 //logger.info('Got reply from partner', {body: body}); 183 //logger.info('Got reply from partner', {body: body});
183 184
184 xml2js(body, function(err, result) { 185 xml2js(body, function(err, result) {
185 var ts = strftime('%Y-%m-%d %H:%M:%S', new Date()); 186 var ts = strftime('%Y-%m-%d %H:%M:%S', new Date());
186 187
187 if (err) { 188 if (err) {
188 logger.warn('Got invalid XML from partner', {err: err, body: body, task: task}); 189 logger.warn('Got invalid XML from partner', {err: err, body: body, task: task});
189 callbackReport(task.requestId, '68', body); 190 callbackReport(task.requestId, '68', body);
190 191
191 pushResponseToMongoDb(task, {ts: ts, raw: body}); 192 pushResponseToMongoDb(task, {ts: ts, raw: body});
192 return; 193 return;
193 } 194 }
194 logger.info('XML message from partner', {result: result}); 195 logger.info('XML message from partner', {result: result});
195 pushResponseToMongoDb(task, {ts: ts, raw: body, parsed: result}); 196 pushResponseToMongoDb(task, {ts: ts, raw: body, parsed: result});
196 197
197 var rc = '68'; 198 var rc = '68';
198 var message = result.ciwaru.msg[0]; 199 var message = result.ciwaru.msg[0];
199 200
200 /* 201 /*
201 var trxid = 0; 202 var trxid = 0;
202 try { 203 try {
203 trxid = result.ciwaru.reqnum[0]; 204 trxid = result.ciwaru.reqnum[0];
204 } 205 }
205 catch(err) { 206 catch(err) {
206 trxid = result.ciwaru.trxid[0]; 207 trxid = result.ciwaru.trxid[0];
207 } 208 }
208 */ 209 */
209 210
210 if (message.toUpperCase().indexOf('PENDING') >= 0) { 211 if (message.toUpperCase().indexOf('PENDING') >= 0) {
211 rc = '68'; 212 rc = '68';
212 } 213 }
213 else if (hasSuccessKeywords(message)) { 214 else if (hasSuccessKeywords(message)) {
214 var sn = getSNFromResponseObject(result); 215 var sn = getSNFromResponseObject(result);
215 if (!sn) { 216 if (!sn) {
216 sn = getSNFromMessage(message); 217 sn = getSNFromMessage(message);
217 } 218 }
218 219
219 message = 'SN=' + sn + '; ' + message; 220 message = 'SN=' + sn + '; ' + message;
220 rc = '00'; 221 rc = '00';
221 222
222 } else { 223 } else {
223 rc = supplierRcToST24Rc(result.ciwaru.rc[0]); 224 rc = supplierRcToST24Rc(result.ciwaru.rc[0]);
224 if (!rc) { 225 if (!rc) {
225 rc = '68'; 226 rc = '68';
226 } 227 }
227 } 228 }
228 229
229 if ((task.retry == maxRetry) || (rc != '68')) { 230 if ((task.retry == maxRetry) || (rc != '68')) {
230 callbackReport(task.requestId, rc, message); 231 callbackReport(task.requestId, rc, message);
231 } else { 232 } else {
232 logger.info('Not reporting to AAA for duplicate 68', {task: task}); 233 logger.info('Not reporting to AAA for duplicate 68', {task: task});
233 } 234 }
234 235
235 if (rc == '68') { 236 if (rc == '68') {
236 topupRequestRetry(task); 237 topupRequestRetry(task);
237 } 238 }
238 }); 239 });
239 } 240 }
240 241
241 function topupRequestRetry(task) { 242 function topupRequestRetry(task) {
242 task.retry--; 243 task.retry--;
243 244
244 if (task.retry > 0) { 245 if (task.retry > 0) {
245 logger.info('Retrying in ' + sleepBeforeRetry + 's'); 246 logger.info('Retrying in ' + sleepBeforeRetry + 's');
246 setTimeout(topupRequest, sleepBeforeRetry * 1000, task, task.retry); 247 setTimeout(topupRequest, sleepBeforeRetry * 1000, task, task.retry);
247 } 248 }
248 else { 249 else {
249 logger.warn('Maximum retry for pending status exceeded', {task: task}); 250 logger.warn('Maximum retry for pending status exceeded', {task: task});
250 } 251 }
251 } 252 }
252 253
253 function topupRequest(task, retry) { 254 function topupRequest(task, retry) {
254 255
255 if (retry === undefined) { 256 if (retry === undefined) {
256 insertTaskToMongoDb(task); 257 insertTaskToMongoDb(task);
257 retry = maxRetry; 258 retry = maxRetry;
258 } 259 }
259 260
260 if (!task.retry) { 261 if (!task.retry) {
261 task.retry = retry; 262 task.retry = retry;
262 } 263 }
263 264
264 if (!task.ts) { 265 if (!task.ts) {
265 task.ts = strftime('%Y-%m-%d %H:%M:%S', new Date()); 266 task.ts = moment(task.timestamp, 'YYYYMMDDHHmmss').format('YYYY-MM-DD HH:mm:ss')
266 } 267 }
267 268
268 var remoteProduct = task.remoteProduct.split(','); 269 var remoteProduct = task.remoteProduct.split(',');
269 270
270 var params = { 271 var params = {
271 trxtype: '01', 272 trxtype: '01',
272 prdcode: remoteProduct[0], 273 prdcode: remoteProduct[0],
273 value: remoteProduct[1], 274 value: remoteProduct[1],
274 msisdn: task.destination, 275 msisdn: task.destination,
275 trxid: task.requestId, 276 trxid: task.requestId,
276 uid: config.h2h_out.userid, 277 uid: config.h2h_out.userid,
277 password: config.h2h_out.password, 278 password: config.h2h_out.password,
278 }; 279 };
279 280
280 var postBody = createXmlPayload(params); 281 var postBody = createXmlPayload(params);
281 282
282 var partnerUrl = url.parse(config.h2h_out.partner); 283 var partnerUrl = url.parse(config.h2h_out.partner);
283 var postRequest = { 284 var postRequest = {
284 host: partnerUrl.hostname, 285 host: partnerUrl.hostname,
285 path: partnerUrl.path, 286 path: partnerUrl.path,
286 port: partnerUrl.port, 287 port: partnerUrl.port,
287 method: "POST", 288 method: "POST",
288 headers: { 289 headers: {
289 'Content-Type': 'text/xml', 290 'Content-Type': 'text/xml',
290 'Content-Length': Buffer.byteLength(postBody) 291 'Content-Length': Buffer.byteLength(postBody)
291 } 292 }
292 }; 293 };
293 294
294 logger.info('POST to partner', {postRequest: postRequest}); 295 logger.info('POST to partner', {postRequest: postRequest});
295 var req = http.request(postRequest, function( res ) { 296 var req = http.request(postRequest, function( res ) {
296 297
297 logger.info('Status code: ' + res.statusCode ); 298 logger.info('Status code: ' + res.statusCode );
298 var buffer = ""; 299 var buffer = "";
299 res.on( "data", function( data ) { buffer = buffer + data; } ); 300 res.on( "data", function( data ) { buffer = buffer + data; } );
300 res.on( "end", function( data ) { 301 res.on( "end", function( data ) {
301 topupResponseHandler(buffer, task); 302 topupResponseHandler(buffer, task);
302 }); 303 });
303 }); 304 });
304 305
305 req.on('error', function(e) { 306 req.on('error', function(e) {
306 logger.warn('problem with request: ' + e.message); 307 logger.warn('problem with request: ' + e.message);
307 callbackReport(task['requestId'], '68', e.message); 308 callbackReport(task['requestId'], '68', e.message);
308 309
309 topupRequestRetry(task); 310 topupRequestRetry(task);
310 }); 311 });
311 312
312 req.write(postBody); 313 req.write(postBody);
313 req.end(); 314 req.end();
314 } 315 }
315 316
316 exports.start = start; 317 exports.start = start;
317 exports.topupRequest = topupRequest; 318 exports.topupRequest = topupRequest;
318 exports.calculateSignature = calculateSignature; 319 exports.calculateSignature = calculateSignature;
319 exports.createXmlPayload = createXmlPayload; 320 exports.createXmlPayload = createXmlPayload;
320 exports.getSNFromMessage = getSNFromMessage; 321 exports.getSNFromMessage = getSNFromMessage;
321 322