Commit 2c73365ae40ffd910a0402af671fbccbe9a349d3
1 parent
f2bf78e38a
Exists in
master
ready to test
Showing 2 changed files with 287 additions and 0 deletions Side-by-side Diff
package.json
... | ... | @@ -21,8 +21,11 @@ |
21 | 21 | "author": "Adhidarma Hadiwinoto <gua@adhisimon.org>", |
22 | 22 | "license": "ISC", |
23 | 23 | "dependencies": { |
24 | + "redis": "^2.6.0-2", | |
25 | + "request": "^2.72.0", | |
24 | 26 | "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git", |
25 | 27 | "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git", |
28 | + "strftime": "^0.9.2", | |
26 | 29 | "winston": "^2.2.0" |
27 | 30 | } |
28 | 31 | } |
partner-masterpulsa-voucher.js
... | ... | @@ -0,0 +1,284 @@ |
1 | +var winston = require('winston'); | |
2 | +var request = require('request'); | |
3 | +var strftime = require('strftime'); | |
4 | +var crypto = require('crypto'); | |
5 | +var redis = require('redis'); | |
6 | + | |
7 | +var config; | |
8 | +var callbackReport; | |
9 | +var aaa; | |
10 | +var logger; | |
11 | +var options; | |
12 | +var redisClient; | |
13 | + | |
14 | +var adviceDelay = 10000; | |
15 | + | |
16 | +function createRedisClient() { | |
17 | + redisClient = redis.createClient(config.globals.redis_port, config.globals.redis_host); | |
18 | +} | |
19 | + | |
20 | +function start(_config, _callbackReport, options) { | |
21 | + config = _config; | |
22 | + callbackReport = _callbackReport | |
23 | + | |
24 | + if (options && options.aaa) { | |
25 | + aaa = options.aaa; | |
26 | + } | |
27 | + | |
28 | + if (options && options.logger) { | |
29 | + logger = options.logger; | |
30 | + } else { | |
31 | + logger = new winston.Logger({ | |
32 | + transports: [ | |
33 | + new (winston.transports.Console)() | |
34 | + ] | |
35 | + }); | |
36 | + } | |
37 | + | |
38 | + createRedisClient(); | |
39 | +} | |
40 | + | |
41 | +function getRedisKey(task) { | |
42 | + return config.globals.gateway_name + '.tid:' + task.requestId; | |
43 | +} | |
44 | + | |
45 | +function putTaskToRedis(task) { | |
46 | + var redisKey = getRedisKey(task); | |
47 | + var taskInJSON = JSON.stringify(task); | |
48 | + | |
49 | + redisClient.set(redisKey, taskInJSON); | |
50 | + redisClient.expire(redisKey, 3600*24*7); | |
51 | +} | |
52 | + | |
53 | +function topupRequest(task, retry) { | |
54 | + var redisKey = getRedisKey(task); | |
55 | + | |
56 | + redisClient.get(redisKey, function(err, result) { | |
57 | + if (err || !result) { | |
58 | + | |
59 | + //logger.info('Redis error or not found', {redisKey: redisKey, error: err, result: result}); | |
60 | + putTaskToRedis(task); | |
61 | + payVoucher(task); | |
62 | + | |
63 | + } else { | |
64 | + advice(task); | |
65 | + } | |
66 | + }); | |
67 | +} | |
68 | + | |
69 | +function calculateSignature(cid, secret, dt) { | |
70 | + return crypto.createHash('sha256').update(cid + dt + secret).digest().toString('hex'); | |
71 | +} | |
72 | + | |
73 | +function parsePaymentResponse(message) { | |
74 | + var data = message.split('#'); | |
75 | + var retval = { | |
76 | + raw: message | |
77 | + }; | |
78 | + | |
79 | + if (data[0] == 'ERROR') { | |
80 | + retval = { | |
81 | + status: data[0], | |
82 | + rc: data[1], | |
83 | + rcmessage: data[2], | |
84 | + } | |
85 | + | |
86 | + } else { | |
87 | + | |
88 | + var i = 0; | |
89 | + retval = { | |
90 | + status: data[i++], | |
91 | + rc: data[i++], | |
92 | + rcmessage: data[i++], | |
93 | + resptext: data[i++], | |
94 | + dt: data[i++], | |
95 | + refnum: data[i++], | |
96 | + voucherid: data[i++], | |
97 | + nominal: data[i++] | |
98 | + } | |
99 | + } | |
100 | + | |
101 | + return retval; | |
102 | +} | |
103 | + | |
104 | +function reportPaymentSuccess(task, response) { | |
105 | + var message = 'SN=' + response.refnum + '; ' + response.raw; | |
106 | + | |
107 | + logger.info('Report payment success to ST24', {task: task, response: response}); | |
108 | + | |
109 | + callbackReport(task.requestId, '00', message); | |
110 | +} | |
111 | + | |
112 | +function reportPaymentError(task, response) { | |
113 | + var errorCode = getErrorCode(response.rcmessage); | |
114 | + var st24rc = getST24ResponseCode(errorCode); | |
115 | + | |
116 | + if (st24rc == '68') { | |
117 | + logger.info('Got pending response, requesting advice in ' + adviceDelay + 'ms', {task: task, response: response}); | |
118 | + setTimeout(advice, adviceDelay, task); | |
119 | + } | |
120 | + | |
121 | + logger.info('Report payment error/pending to ST24', {supplier_rc: errorCode, st24_rc: st24rc, task: task, response: response}); | |
122 | + | |
123 | + callbackReport( | |
124 | + task.requestId, | |
125 | + getST24ResponseCode(errorCode), | |
126 | + response.raw | |
127 | + ); | |
128 | +} | |
129 | + | |
130 | +function getST24ResponseCode(supplierResponseCode) { | |
131 | + var st24rc = '40'; | |
132 | + | |
133 | + if (supplierResponseCode.length == 1) { | |
134 | + supplierResponseCode = '0' + supplierResponseCode; | |
135 | + } | |
136 | + | |
137 | + if (['00', '13', '14', '47', '68'].indexOf(supplierResponseCode) >= 0) { | |
138 | + st24rc = supplierResponseCode; | |
139 | + } | |
140 | + else if (supplierResponseCode == '15') { | |
141 | + st24rc = '14'; | |
142 | + } | |
143 | + else if (['05', '18', '63', '68'].indexOf(supplierResponseCode) >= 0) { | |
144 | + st24rc = '68'; | |
145 | + } | |
146 | + else if (supplierResponseCode == '67') { | |
147 | + st24rc = '91' | |
148 | + } | |
149 | + else if (supplierResponseCode == '46') { | |
150 | + st24rc = '40' | |
151 | + | |
152 | + if (aaa && config && config.globals && config.globals.pause_on_not_enough_balance | |
153 | + && (config.globals.pause_on_not_enough_balance == '1')) { | |
154 | + | |
155 | + logger.warn('Not enough balance detected. Going to pause the system.'); | |
156 | + aaa.pause(); | |
157 | + } | |
158 | + } | |
159 | + | |
160 | + return st24rc; | |
161 | +} | |
162 | + | |
163 | +function getErrorCode(rcmessage) { | |
164 | + try { | |
165 | + var errorCode = rcmessage.match(/\[(\d+)\]/); | |
166 | + return errorCode[1]; | |
167 | + } | |
168 | + catch(err) { | |
169 | + logger.warn('Empty RCMESSAGE, returning 68 as RC for safety'); | |
170 | + return '68'; | |
171 | + } | |
172 | +} | |
173 | + | |
174 | +function generateDt(taskTimestamp) { | |
175 | + if (!taskTimestamp) { | |
176 | + return strftime('%Y%m%d', new Date()); | |
177 | + } | |
178 | + | |
179 | + return taskTimestamp.slice(0, 8); | |
180 | +} | |
181 | + | |
182 | +function generateRequestOptions(userid, password, partnerUrl, task) { | |
183 | + var dt = generateDt(task.timestamp); | |
184 | + var sign = calculateSignature(userid, password, dt); | |
185 | + | |
186 | + var requestOptions = { | |
187 | + method: 'GET', | |
188 | + url: partnerUrl, | |
189 | + qs: { | |
190 | + modul: '', | |
191 | + command: '', | |
192 | + tujuan: task['destination'], | |
193 | + voucherid: task['remoteProduct'], | |
194 | + cid: userid, | |
195 | + dt: dt, | |
196 | + hc: sign, | |
197 | + trxid: task['requestId'], | |
198 | + } | |
199 | + } | |
200 | + | |
201 | + return requestOptions; | |
202 | +} | |
203 | + | |
204 | +function advice(task, retry) { | |
205 | + | |
206 | + if (retry === null || retry === undefined) { | |
207 | + retry = 10; | |
208 | + } | |
209 | + | |
210 | + var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task); | |
211 | + | |
212 | + requestOptions.qs.modul = 'ISI'; | |
213 | + requestOptions.qs.command = 'ADV'; | |
214 | + | |
215 | + logger.info('Requesting advice to supplier', {requestOptions: requestOptions}); | |
216 | + request(requestOptions, function(requestError, requestResponse, requestResponseBody) { | |
217 | + if (requestError || requestResponse.statusCode != 200) { | |
218 | + logger.warn('Advice error', {error: request_error, http_response: requestResponse.statusCode}); | |
219 | + | |
220 | + if (retry > 0) { | |
221 | + logger.warn('Going to retry advice in ' + adviceDelay + 'ms', {task: task, retry: retry}); | |
222 | + setTimeout(advice, adviceDelay, task, --retry); | |
223 | + } | |
224 | + | |
225 | + return; | |
226 | + } | |
227 | + | |
228 | + var paymentResponse = parsePaymentResponse(requestResponseBody); | |
229 | + logger.info('Got advice payment response', {paymentResponse: paymentResponse}); | |
230 | + | |
231 | + if (paymentResponse.status == 'SUCCESS') { | |
232 | + reportPaymentSuccess(task, paymentResponse); | |
233 | + } | |
234 | + else { | |
235 | + reportPaymentError(task, paymentResponse); | |
236 | + } | |
237 | + }); | |
238 | + | |
239 | +} | |
240 | + | |
241 | +function voucherPay(task) { | |
242 | + var requestOptions = generateRequestOptions(config.h2h_out.userid, config.h2h_out.password, config.h2h_out.partner, task); | |
243 | + | |
244 | + requestOptions.qs.modul = 'ISI'; | |
245 | + requestOptions.qs.command = 'PAY'; | |
246 | + | |
247 | + logger.info('Requesting auto payment to supplier', {requestOptions: requestOptions}); | |
248 | + request(requestOptions, function(requestError, requestResponse, requestResponseBody) { | |
249 | + if (requestError) { | |
250 | + logger.warn('Request error', {error: requestError}); | |
251 | + | |
252 | + setTimeout(advice, adviceDelay, task); | |
253 | + return; | |
254 | + } | |
255 | + | |
256 | + if (requestResponse.statusCode != 200) { | |
257 | + logger.warn('HTTP response status code is not 200', {http_response: requestResponse.statusCode}); | |
258 | + | |
259 | + setTimeout(advice, adviceDelay, task); | |
260 | + return; | |
261 | + } | |
262 | + | |
263 | + logger.info('Supplier response: ' + requestResponseBody); | |
264 | + | |
265 | + var paymentResponse = parsePaymentResponse(requestResponseBody); | |
266 | + logger.info('Got payment response', {paymentResponse: paymentResponse}); | |
267 | + | |
268 | + if (paymentResponse.status == 'SUCCESS') { | |
269 | + reportPaymentSuccess(task, paymentResponse); | |
270 | + } | |
271 | + else { | |
272 | + reportPaymentError(task, paymentResponse); | |
273 | + } | |
274 | + }); | |
275 | +} | |
276 | + | |
277 | +exports.start = start; | |
278 | +exports.topupRequest = topupRequest; | |
279 | +exports.calculateSignature = calculateSignature; | |
280 | +exports.parsePaymentResponse = parsePaymentResponse; | |
281 | +exports.getErrorCode = getErrorCode; | |
282 | +exports.getST24ResponseCode = getST24ResponseCode; | |
283 | +exports.generateSN = generateSN; | |
284 | +exports.generateDt = generateDt; |