Commit 4334f5ca888f4108468a50fcb28cb3552f34882d

Authored by Adhidarma Hadiwinoto
1 parent 3d923a578c
Exists in master

sn real

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

1 node_modules/ 1 node_modules/
2 logs/log* 2 logs/log*
3 config.ini*
4 log.txt
5 run.sh
3 6
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
137 try {
138 retval.sn = data[9];
139 }
140 catch(err) {
141 retval.sn = retval.refnum;
142 }
136 } 143 }
137 144
138 retval.raw = message; 145 retval.raw = message;
139 146
140 return retval; 147 return retval;
141 } 148 }
142 149
143 function rehashRefnum(refnum) { 150 function rehashRefnum(refnum) {
144 var hashed = refnum.replace(/^0+/, ''); 151 var hashed = refnum.replace(/^0+/, '');
145 152
146 try { 153 try {
147 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'); 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');
148 hashed = hashed.substring(0, 20); 155 hashed = hashed.substring(0, 20);
149 } 156 }
150 catch(err) { 157 catch(err) {
151 logger.warn('Gagal rehashRefnum: ' + err); 158 logger.warn('Gagal rehashRefnum: ' + err);
152 hashed = refnum; 159 hashed = refnum;
153 } 160 }
154 return hashed; 161 return hashed;
155 } 162 }
156 163
157 function reportPaymentSuccess(task, response) { 164 function reportPaymentSuccess(task, response) {
158 var sn = rehashRefnum(response.refnum); 165 var sn = '';
166 if (response.sn) {
167 sn = response.sn;
168 } else {
169 sn = rehashRefnum(response.refnum);
170 }
171
159 var message = 'SN=' + sn + '; ' + response.raw; 172 var message = 'SN=' + sn + '; ' + response.raw;
160 173
161 logger.info('Report payment success to ST24', {task: task, response: response}); 174 logger.info('Report payment success to ST24', {task: task, response: response});
162 175
163 callbackReportWrapper(task.requestId, '00', message); 176 callbackReportWrapper(task.requestId, '00', message);
164 } 177 }
165 178
166 function reportPaymentError(task, response) { 179 function reportPaymentError(task, response) {
167 var errorCode = getErrorCode(response.rcmessage); 180 var errorCode = getErrorCode(response.rcmessage);
168 var st24rc = getST24ResponseCode(errorCode); 181 var st24rc = getST24ResponseCode(errorCode);
169 182
170 if (st24rc == '68') { 183 if (st24rc == '68') {
171 logger.info('Got pending response, requesting advice in ' + adviceDelay + 'ms', {task: task, response: response}); 184 logger.info('Got pending response, requesting advice in ' + adviceDelay + 'ms', {task: task, response: response});
172 setTimeout(advice, adviceDelay, task); 185 setTimeout(advice, adviceDelay, task);
173 } 186 }
174 187
175 logger.info('Report payment error/pending to ST24', {supplier_rc: errorCode, st24_rc: st24rc, task: task, response: response}); 188 logger.info('Report payment error/pending to ST24', {supplier_rc: errorCode, st24_rc: st24rc, task: task, response: response});
176 189
177 callbackReportWrapper( 190 callbackReportWrapper(
178 task.requestId, 191 task.requestId,
179 getST24ResponseCode(errorCode), 192 getST24ResponseCode(errorCode),
180 response.raw 193 response.raw
181 ); 194 );
182 } 195 }
183 196
184 function getST24ResponseCode(supplierResponseCode) { 197 function getST24ResponseCode(supplierResponseCode) {
185 var st24rc = '40'; 198 var st24rc = '40';
186 199
187 if (supplierResponseCode.length == 1) { 200 if (supplierResponseCode.length == 1) {
188 supplierResponseCode = '0' + supplierResponseCode; 201 supplierResponseCode = '0' + supplierResponseCode;
189 } 202 }
190 203
191 if (supplierResponseCode == '05') { 204 if (supplierResponseCode == '05') {
192 st24rc = '40'; 205 st24rc = '40';
193 } 206 }
194 else if (['00', '13', '14', '47', '68'].indexOf(supplierResponseCode) >= 0) { 207 else if (['00', '13', '14', '47', '68'].indexOf(supplierResponseCode) >= 0) {
195 st24rc = supplierResponseCode; 208 st24rc = supplierResponseCode;
196 } 209 }
197 else if (['15', '56'].indexOf(supplierResponseCode) >= 0) { 210 else if (['15', '56'].indexOf(supplierResponseCode) >= 0) {
198 // nomor tidak valid 211 // nomor tidak valid
199 st24rc = '14'; 212 st24rc = '14';
200 } 213 }
201 else if (['18', '63', '68'].indexOf(supplierResponseCode) >= 0) { 214 else if (['18', '63', '68'].indexOf(supplierResponseCode) >= 0) {
202 st24rc = '68'; 215 st24rc = '68';
203 } 216 }
204 else if (supplierResponseCode == '67') { 217 else if (supplierResponseCode == '67') {
205 st24rc = '91' 218 st24rc = '91'
206 } 219 }
207 else if (supplierResponseCode == '46') { 220 else if (supplierResponseCode == '46') {
208 st24rc = '40' 221 st24rc = '40'
209 222
210 if (aaa && config && config.globals && config.globals.pause_on_not_enough_balance 223 if (aaa && config && config.globals && config.globals.pause_on_not_enough_balance
211 && (config.globals.pause_on_not_enough_balance == '1')) { 224 && (config.globals.pause_on_not_enough_balance == '1')) {
212 225
213 logger.warn('Not enough balance detected. Going to pause the system.'); 226 logger.warn('Not enough balance detected. Going to pause the system.');
214 aaa.pause(); 227 aaa.pause();
215 } 228 }
216 } 229 }
217 230
218 return st24rc; 231 return st24rc;
219 } 232 }
220 233
221 function getErrorCode(rcmessage) { 234 function getErrorCode(rcmessage) {
222 try { 235 try {
223 var errorCode = rcmessage.match(/\[(\d+)\]/); 236 var errorCode = rcmessage.match(/\[(\d+)\]/);
224 return errorCode[1]; 237 return errorCode[1];
225 } 238 }
226 catch(err) { 239 catch(err) {
227 logger.warn('Empty RCMESSAGE, returning 68 as RC for safety'); 240 logger.warn('Empty RCMESSAGE, returning 68 as RC for safety');
228 return '68'; 241 return '68';
229 } 242 }
230 } 243 }
231 244
232 function generateDt(taskTimestamp) { 245 function generateDt(taskTimestamp) {
233 if (!taskTimestamp) { 246 if (!taskTimestamp) {
234 return strftime('%Y%m%d', new Date()); 247 return strftime('%Y%m%d', new Date());
235 } 248 }
236 249
237 return taskTimestamp.slice(0, 8); 250 return taskTimestamp.slice(0, 8);
238 } 251 }
239 252
240 function generateRequestOptions(userid, password, partnerUrl, task) { 253 function generateRequestOptions(userid, password, partnerUrl, task) {
241 var dt = generateDt(task.timestamp); 254 var dt = generateDt(task.timestamp);
242 var sign = calculateSignature(userid, password, dt); 255 var sign = calculateSignature(userid, password, dt);
243 256
244 var requestOptions = { 257 var requestOptions = {
245 method: 'GET', 258 method: 'GET',
246 url: partnerUrl, 259 url: partnerUrl,
247 qs: { 260 qs: {
248 modul: '', 261 modul: '',
249 command: '', 262 command: '',
250 tujuan: task['destination'], 263 tujuan: task['destination'],
251 voucherid: task['remoteProduct'], 264 voucherid: task['remoteProduct'],
252 cid: userid, 265 cid: userid,
253 dt: dt, 266 dt: dt,
254 hc: sign, 267 hc: sign,
255 trxid: task['requestId'], 268 trxid: task['requestId'],
256 } 269 }
257 } 270 }
258 271
259 return requestOptions; 272 return requestOptions;
260 } 273 }
261 274
262 function advice(task, retry) { 275 function advice(task, retry) {
263 276
264 if (retry === null || retry === undefined) { 277 if (retry === null || retry === undefined) {
265 retry = 10; 278 retry = 10;
266 } 279 }
267 280
268 var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task); 281 var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task);
269 282
270 requestOptions.qs.modul = 'ISI'; 283 requestOptions.qs.modul = 'ISI';
271 requestOptions.qs.command = 'ADV'; 284 requestOptions.qs.command = 'ADV';
272 285
273 logger.info('Requesting advice to supplier', {requestOptions: requestOptions}); 286 logger.info('Requesting advice to supplier', {requestOptions: requestOptions});
274 request(requestOptions, function(requestError, requestResponse, requestResponseBody) { 287 request(requestOptions, function(requestError, requestResponse, requestResponseBody) {
275 if (requestError || requestResponse.statusCode != 200) { 288 if (requestError || requestResponse.statusCode != 200) {
276 logger.warn('Advice error', {error: requestError, http_response: requestResponse.statusCode}); 289 logger.warn('Advice error', {error: requestError, http_response: requestResponse.statusCode});
277 290
278 if (retry > 0) { 291 if (retry > 0) {
279 logger.warn('Going to retry advice in ' + adviceDelay + 'ms', {task: task, retry: retry}); 292 logger.warn('Going to retry advice in ' + adviceDelay + 'ms', {task: task, retry: retry});
280 setTimeout(advice, adviceDelay, task, --retry); 293 setTimeout(advice, adviceDelay, task, --retry);
281 } 294 }
282 295
283 return; 296 return;
284 } 297 }
285 298
286 var paymentResponse = parsePaymentResponse(requestResponseBody); 299 var paymentResponse = parsePaymentResponse(requestResponseBody);
287 logger.info('Got advice payment response', {paymentResponse: paymentResponse}); 300 logger.info('Got advice payment response', {paymentResponse: paymentResponse});
288 301
289 if (paymentResponse.status == 'SUCCESS') { 302 if (paymentResponse.status == 'SUCCESS') {
290 reportPaymentSuccess(task, paymentResponse); 303 reportPaymentSuccess(task, paymentResponse);
291 } 304 }
292 else { 305 else {
293 reportPaymentError(task, paymentResponse); 306 reportPaymentError(task, paymentResponse);
294 } 307 }
295 }); 308 });
296 309
297 } 310 }
298 311
299 function voucherPay(task) { 312 function voucherPay(task) {
300 var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task); 313 var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task);
301 314
302 requestOptions.qs.modul = 'ISI'; 315 requestOptions.qs.modul = 'ISI';
303 requestOptions.qs.command = 'PAY'; 316 requestOptions.qs.command = 'PAY';
304 317
305 logger.info('Requesting auto payment to supplier', {requestOptions: requestOptions}); 318 logger.info('Requesting auto payment to supplier', {requestOptions: requestOptions});
306 request(requestOptions, function(requestError, requestResponse, requestResponseBody) { 319 request(requestOptions, function(requestError, requestResponse, requestResponseBody) {
307 if (requestError) { 320 if (requestError) {
308 logger.warn('Request error', {error: requestError}); 321 logger.warn('Request error', {error: requestError});
309 322
310 setTimeout(advice, adviceDelay, task); 323 setTimeout(advice, adviceDelay, task);
311 return; 324 return;
312 } 325 }
313 326
314 if (requestResponse.statusCode != 200) { 327 if (requestResponse.statusCode != 200) {
315 logger.warn('HTTP response status code is not 200', {http_response: requestResponse.statusCode}); 328 logger.warn('HTTP response status code is not 200', {http_response: requestResponse.statusCode});
316 329
317 setTimeout(advice, adviceDelay, task); 330 setTimeout(advice, adviceDelay, task);
318 return; 331 return;
319 } 332 }
320 333
321 logger.info('Supplier response: ' + requestResponseBody); 334 logger.info('Supplier response: ' + requestResponseBody);
322 335
323 var paymentResponse = parsePaymentResponse(requestResponseBody); 336 var paymentResponse = parsePaymentResponse(requestResponseBody);
324 logger.info('Got payment response', {paymentResponse: paymentResponse}); 337 logger.info('Got payment response', {paymentResponse: paymentResponse});
325 338
326 if (paymentResponse.status == 'SUCCESS') { 339 if (paymentResponse.status == 'SUCCESS') {
327 reportPaymentSuccess(task, paymentResponse); 340 reportPaymentSuccess(task, paymentResponse);
328 } 341 }
329 else { 342 else {
330 reportPaymentError(task, paymentResponse); 343 reportPaymentError(task, paymentResponse);
331 } 344 }
332 }); 345 });
333 } 346 }
334 347
335 exports.start = start; 348 exports.start = start;
336 exports.topupRequest = topupRequest; 349 exports.topupRequest = topupRequest;
337 exports.calculateSignature = calculateSignature; 350 exports.calculateSignature = calculateSignature;
338 exports.parsePaymentResponse = parsePaymentResponse; 351 exports.parsePaymentResponse = parsePaymentResponse;
339 exports.getErrorCode = getErrorCode; 352 exports.getErrorCode = getErrorCode;
340 exports.getST24ResponseCode = getST24ResponseCode; 353 exports.getST24ResponseCode = getST24ResponseCode;
341 exports.generateDt = generateDt; 354 exports.generateDt = generateDt;
342 355