Commit 96e4677f6e00cb0bdb4ff4a9d97efb72e6489929

Authored by Adhidarma Hadiwinoto
1 parent 0203b4c7ac
Exists in master

put last rc, messsage refnum to mongo

Showing 2 changed files with 29 additions and 17 deletions Inline Diff

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