Commit c0406ae491d2527bad78c503aabd3eeb13cf715c

Authored by Adhidarma Hadiwinoto
1 parent d356e0e7b2
Exists in master

tambah ;

Showing 1 changed file with 1 additions and 1 deletions Inline Diff

partner-masterpulsa-voucher.js
1 var winston = require('winston'); 1 var winston = require('winston');
2 var request = require('request'); 2 var request = require('request');
3 var strftime = require('strftime'); 3 var strftime = require('strftime');
4 var crypto = require('crypto'); 4 var crypto = require('crypto');
5 var redis = require('redis'); 5 var redis = require('redis');
6 6
7 var config; 7 var config;
8 var callbackReport; 8 var callbackReport;
9 var aaa; 9 var aaa;
10 var logger; 10 var logger;
11 var options; 11 var options;
12 var redisClient; 12 var redisClient;
13 13
14 var adviceDelay = 30000; 14 var adviceDelay = 30000;
15 15
16 function callbackReportWrapper(requestId, rc, message) { 16 function callbackReportWrapper(requestId, rc, message) {
17 try { 17 try {
18 message = message.replace(/:/g, ' ').replace(/#/g, ' '); 18 message = message.replace(/:/g, ' ').replace(/#/g, ' ');
19 message = message.replace(/\s+/g, ' '); 19 message = message.replace(/\s+/g, ' ');
20 } 20 }
21 catch(err) { 21 catch(err) {
22 if (logger) { 22 if (logger) {
23 logger.warn('Exception on callbackReportWrapper: ' + err); 23 logger.warn('Exception on callbackReportWrapper: ' + err);
24 } 24 }
25 } 25 }
26 26
27 if (rc != '00' && rc != '68') { 27 if (rc != '00' && rc != '68') {
28 try { 28 try {
29 var key = dupcheckKey(config.globals.gateway_name, task); 29 var key = dupcheckKey(config.globals.gateway_name, task);
30 redisClient.del(key); 30 redisClient.del(key);
31 } 31 }
32 catch(delErr) { 32 catch(delErr) {
33 } 33 }
34 } 34 }
35 35
36 callbackReport(requestId, rc, message); 36 callbackReport(requestId, rc, message);
37 } 37 }
38 38
39 function createRedisClient() { 39 function createRedisClient() {
40 redisClient = redis.createClient(config.globals.redis_port, config.globals.redis_host); 40 redisClient = redis.createClient(config.globals.redis_port, config.globals.redis_host);
41 } 41 }
42 42
43 function start(options) { 43 function start(options) {
44 if (!options) { 44 if (!options) {
45 console.log('Undefined options, terminating....'); 45 console.log('Undefined options, terminating....');
46 process.exit(1); 46 process.exit(1);
47 } 47 }
48 48
49 if (options.config) { 49 if (options.config) {
50 config = options.config; 50 config = options.config;
51 } else { 51 } else {
52 console.log('Undefined options.config, terminating....') 52 console.log('Undefined options.config, terminating....')
53 process.exit(1); 53 process.exit(1);
54 } 54 }
55 55
56 if (options.aaa) { 56 if (options.aaa) {
57 aaa = options.aaa; 57 aaa = options.aaa;
58 callbackReport = options.aaa.callbackReportWithPushToMongoDb; 58 callbackReport = options.aaa.callbackReportWithPushToMongoDb;
59 } else { 59 } else {
60 console.log('Undefined options.aaa, terminating....') 60 console.log('Undefined options.aaa, terminating....')
61 process.exit(1); 61 process.exit(1);
62 } 62 }
63 63
64 if (options && options.logger) { 64 if (options && options.logger) {
65 logger = options.logger; 65 logger = options.logger;
66 } else { 66 } else {
67 logger = new winston.Logger({ 67 logger = new winston.Logger({
68 transports: [ 68 transports: [
69 new (winston.transports.Console)() 69 new (winston.transports.Console)()
70 ] 70 ]
71 }); 71 });
72 } 72 }
73 73
74 createRedisClient(); 74 createRedisClient();
75 } 75 }
76 76
77 function getRedisKey(task) { 77 function getRedisKey(task) {
78 return config.globals.gateway_name + '.tid:' + task.requestId; 78 return config.globals.gateway_name + '.tid:' + task.requestId;
79 } 79 }
80 80
81 function dupcheckKey(gatewayName, task) { 81 function dupcheckKey(gatewayName, task) {
82 return 'DUPCHECK.gw:' + gatewayName + '.prod:' + task.remoteProduct + '.dest:' + task.destination + '.date:' + strftime('%Y%m%d', new Date); 82 return 'DUPCHECK.gw:' + gatewayName + '.prod:' + task.remoteProduct + '.dest:' + task.destination + '.date:' + strftime('%Y%m%d', new Date);
83 } 83 }
84 84
85 function topupRequest(task, retry) { 85 function topupRequest(task, retry) {
86 aaa.insertTaskToMongoDb(task); 86 aaa.insertTaskToMongoDb(task);
87 87
88 var key = dupcheckKey(config.globals.gateway_name, task); 88 var key = dupcheckKey(config.globals.gateway_name, task);
89 89
90 redisClient.get(key, function(err, data) { 90 redisClient.get(key, function(err, data) {
91 if (err) { 91 if (err) {
92 callbackReportWrapper(task.requestId, '40', 'Gagal cek anti transaksi duplikat (redis error)'); 92 callbackReportWrapper(task.requestId, '40', 'Gagal cek anti transaksi duplikat (redis error)');
93 return; 93 return;
94 } 94 }
95 95
96 if (!data) { 96 if (!data) {
97 logger.verbose('Belum ada trx dengan tujuan dan denom yang sama pada hari ini. Lanjutkan.'); 97 logger.verbose('Belum ada trx dengan tujuan dan denom yang sama pada hari ini. Lanjutkan.');
98 98
99 redisClient.set(key, JSON.stringify(task)); 99 redisClient.set(key, JSON.stringify(task));
100 redisClient.expire(key, 3600 * 24 * 2); 100 redisClient.expire(key, 3600 * 24 * 2);
101 101
102 voucherPay(task, retry); 102 voucherPay(task, retry);
103 103
104 } else { 104 } else {
105 105
106 try { 106 try {
107 var taskOnRedis = JSON.parse(data); 107 var taskOnRedis = JSON.parse(data);
108 if (task.requestId == taskOnRedis.requestId) { 108 if (task.requestId == taskOnRedis.requestId) {
109 logger.verbose('Sudah ada trx dengan tujuan dan denom yg sama, requestId jg sama. Lanjutkan dengan advice.') 109 logger.verbose('Sudah ada trx dengan tujuan dan denom yg sama, requestId jg sama. Lanjutkan dengan advice.')
110 advice(task, retry); 110 advice(task, retry);
111 } else { 111 } else {
112 logger.verbose('Sudah ada trx dengan tujuan dan denom yg sama, requestId tidak sama. Batalkan.') 112 logger.verbose('Sudah ada trx dengan tujuan dan denom yg sama, requestId tidak sama. Batalkan.')
113 callbackReportWrapper(task.requestId, '55', 'Transaksi duplikat') 113 callbackReportWrapper(task.requestId, '55', 'Transaksi duplikat')
114 } 114 }
115 } 115 }
116 catch(errJSONParse) { 116 catch(errJSONParse) {
117 callbackReportWrapper(task.requestId, '68', "error parsing json"); 117 callbackReportWrapper(task.requestId, '68', "error parsing json");
118 } 118 }
119 } 119 }
120 }); 120 });
121 } 121 }
122 122
123 function calculateSignature(cid, secret, dt) { 123 function calculateSignature(cid, secret, dt) {
124 return crypto.createHash('sha256').update(cid + dt + secret).digest().toString('hex'); 124 return crypto.createHash('sha256').update(cid + dt + secret).digest().toString('hex');
125 } 125 }
126 126
127 function parsePaymentResponse(message) { 127 function parsePaymentResponse(message) {
128 var data = message.split('#'); 128 var data = message.split('#');
129 var retval = {}; 129 var retval = {};
130 130
131 if (data[0] == 'ERROR') { 131 if (data[0] == 'ERROR') {
132 retval = { 132 retval = {
133 status: data[0], 133 status: data[0],
134 rc: data[1], 134 rc: data[1],
135 rcmessage: data[2], 135 rcmessage: data[2],
136 } 136 }
137 137
138 } else { 138 } else {
139 139
140 var i = 0; 140 var i = 0;
141 retval = { 141 retval = {
142 status: data[i++], 142 status: data[i++],
143 rc: data[i++], 143 rc: data[i++],
144 rcmessage: data[i++], 144 rcmessage: data[i++],
145 resptext: data[i++], 145 resptext: data[i++],
146 dt: data[i++], 146 dt: data[i++],
147 refnum: data[i++], 147 refnum: data[i++],
148 voucherid: data[i++], 148 voucherid: data[i++],
149 nominal: data[i++] 149 nominal: data[i++]
150 } 150 }
151 151
152 try { 152 try {
153 retval.sn = data[9]; 153 retval.sn = data[9];
154 } 154 }
155 catch(err) { 155 catch(err) {
156 retval.sn = null; 156 retval.sn = null;
157 } 157 }
158 158
159 if (!retval.sn) { 159 if (!retval.sn) {
160 retval.sn = rehashRefnum(retval.refnum); 160 retval.sn = rehashRefnum(retval.refnum);
161 } 161 }
162 } 162 }
163 163
164 retval.raw = message; 164 retval.raw = message;
165 165
166 return retval; 166 return retval;
167 } 167 }
168 168
169 function rehashRefnum(refnum) { 169 function rehashRefnum(refnum) {
170 var hashed = refnum.replace(/^0+/, ''); 170 var hashed = refnum.replace(/^0+/, '');
171 171
172 try { 172 try {
173 hashed = hashed.replace(/A/g, '1').replace(/B/g, '2').replace(/C/g, '3').replace(/D/g, '4').replace(/E/g, '5').replace(/F/g, '6'); 173 hashed = hashed.replace(/A/g, '1').replace(/B/g, '2').replace(/C/g, '3').replace(/D/g, '4').replace(/E/g, '5').replace(/F/g, '6');
174 hashed = hashed.substring(0, 20); 174 hashed = hashed.substring(0, 20);
175 } 175 }
176 catch(err) { 176 catch(err) {
177 logger.warn('Gagal rehashRefnum: ' + err); 177 logger.warn('Gagal rehashRefnum: ' + err);
178 hashed = refnum; 178 hashed = refnum;
179 } 179 }
180 return hashed; 180 return hashed;
181 } 181 }
182 182
183 function reportPaymentSuccess(task, response) { 183 function reportPaymentSuccess(task, response) {
184 var message = 'SN=' + response.sn + '; ' + response.raw; 184 var message = 'SN=' + response.sn + '; ' + response.raw;
185 185
186 logger.info('Report payment success to ST24', {task: task, response: response}); 186 logger.info('Report payment success to ST24', {task: task, response: response});
187 187
188 callbackReportWrapper(task.requestId, '00', message); 188 callbackReportWrapper(task.requestId, '00', message);
189 } 189 }
190 190
191 function reportPaymentError(task, response) { 191 function reportPaymentError(task, response) {
192 var errorCode = getErrorCode(response.rcmessage); 192 var errorCode = getErrorCode(response.rcmessage);
193 var st24rc = getST24ResponseCode(errorCode); 193 var st24rc = getST24ResponseCode(errorCode);
194 194
195 if (st24rc == '68') { 195 if (st24rc == '68') {
196 logger.info('Got pending response, requesting advice in ' + adviceDelay + 'ms', {task: task, response: response}); 196 logger.info('Got pending response, requesting advice in ' + adviceDelay + 'ms', {task: task, response: response});
197 setTimeout(advice, adviceDelay, task); 197 setTimeout(advice, adviceDelay, task);
198 } 198 }
199 199
200 logger.info('Report payment error/pending to ST24', {supplier_rc: errorCode, st24_rc: st24rc, task: task, response: response}); 200 logger.info('Report payment error/pending to ST24', {supplier_rc: errorCode, st24_rc: st24rc, task: task, response: response});
201 201
202 callbackReportWrapper( 202 callbackReportWrapper(
203 task.requestId, 203 task.requestId,
204 getST24ResponseCode(errorCode), 204 getST24ResponseCode(errorCode),
205 response.raw 205 response.raw
206 ); 206 );
207 } 207 }
208 208
209 function getST24ResponseCode(supplierResponseCode) { 209 function getST24ResponseCode(supplierResponseCode) {
210 var st24rc = '40'; 210 var st24rc = '40';
211 211
212 if (supplierResponseCode.length == 1) { 212 if (supplierResponseCode.length == 1) {
213 supplierResponseCode = '0' + supplierResponseCode; 213 supplierResponseCode = '0' + supplierResponseCode;
214 } 214 }
215 215
216 if (supplierResponseCode == '05') { 216 if (supplierResponseCode == '05') {
217 st24rc = '40'; 217 st24rc = '40';
218 } 218 }
219 else if (supplierResponseCode == '63') { 219 else if (supplierResponseCode == '63') {
220 // kata mp sih harusnya suspect tapi selama ini gagal hasilnya, 220 // kata mp sih harusnya suspect tapi selama ini gagal hasilnya,
221 // contoh: [63] ERROR Tidak ada Pembayaran ke:089664568903 221 // contoh: [63] ERROR Tidak ada Pembayaran ke:089664568903
222 st24rc = '40' 222 st24rc = '40';
223 } 223 }
224 else if (['00', '13', '14', '47', '68'].indexOf(supplierResponseCode) >= 0) { 224 else if (['00', '13', '14', '47', '68'].indexOf(supplierResponseCode) >= 0) {
225 st24rc = supplierResponseCode; 225 st24rc = supplierResponseCode;
226 } 226 }
227 else if (['15', '56'].indexOf(supplierResponseCode) >= 0) { 227 else if (['15', '56'].indexOf(supplierResponseCode) >= 0) {
228 // nomor tidak valid 228 // nomor tidak valid
229 st24rc = '14'; 229 st24rc = '14';
230 } 230 }
231 else if (['18', '68'].indexOf(supplierResponseCode) >= 0) { 231 else if (['18', '68'].indexOf(supplierResponseCode) >= 0) {
232 st24rc = '68'; 232 st24rc = '68';
233 } 233 }
234 else if (supplierResponseCode == '67') { 234 else if (supplierResponseCode == '67') {
235 st24rc = '91' 235 st24rc = '91'
236 } 236 }
237 else if (supplierResponseCode == '46') { 237 else if (supplierResponseCode == '46') {
238 st24rc = '40' 238 st24rc = '40'
239 239
240 if (aaa && config && config.globals && config.globals.pause_on_not_enough_balance 240 if (aaa && config && config.globals && config.globals.pause_on_not_enough_balance
241 && (config.globals.pause_on_not_enough_balance == '1')) { 241 && (config.globals.pause_on_not_enough_balance == '1')) {
242 242
243 logger.warn('Not enough balance detected. Going to pause the system.'); 243 logger.warn('Not enough balance detected. Going to pause the system.');
244 aaa.pause(); 244 aaa.pause();
245 } 245 }
246 } 246 }
247 247
248 return st24rc; 248 return st24rc;
249 } 249 }
250 250
251 function getErrorCode(rcmessage) { 251 function getErrorCode(rcmessage) {
252 try { 252 try {
253 var errorCode = rcmessage.match(/\[(\d+)\]/); 253 var errorCode = rcmessage.match(/\[(\d+)\]/);
254 return errorCode[1]; 254 return errorCode[1];
255 } 255 }
256 catch(err) { 256 catch(err) {
257 logger.warn('Empty RCMESSAGE, returning 68 as RC for safety'); 257 logger.warn('Empty RCMESSAGE, returning 68 as RC for safety');
258 return '68'; 258 return '68';
259 } 259 }
260 } 260 }
261 261
262 function generateDt(taskTimestamp) { 262 function generateDt(taskTimestamp) {
263 if (!taskTimestamp) { 263 if (!taskTimestamp) {
264 return strftime('%Y%m%d', new Date()); 264 return strftime('%Y%m%d', new Date());
265 } 265 }
266 266
267 return taskTimestamp.slice(0, 8); 267 return taskTimestamp.slice(0, 8);
268 } 268 }
269 269
270 function generateRequestOptions(userid, password, partnerUrl, task) { 270 function generateRequestOptions(userid, password, partnerUrl, task) {
271 var dt = generateDt(task.timestamp); 271 var dt = generateDt(task.timestamp);
272 var sign = calculateSignature(userid, password, dt); 272 var sign = calculateSignature(userid, password, dt);
273 273
274 var requestOptions = { 274 var requestOptions = {
275 method: 'GET', 275 method: 'GET',
276 url: partnerUrl, 276 url: partnerUrl,
277 qs: { 277 qs: {
278 modul: '', 278 modul: '',
279 command: '', 279 command: '',
280 tujuan: task['destination'], 280 tujuan: task['destination'],
281 voucherid: task['remoteProduct'], 281 voucherid: task['remoteProduct'],
282 cid: userid, 282 cid: userid,
283 dt: dt, 283 dt: dt,
284 hc: sign, 284 hc: sign,
285 trxid: task['requestId'], 285 trxid: task['requestId'],
286 } 286 }
287 } 287 }
288 288
289 return requestOptions; 289 return requestOptions;
290 } 290 }
291 291
292 function advice(task, retry) { 292 function advice(task, retry) {
293 293
294 if (retry === null || retry === undefined) { 294 if (retry === null || retry === undefined) {
295 retry = 10; 295 retry = 10;
296 } 296 }
297 297
298 var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task); 298 var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task);
299 299
300 requestOptions.qs.modul = 'ISI'; 300 requestOptions.qs.modul = 'ISI';
301 requestOptions.qs.command = 'ADV'; 301 requestOptions.qs.command = 'ADV';
302 302
303 logger.info('Requesting advice to supplier', {requestOptions: requestOptions}); 303 logger.info('Requesting advice to supplier', {requestOptions: requestOptions});
304 request(requestOptions, function(requestError, requestResponse, requestResponseBody) { 304 request(requestOptions, function(requestError, requestResponse, requestResponseBody) {
305 if (requestError || requestResponse.statusCode != 200) { 305 if (requestError || requestResponse.statusCode != 200) {
306 logger.warn('Advice error', {error: requestError, http_response: requestResponse.statusCode}); 306 logger.warn('Advice error', {error: requestError, http_response: requestResponse.statusCode});
307 307
308 if (retry > 0) { 308 if (retry > 0) {
309 logger.warn('Going to retry advice in ' + adviceDelay + 'ms', {task: task, retry: retry}); 309 logger.warn('Going to retry advice in ' + adviceDelay + 'ms', {task: task, retry: retry});
310 setTimeout(advice, adviceDelay, task, --retry); 310 setTimeout(advice, adviceDelay, task, --retry);
311 } 311 }
312 312
313 return; 313 return;
314 } 314 }
315 315
316 var paymentResponse = parsePaymentResponse(requestResponseBody); 316 var paymentResponse = parsePaymentResponse(requestResponseBody);
317 logger.info('Got advice payment response', {paymentResponse: paymentResponse}); 317 logger.info('Got advice payment response', {paymentResponse: paymentResponse});
318 318
319 if (paymentResponse.status == 'SUCCESS') { 319 if (paymentResponse.status == 'SUCCESS') {
320 reportPaymentSuccess(task, paymentResponse); 320 reportPaymentSuccess(task, paymentResponse);
321 } 321 }
322 else { 322 else {
323 reportPaymentError(task, paymentResponse); 323 reportPaymentError(task, paymentResponse);
324 } 324 }
325 }); 325 });
326 326
327 } 327 }
328 328
329 function voucherPay(task) { 329 function voucherPay(task) {
330 var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task); 330 var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task);
331 331
332 requestOptions.qs.modul = 'ISI'; 332 requestOptions.qs.modul = 'ISI';
333 requestOptions.qs.command = 'PAY'; 333 requestOptions.qs.command = 'PAY';
334 334
335 logger.info('Requesting auto payment to supplier', {requestOptions: requestOptions}); 335 logger.info('Requesting auto payment to supplier', {requestOptions: requestOptions});
336 request(requestOptions, function(requestError, requestResponse, requestResponseBody) { 336 request(requestOptions, function(requestError, requestResponse, requestResponseBody) {
337 if (requestError) { 337 if (requestError) {
338 logger.warn('Request error', {error: requestError}); 338 logger.warn('Request error', {error: requestError});
339 339
340 setTimeout(advice, adviceDelay, task); 340 setTimeout(advice, adviceDelay, task);
341 return; 341 return;
342 } 342 }
343 343
344 if (requestResponse.statusCode != 200) { 344 if (requestResponse.statusCode != 200) {
345 logger.warn('HTTP response status code is not 200', {http_response: requestResponse.statusCode}); 345 logger.warn('HTTP response status code is not 200', {http_response: requestResponse.statusCode});
346 346
347 setTimeout(advice, adviceDelay, task); 347 setTimeout(advice, adviceDelay, task);
348 return; 348 return;
349 } 349 }
350 350
351 logger.info('Supplier response: ' + requestResponseBody); 351 logger.info('Supplier response: ' + requestResponseBody);
352 352
353 var paymentResponse = parsePaymentResponse(requestResponseBody); 353 var paymentResponse = parsePaymentResponse(requestResponseBody);
354 logger.info('Got payment response', {paymentResponse: paymentResponse}); 354 logger.info('Got payment response', {paymentResponse: paymentResponse});
355 355
356 if (paymentResponse.status == 'SUCCESS') { 356 if (paymentResponse.status == 'SUCCESS') {
357 reportPaymentSuccess(task, paymentResponse); 357 reportPaymentSuccess(task, paymentResponse);
358 } 358 }
359 else { 359 else {
360 reportPaymentError(task, paymentResponse); 360 reportPaymentError(task, paymentResponse);
361 } 361 }
362 }); 362 });
363 } 363 }
364 364
365 exports.start = start; 365 exports.start = start;
366 exports.topupRequest = topupRequest; 366 exports.topupRequest = topupRequest;
367 exports.calculateSignature = calculateSignature; 367 exports.calculateSignature = calculateSignature;
368 exports.parsePaymentResponse = parsePaymentResponse; 368 exports.parsePaymentResponse = parsePaymentResponse;
369 exports.getErrorCode = getErrorCode; 369 exports.getErrorCode = getErrorCode;
370 exports.getST24ResponseCode = getST24ResponseCode; 370 exports.getST24ResponseCode = getST24ResponseCode;
371 exports.generateDt = generateDt; 371 exports.generateDt = generateDt;
372 372