Commit 1a8b8868c56cbaf43012261a55b456e0376b1e09

Authored by Adhidarma Hadiwinoto
1 parent f71680fb1a
Exists in master

saldo tidak cukup

Showing 1 changed file with 50 additions and 49 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 task.supplier = config.globals.gateway_name; 99 task.supplier = config.globals.gateway_name;
100 100
101 try { 101 try {
102 mongodb.collection(config.mongodb.collection).insertOne(task); 102 mongodb.collection(config.mongodb.collection).insertOne(task);
103 } 103 }
104 catch(err) { 104 catch(err) {
105 //logger.warn('Exception when inserting document to mongodb', {err: err, task: task}); 105 //logger.warn('Exception when inserting document to mongodb', {err: err, task: task});
106 } 106 }
107 } 107 }
108 108
109 function pushResponseToMongoDb(task, response) { 109 function pushResponseToMongoDb(task, response) {
110 if (!isMongoReady()) { return; } 110 if (!isMongoReady()) { return; }
111 111
112 try { 112 try {
113 mongodb.collection(config.mongodb.collection).updateOne( 113 mongodb.collection(config.mongodb.collection).updateOne(
114 {requestId: task.requestId}, 114 {requestId: task.requestId},
115 { 115 {
116 $set: { 116 $set: {
117 lastResponse: response, 117 lastResponse: response,
118 supplier: config.globals.gateway_name 118 supplier: config.globals.gateway_name
119 }, 119 },
120 $push: { 120 $push: {
121 responses: response 121 responses: response
122 } 122 }
123 }, 123 },
124 function(err, result) { 124 function(err, result) {
125 if (err) { 125 if (err) {
126 logger.warn('Error when pushing response to mongodb', {err: err, task: task, response: response}); 126 logger.warn('Error when pushing response to mongodb', {err: err, task: task, response: response});
127 return; 127 return;
128 } 128 }
129 } 129 }
130 ); 130 );
131 } 131 }
132 catch(err) { 132 catch(err) {
133 logger.warn('Exception when pushing response to mongodb', {err: err, task: task, response: response}); 133 logger.warn('Exception when pushing response to mongodb', {err: err, task: task, response: response});
134 } 134 }
135 } 135 }
136 136
137 function isMongoReady() { 137 function isMongoReady() {
138 if (!config.mongodb) { return; } 138 if (!config.mongodb) { return; }
139 if (!config.mongodb.collection) { return; } 139 if (!config.mongodb.collection) { return; }
140 if (!mongodb) { return; } 140 if (!mongodb) { return; }
141 141
142 return true; 142 return true;
143 } 143 }
144 144
145 function getSNFromMessage(message) { 145 function getSNFromMessage(message) {
146 try { 146 try {
147 var sn_match = message.match(/SN: (\w+)/); 147 var sn_match = message.match(/SN: (\w+)/);
148 return sn_match[1].trim(); 148 return sn_match[1].trim();
149 } 149 }
150 catch(err) { 150 catch(err) {
151 if (logger) { 151 if (logger) {
152 logger.verbose('Exception on getting sn from message', {err: err}); 152 logger.verbose('Exception on getting sn from message', {err: err});
153 } 153 }
154 return ''; 154 return '';
155 } 155 }
156 } 156 }
157 157
158 function hasSuccessKeywords(message) { 158 function hasSuccessKeywords(message) {
159 var keywords = ['SUKSES', 'Finish']; 159 var keywords = ['SUKSES', 'Finish'];
160 160
161 var count = keywords.length; 161 var count = keywords.length;
162 for (var i=0; i < count; i++) { 162 for (var i=0; i < count; i++) {
163 if (message.indexOf(keywords[i]) >= 0) { 163 if (message.indexOf(keywords[i]) >= 0) {
164 return true; 164 return true;
165 } 165 }
166 } 166 }
167 return false; 167 return false;
168 } 168 }
169 169
170 function supplierRcToST24Rc(rc) { 170 function supplierRcToST24Rc(rc) {
171 var rcs = { 171 var rcs = {
172 '0001': '40', 172 '0001': '40',
173 '0012': '40', // hash data tidak sesuai 173 '0012': '40', // hash data tidak sesuai
174 '0015': '40', // saldo tidak cukup
174 '0019': '13', // produk tidak tersedia 175 '0019': '13', // produk tidak tersedia
175 } 176 }
176 177
177 if (rcs[rc]) { 178 if (rcs[rc]) {
178 return rcs[rc]; 179 return rcs[rc];
179 } else { 180 } else {
180 return; 181 return;
181 } 182 }
182 } 183 }
183 184
184 function getSNFromResponseObject(respObj) { 185 function getSNFromResponseObject(respObj) {
185 try { 186 try {
186 return respObj.ciwaru.sn[0].trim(); 187 return respObj.ciwaru.sn[0].trim();
187 } 188 }
188 catch(err) { 189 catch(err) {
189 return; 190 return;
190 } 191 }
191 } 192 }
192 193
193 function topupResponseHandler(body, task) { 194 function topupResponseHandler(body, task) {
194 195
195 //logger.info('Got reply from partner', {body: body}); 196 //logger.info('Got reply from partner', {body: body});
196 197
197 xml2js(body, function(err, result) { 198 xml2js(body, function(err, result) {
198 var ts = strftime('%Y-%m-%d %H:%M:%S', new Date()); 199 var ts = strftime('%Y-%m-%d %H:%M:%S', new Date());
199 200
200 if (err) { 201 if (err) {
201 logger.warn('Got invalid XML from partner', {err: err, body: body, task: task}); 202 logger.warn('Got invalid XML from partner', {err: err, body: body, task: task});
202 callbackReport(task.requestId, '68', body); 203 callbackReport(task.requestId, '68', body);
203 204
204 pushResponseToMongoDb(task, {ts: ts, supplier: config.globals.gateway_name, raw: body}); 205 pushResponseToMongoDb(task, {ts: ts, supplier: config.globals.gateway_name, raw: body});
205 return; 206 return;
206 } 207 }
207 logger.info('XML message from partner', {result: result}); 208 logger.info('XML message from partner', {result: result});
208 pushResponseToMongoDb(task, {ts: ts, supplier: config.globals.gateway_name, raw: body, parsed: result}); 209 pushResponseToMongoDb(task, {ts: ts, supplier: config.globals.gateway_name, raw: body, parsed: result});
209 210
210 var rc = '68'; 211 var rc = '68';
211 var message = result.ciwaru.msg[0]; 212 var message = result.ciwaru.msg[0];
212 213
213 if (message.toUpperCase().indexOf('PENDING') >= 0) { 214 if (message.toUpperCase().indexOf('PENDING') >= 0) {
214 rc = '68'; 215 rc = '68';
215 } 216 }
216 else if (hasSuccessKeywords(message)) { 217 else if (hasSuccessKeywords(message)) {
217 218
218 var sn = getSNFromResponseObject(result); 219 var sn = getSNFromResponseObject(result);
219 220
220 if (!sn) { 221 if (!sn) {
221 sn = getSNFromMessage(message); 222 sn = getSNFromMessage(message);
222 } 223 }
223 224
224 message = 'SN=' + sn + '; ' + message; 225 message = 'SN=' + sn + '; ' + message;
225 rc = '00'; 226 rc = '00';
226 227
227 } 228 }
228 else if (message.indexOf('Nomor Pelanggan Salah') >= 0) { 229 else if (message.indexOf('Nomor Pelanggan Salah') >= 0) {
229 230
230 rc = '14'; 231 rc = '14';
231 232
232 } else { 233 } else {
233 rc = supplierRcToST24Rc(result.ciwaru.rc[0]); 234 rc = supplierRcToST24Rc(result.ciwaru.rc[0]);
234 if (!rc) { 235 if (!rc) {
235 rc = '68'; 236 rc = '68';
236 } 237 }
237 } 238 }
238 239
239 if ((task.retry == maxRetry) || (rc != '68')) { 240 if ((task.retry == maxRetry) || (rc != '68')) {
240 callbackReport(task.requestId, rc, message); 241 callbackReport(task.requestId, rc, message);
241 } else { 242 } else {
242 logger.info('Not reporting to AAA for duplicate 68', {task: task}); 243 logger.info('Not reporting to AAA for duplicate 68', {task: task});
243 } 244 }
244 245
245 if (rc == '68') { 246 if (rc == '68') {
246 topupRequestRetry(task); 247 topupRequestRetry(task);
247 } 248 }
248 }); 249 });
249 } 250 }
250 251
251 function topupRequestRetry(task) { 252 function topupRequestRetry(task) {
252 task.retry--; 253 task.retry--;
253 254
254 if (task.retry > 0) { 255 if (task.retry > 0) {
255 logger.info('Retrying in ' + sleepBeforeRetry + 's'); 256 logger.info('Retrying in ' + sleepBeforeRetry + 's');
256 setTimeout(topupRequest, sleepBeforeRetry * 1000, task, task.retry); 257 setTimeout(topupRequest, sleepBeforeRetry * 1000, task, task.retry);
257 } 258 }
258 else { 259 else {
259 logger.warn('Maximum retry for pending status exceeded', {task: task}); 260 logger.warn('Maximum retry for pending status exceeded', {task: task});
260 } 261 }
261 } 262 }
262 263
263 function topupRequest(task, retry) { 264 function topupRequest(task, retry) {
264 265
265 if (retry === undefined) { 266 if (retry === undefined) {
266 267
267 task.ts = moment(task.timestamp, 'YYYYMMDDHHmmss').format('YYYY-MM-DD HH:mm:ss'); 268 task.ts = moment(task.timestamp, 'YYYYMMDDHHmmss').format('YYYY-MM-DD HH:mm:ss');
268 task.ts_date = moment(task.timestamp, 'YYYYMMDDHHmmss').format('YYYY-MM-DD'); 269 task.ts_date = moment(task.timestamp, 'YYYYMMDDHHmmss').format('YYYY-MM-DD');
269 270
270 insertTaskToMongoDb(task); 271 insertTaskToMongoDb(task);
271 272
272 retry = maxRetry; 273 retry = maxRetry;
273 } 274 }
274 275
275 if (!task.retry) { 276 if (!task.retry) {
276 task.retry = retry; 277 task.retry = retry;
277 } 278 }
278 279
279 var remoteProduct = task.remoteProduct.split(','); 280 var remoteProduct = task.remoteProduct.split(',');
280 281
281 var params = { 282 var params = {
282 trxtype: '01', 283 trxtype: '01',
283 prdcode: remoteProduct[0], 284 prdcode: remoteProduct[0],
284 value: remoteProduct[1], 285 value: remoteProduct[1],
285 msisdn: task.destination, 286 msisdn: task.destination,
286 trxid: task.requestId, 287 trxid: task.requestId,
287 uid: config.h2h_out.userid, 288 uid: config.h2h_out.userid,
288 password: config.h2h_out.password, 289 password: config.h2h_out.password,
289 }; 290 };
290 291
291 var postBody = createXmlPayload(params); 292 var postBody = createXmlPayload(params);
292 293
293 var partnerUrl = url.parse(config.h2h_out.partner); 294 var partnerUrl = url.parse(config.h2h_out.partner);
294 var postRequest = { 295 var postRequest = {
295 host: partnerUrl.hostname, 296 host: partnerUrl.hostname,
296 path: partnerUrl.path, 297 path: partnerUrl.path,
297 port: partnerUrl.port, 298 port: partnerUrl.port,
298 method: "POST", 299 method: "POST",
299 headers: { 300 headers: {
300 'Content-Type': 'text/xml', 301 'Content-Type': 'text/xml',
301 'Content-Length': Buffer.byteLength(postBody) 302 'Content-Length': Buffer.byteLength(postBody)
302 } 303 }
303 }; 304 };
304 305
305 logger.info('POST to partner', {postRequest: postRequest}); 306 logger.info('POST to partner', {postRequest: postRequest});
306 var req = http.request(postRequest, function( res ) { 307 var req = http.request(postRequest, function( res ) {
307 308
308 logger.verbose('Status code: ' + res.statusCode ); 309 logger.verbose('Status code: ' + res.statusCode );
309 var buffer = ""; 310 var buffer = "";
310 311
311 res.on( "data", function( data ) { 312 res.on( "data", function( data ) {
312 buffer = buffer + data; 313 buffer = buffer + data;
313 }); 314 });
314 315
315 res.on( "end", function( data ) { 316 res.on( "end", function( data ) {
316 topupResponseHandler(buffer, task); 317 topupResponseHandler(buffer, task);
317 }); 318 });
318 319
319 }); 320 });
320 321
321 req.on('error', function(e) { 322 req.on('error', function(e) {
322 logger.warn('problem with request: ' + e.message); 323 logger.warn('problem with request: ' + e.message);
323 callbackReport(task['requestId'], '68', e.message); 324 callbackReport(task['requestId'], '68', e.message);
324 325
325 topupRequestRetry(task); 326 topupRequestRetry(task);
326 }); 327 });
327 328
328 req.write(postBody); 329 req.write(postBody);
329 req.end(); 330 req.end();
330 } 331 }
331 332
332 exports.start = start; 333 exports.start = start;
333 exports.topupRequest = topupRequest; 334 exports.topupRequest = topupRequest;
334 exports.calculateSignature = calculateSignature; 335 exports.calculateSignature = calculateSignature;
335 exports.createXmlPayload = createXmlPayload; 336 exports.createXmlPayload = createXmlPayload;
336 exports.getSNFromMessage = getSNFromMessage; 337 exports.getSNFromMessage = getSNFromMessage;
337 338