Commit 810851055ac56199bd5c5f2ad07078029a60f5de

Authored by Adhidarma Hadiwinoto
1 parent f987dc6b6b
Exists in master

perbaikan real sn

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