Commit 866d1d2c166f2a3636b8b3bfa9e3829226affa9b

Authored by Adhidarma Hadiwinoto
1 parent 53eca14902
Exists in master

sn panjang 16 karakter

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