Commit c5e243b292688ebd12d76587132e08164d9d8728
1 parent
0dbce6884a
Exists in
master
topup jika tidak ada data
Showing 1 changed file with 5 additions and 0 deletions Inline Diff
partner-trustlink.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 url = require('url'); | 4 | var url = require('url'); |
5 | var xor = require('base64-xor'); | 5 | var xor = require('base64-xor'); |
6 | var http = require('http'); | 6 | var http = require('http'); |
7 | var xml = require('xml'); | 7 | var xml = require('xml'); |
8 | var xml2js = require('xml2js').parseString; | 8 | var xml2js = require('xml2js').parseString; |
9 | var redis = require('redis'); | 9 | var redis = require('redis'); |
10 | 10 | ||
11 | var max_retry = 0; | 11 | var max_retry = 0; |
12 | var sleep_before_retry = 2000; | 12 | var sleep_before_retry = 2000; |
13 | 13 | ||
14 | var config; | 14 | var config; |
15 | var callbackReport; | 15 | var callbackReport; |
16 | var aaa; | 16 | var aaa; |
17 | var logger; | 17 | var logger; |
18 | var options; | 18 | var options; |
19 | var redisClient; | 19 | var redisClient; |
20 | 20 | ||
21 | function start(_config, _callbackReport, options) { | 21 | function start(_config, _callbackReport, options) { |
22 | config = _config; | 22 | config = _config; |
23 | callbackReport = _callbackReport | 23 | callbackReport = _callbackReport |
24 | 24 | ||
25 | if (options && options.aaa) { | 25 | if (options && options.aaa) { |
26 | aaa = options.aaa; | 26 | aaa = options.aaa; |
27 | } | 27 | } |
28 | 28 | ||
29 | if (options && options.logger) { | 29 | if (options && options.logger) { |
30 | logger = options.logger; | 30 | logger = options.logger; |
31 | } else { | 31 | } else { |
32 | logger = new winston.Logger({ | 32 | logger = new winston.Logger({ |
33 | transports: [ | 33 | transports: [ |
34 | new (winston.transports.Console)() | 34 | new (winston.transports.Console)() |
35 | ] | 35 | ] |
36 | }); | 36 | }); |
37 | } | 37 | } |
38 | 38 | ||
39 | createRedisClient(); | 39 | createRedisClient(); |
40 | createReverseReportServer(); | 40 | createReverseReportServer(); |
41 | } | 41 | } |
42 | 42 | ||
43 | function createReverseReportServer() { | 43 | function createReverseReportServer() { |
44 | var httpServer = http.createServer(onReverseReport).listen(config.h2h_out.listen_port, function() { | 44 | var httpServer = http.createServer(onReverseReport).listen(config.h2h_out.listen_port, function() { |
45 | logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port); | 45 | logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port); |
46 | }); | 46 | }); |
47 | } | 47 | } |
48 | 48 | ||
49 | function onReverseReport(req, res) { | 49 | function onReverseReport(req, res) { |
50 | res.end('OK'); | 50 | res.end('OK'); |
51 | 51 | ||
52 | var qs = url.parse(req.url, true).query; | 52 | var qs = url.parse(req.url, true).query; |
53 | logger.info('Reverse Report', {qs: qs}); | 53 | logger.info('Reverse Report', {qs: qs}); |
54 | } | 54 | } |
55 | 55 | ||
56 | function calculateSignature(ts, destination, password) { | 56 | function calculateSignature(ts, destination, password) { |
57 | var a = ts + destination.substr(destination.length - 4); | 57 | var a = ts + destination.substr(destination.length - 4); |
58 | var b = destination.substr(destination.length - 4).split('').reverse().join('') + password; | 58 | var b = destination.substr(destination.length - 4).split('').reverse().join('') + password; |
59 | 59 | ||
60 | return xor.encode(a,b); | 60 | return xor.encode(a,b); |
61 | } | 61 | } |
62 | 62 | ||
63 | function createXmlPayload(task, userid, password) { | 63 | function createXmlPayload(task, userid, password) { |
64 | var ts = strftime('%H%M%S', new Date()); | 64 | var ts = strftime('%H%M%S', new Date()); |
65 | 65 | ||
66 | var signature = calculateSignature(ts, task.destination, password); | 66 | var signature = calculateSignature(ts, task.destination, password); |
67 | 67 | ||
68 | var payload = { | 68 | var payload = { |
69 | evoucher: [ | 69 | evoucher: [ |
70 | {command: 'TOPUP'}, | 70 | {command: 'TOPUP'}, |
71 | {product: task.remoteProduct}, | 71 | {product: task.remoteProduct}, |
72 | {userid: userid}, | 72 | {userid: userid}, |
73 | {time: ts}, | 73 | {time: ts}, |
74 | {msisdn: task.destination}, | 74 | {msisdn: task.destination}, |
75 | {partner_trxid: task.requestId}, | 75 | {partner_trxid: task.requestId}, |
76 | {signature: signature}, | 76 | {signature: signature}, |
77 | {trxke: 1}, | 77 | {trxke: 1}, |
78 | ] | 78 | ] |
79 | }; | 79 | }; |
80 | 80 | ||
81 | if (logger) { | 81 | if (logger) { |
82 | logger.verbose('Generate xml payload', {payload: payload}); | 82 | logger.verbose('Generate xml payload', {payload: payload}); |
83 | } | 83 | } |
84 | 84 | ||
85 | return "<?xml version=\"1.0\" ?>\n" + xml(payload); | 85 | return "<?xml version=\"1.0\" ?>\n" + xml(payload); |
86 | } | 86 | } |
87 | 87 | ||
88 | function topupRequestHit(task, retry) { | 88 | function topupRequestHit(task, retry) { |
89 | if (retry === undefined) { | 89 | if (retry === undefined) { |
90 | retry = max_retry; | 90 | retry = max_retry; |
91 | } | 91 | } |
92 | 92 | ||
93 | var payload = createXmlPayload(task, config.h2h_out.userid, config.h2h_out.password); | 93 | var payload = createXmlPayload(task, config.h2h_out.userid, config.h2h_out.password); |
94 | 94 | ||
95 | var partner = url.parse(config.h2h_out.partner); | 95 | var partner = url.parse(config.h2h_out.partner); |
96 | 96 | ||
97 | var request_options = { | 97 | var request_options = { |
98 | host: partner.hostname, | 98 | host: partner.hostname, |
99 | path: partner.path, | 99 | path: partner.path, |
100 | port: partner.port, | 100 | port: partner.port, |
101 | method: "POST", | 101 | method: "POST", |
102 | headers: { | 102 | headers: { |
103 | 'Content-Type': 'text/xml', | 103 | 'Content-Type': 'text/xml', |
104 | 'Content-Length': Buffer.byteLength(payload) | 104 | 'Content-Length': Buffer.byteLength(payload) |
105 | } | 105 | } |
106 | }; | 106 | }; |
107 | 107 | ||
108 | var buffer = ""; | 108 | var buffer = ""; |
109 | 109 | ||
110 | logger.info('Requesting to partner', {request_options: request_options}); | 110 | logger.info('Requesting to partner', {request_options: request_options}); |
111 | 111 | ||
112 | var req = http.request(request_options, function( res ) { | 112 | var req = http.request(request_options, function( res ) { |
113 | 113 | ||
114 | logger.verbose('Status code: ' + res.statusCode ); | 114 | logger.verbose('Status code: ' + res.statusCode ); |
115 | var buffer = ""; | 115 | var buffer = ""; |
116 | res.on( "data", function( data ) { buffer = buffer + data; } ); | 116 | res.on( "data", function( data ) { buffer = buffer + data; } ); |
117 | res.on( "end", function( data ) { | 117 | res.on( "end", function( data ) { |
118 | logger.verbose('Got direct response from partner', {resp: buffer}); | 118 | logger.verbose('Got direct response from partner', {resp: buffer}); |
119 | directResponseHandler(buffer, task); | 119 | directResponseHandler(buffer, task); |
120 | }); | 120 | }); |
121 | 121 | ||
122 | }); | 122 | }); |
123 | 123 | ||
124 | req.on('error', function(e) { | 124 | req.on('error', function(e) { |
125 | logger.warn('problem with request: ' + e.message); | 125 | logger.warn('problem with request: ' + e.message); |
126 | callbackReport(task.requestId, '68', e.message); | 126 | callbackReport(task.requestId, '68', e.message); |
127 | return; | 127 | return; |
128 | }); | 128 | }); |
129 | 129 | ||
130 | logger.verbose('Sending payload to partner', {payload: payload}); | 130 | logger.verbose('Sending payload to partner', {payload: payload}); |
131 | req.write( payload ); | 131 | req.write( payload ); |
132 | req.end(); | 132 | req.end(); |
133 | } | 133 | } |
134 | 134 | ||
135 | function directResponseHandler(body, task) { | 135 | function directResponseHandler(body, task) { |
136 | 136 | ||
137 | logger.info('Got direct response'); | 137 | logger.info('Got direct response'); |
138 | 138 | ||
139 | xml2js(body, function (err, result) { | 139 | xml2js(body, function (err, result) { |
140 | if (err) { | 140 | if (err) { |
141 | logger.warn('Error parsing xml', {body: body}); | 141 | logger.warn('Error parsing xml', {body: body}); |
142 | callbackReport(task.requestId, '68', buffer); | 142 | callbackReport(task.requestId, '68', buffer); |
143 | return; | 143 | return; |
144 | } | 144 | } |
145 | 145 | ||
146 | logger.info('Direct response parsed', {result: result}); | 146 | logger.info('Direct response parsed', {result: result}); |
147 | 147 | ||
148 | var response_code = '68'; | 148 | var response_code = '68'; |
149 | 149 | ||
150 | var request_id = task.requestId; | 150 | var request_id = task.requestId; |
151 | var status = result.evoucher.result[0].trim(); | 151 | var status = result.evoucher.result[0].trim(); |
152 | var message = result.evoucher.value[0].string[0].trim(); | 152 | var message = result.evoucher.value[0].string[0].trim(); |
153 | 153 | ||
154 | if (message.indexOf('SUKSES') >= 0) { | 154 | if (message.indexOf('SUKSES') >= 0) { |
155 | /* | 155 | /* |
156 | var sn = getSNFromMessage(message); | 156 | var sn = getSNFromMessage(message); |
157 | message = 'SN=' + sn + '; ' + message; | 157 | message = 'SN=' + sn + '; ' + message; |
158 | */ | 158 | */ |
159 | 159 | ||
160 | response_code = '68'; | 160 | response_code = '68'; |
161 | } | 161 | } |
162 | else if (message.indexOf('GAGAL') >= 0) { | 162 | else if (message.indexOf('GAGAL') >= 0) { |
163 | response_code = '40'; | 163 | response_code = '40'; |
164 | } | 164 | } |
165 | else { | 165 | else { |
166 | response_code = '68'; | 166 | response_code = '68'; |
167 | } | 167 | } |
168 | 168 | ||
169 | callbackReport(request_id, response_code, message); | 169 | callbackReport(request_id, response_code, message); |
170 | }); | 170 | }); |
171 | } | 171 | } |
172 | 172 | ||
173 | function getSNFromMessage(message) { | 173 | function getSNFromMessage(message) { |
174 | try { | 174 | try { |
175 | var sn_match = message.match(/SN=(\w+)/); | 175 | var sn_match = message.match(/SN=(\w+)/); |
176 | return sn_match[1].trim(); | 176 | return sn_match[1].trim(); |
177 | } | 177 | } |
178 | catch(e) { | 178 | catch(e) { |
179 | return; | 179 | return; |
180 | } | 180 | } |
181 | } | 181 | } |
182 | 182 | ||
183 | function topupRequest(task, retry) { | 183 | function topupRequest(task, retry) { |
184 | var key = 'DUPCHECK.gw:' + config.globals.gateway_name + '.prod:' + task.remoteProduct + '.dest:' + task.destination + '.date:' + strftime('%Y%m%d', new Date); | 184 | var key = 'DUPCHECK.gw:' + config.globals.gateway_name + '.prod:' + task.remoteProduct + '.dest:' + task.destination + '.date:' + strftime('%Y%m%d', new Date); |
185 | redisClient.get(key, function(err, data) { | 185 | redisClient.get(key, function(err, data) { |
186 | if (err) { | 186 | if (err) { |
187 | callbackReport(task.requestId, '40', 'Gagal cek anti transaksi duplikat (redis error)'); | 187 | callbackReport(task.requestId, '40', 'Gagal cek anti transaksi duplikat (redis error)'); |
188 | return; | 188 | return; |
189 | } | 189 | } |
190 | 190 | ||
191 | if (!data) { | 191 | if (!data) { |
192 | logger.verbose('Belum ada trx dengan tujuan dan denom yang sama pada hari ini. Lanjutkan.'); | ||
192 | 193 | ||
193 | redisClient.set(key, JSON.stringify(task)); | 194 | redisClient.set(key, JSON.stringify(task)); |
194 | redisClient.expire(key, 3600 * 24 * 2); | 195 | redisClient.expire(key, 3600 * 24 * 2); |
195 | 196 | ||
197 | topupRequestHit(task, retry); | ||
198 | |||
196 | } else { | 199 | } else { |
197 | 200 | ||
198 | try { | 201 | try { |
199 | var taskOnRedis = JSON.parse(data); | 202 | var taskOnRedis = JSON.parse(data); |
200 | if (task.requestId == taskOnRedis.requestId) { | 203 | if (task.requestId == taskOnRedis.requestId) { |
204 | logger.verbose('Sudah ada trx dengan tujuan dan denom yg sama, requestId jg sama. Lanjutkan.') | ||
201 | topupRequestHit(task, retry); | 205 | topupRequestHit(task, retry); |
202 | } else { | 206 | } else { |
207 | logger.verbose('Sudah ada trx dengan tujuan dan denom yg sama, requestId tidak sama. Batalkan.') | ||
203 | callbackReport(task.requestId, '55', 'Transaksi duplikat') | 208 | callbackReport(task.requestId, '55', 'Transaksi duplikat') |
204 | } | 209 | } |
205 | } | 210 | } |
206 | catch(errJSONParse) { | 211 | catch(errJSONParse) { |
207 | callbackReport(task.requestId, '68', "error parsing json"); | 212 | callbackReport(task.requestId, '68', "error parsing json"); |
208 | } | 213 | } |
209 | } | 214 | } |
210 | 215 | ||
211 | }); | 216 | }); |
212 | } | 217 | } |
213 | 218 | ||
214 | function createRedisClient() { | 219 | function createRedisClient() { |
215 | try { | 220 | try { |
216 | redisClient = redis.createClient(config.globals.redis_port, config.globals.redis_host); | 221 | redisClient = redis.createClient(config.globals.redis_port, config.globals.redis_host); |
217 | } catch(err) { | 222 | } catch(err) { |
218 | logger.info("Error creating redis client"); | 223 | logger.info("Error creating redis client"); |
219 | } | 224 | } |
220 | } | 225 | } |
221 | 226 | ||
222 | exports.start = start; | 227 | exports.start = start; |
223 | exports.topupRequest = topupRequest; | 228 | exports.topupRequest = topupRequest; |
224 | exports.calculateSignature = calculateSignature; | 229 | exports.calculateSignature = calculateSignature; |
225 | 230 |