Commit 998960429203fdd1732c322f6057d77773cfeffb
1 parent
efdf72c3e9
Exists in
master
info harga supplier
Showing 1 changed file with 5 additions and 0 deletions Inline Diff
partner-simplepay.js
1 | "use strict"; | 1 | "use strict"; |
2 | 2 | ||
3 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; | 3 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; |
4 | 4 | ||
5 | const request = require('request'); | 5 | const request = require('request'); |
6 | const crypto = require('crypto'); | 6 | const crypto = require('crypto'); |
7 | const moment = require('moment'); | 7 | const moment = require('moment'); |
8 | 8 | ||
9 | const http = require('http'); | 9 | const http = require('http'); |
10 | http.globalAgent.maxSockets = Infinity; | 10 | http.globalAgent.maxSockets = Infinity; |
11 | 11 | ||
12 | const seconds_to_wait_before_resend_on_pending = 30; | 12 | const seconds_to_wait_before_resend_on_pending = 30; |
13 | 13 | ||
14 | var config; | 14 | var config; |
15 | var aaa; | 15 | var aaa; |
16 | var logger; | 16 | var logger; |
17 | 17 | ||
18 | function start(options) { | 18 | function start(options) { |
19 | if (!options) { | 19 | if (!options) { |
20 | console.log('Undefined options, terminating....'); | 20 | console.log('Undefined options, terminating....'); |
21 | process.exit(1); | 21 | process.exit(1); |
22 | } | 22 | } |
23 | 23 | ||
24 | if (options.config) { | 24 | if (options.config) { |
25 | config = options.config; | 25 | config = options.config; |
26 | } else { | 26 | } else { |
27 | console.log('Undefined options.config, terminating....') | 27 | console.log('Undefined options.config, terminating....') |
28 | process.exit(1); | 28 | process.exit(1); |
29 | } | 29 | } |
30 | 30 | ||
31 | if (options.aaa) { | 31 | if (options.aaa) { |
32 | aaa = options.aaa; | 32 | aaa = options.aaa; |
33 | } else { | 33 | } else { |
34 | console.log('Undefined options.aaa, terminating....') | 34 | console.log('Undefined options.aaa, terminating....') |
35 | process.exit(1); | 35 | process.exit(1); |
36 | } | 36 | } |
37 | 37 | ||
38 | if (options && options.logger) { | 38 | if (options && options.logger) { |
39 | logger = options.logger; | 39 | logger = options.logger; |
40 | } else { | 40 | } else { |
41 | console.log('Undefined options.logger, terminating....') | 41 | console.log('Undefined options.logger, terminating....') |
42 | process.exit(1); | 42 | process.exit(1); |
43 | } | 43 | } |
44 | 44 | ||
45 | createReverseHttpServer(); | 45 | createReverseHttpServer(); |
46 | } | 46 | } |
47 | 47 | ||
48 | function callbackReport(requestId, rc, message, options) { | 48 | function callbackReport(requestId, rc, message, options) { |
49 | aaa.callbackReportWithPushToMongoDb(requestId, rc, message); | 49 | aaa.callbackReportWithPushToMongoDb(requestId, rc, message); |
50 | 50 | ||
51 | // resend trx as status check if rc 68 and task is defined | 51 | // resend trx as status check if rc 68 and task is defined |
52 | if (options && options.task && (rc == '68')) { | 52 | if (options && options.task && (rc == '68')) { |
53 | logger.verbose('Got pending trx, requesting in ' + seconds_to_wait_before_resend_on_pending + ' secs'); | 53 | logger.verbose('Got pending trx, requesting in ' + seconds_to_wait_before_resend_on_pending + ' secs'); |
54 | setTimeout(function() { | 54 | setTimeout(function() { |
55 | checkStatus(options.task); | 55 | checkStatus(options.task); |
56 | }, seconds_to_wait_before_resend_on_pending * 1000) | 56 | }, seconds_to_wait_before_resend_on_pending * 1000) |
57 | } | 57 | } |
58 | } | 58 | } |
59 | 59 | ||
60 | function calculateSign(dt, trx_ref_id, cust_num, username, password) { | 60 | function calculateSign(dt, trx_ref_id, cust_num, username, password) { |
61 | const cryptoHashPassword = crypto.createHash('sha1'); | 61 | const cryptoHashPassword = crypto.createHash('sha1'); |
62 | const cryptoHashUsernamePassword = crypto.createHash('sha1'); | 62 | const cryptoHashUsernamePassword = crypto.createHash('sha1'); |
63 | const cryptoHashOutest = crypto.createHash('sha1'); | 63 | const cryptoHashOutest = crypto.createHash('sha1'); |
64 | 64 | ||
65 | cryptoHashPassword.update(password); | 65 | cryptoHashPassword.update(password); |
66 | const hashPassword = cryptoHashPassword.digest('hex'); | 66 | const hashPassword = cryptoHashPassword.digest('hex'); |
67 | 67 | ||
68 | cryptoHashUsernamePassword.update(username + hashPassword); | 68 | cryptoHashUsernamePassword.update(username + hashPassword); |
69 | const hashUsernamePassword = cryptoHashUsernamePassword.digest('hex'); | 69 | const hashUsernamePassword = cryptoHashUsernamePassword.digest('hex'); |
70 | 70 | ||
71 | cryptoHashOutest.update(dt + trx_ref_id + cust_num + hashUsernamePassword); | 71 | cryptoHashOutest.update(dt + trx_ref_id + cust_num + hashUsernamePassword); |
72 | return cryptoHashOutest.digest('hex'); | 72 | return cryptoHashOutest.digest('hex'); |
73 | } | 73 | } |
74 | 74 | ||
75 | function _decodeResponseBody(responseBody) { | 75 | function _decodeResponseBody(responseBody) { |
76 | let response; | 76 | let response; |
77 | 77 | ||
78 | try { | 78 | try { |
79 | response = JSON.parse(responseBody); | 79 | response = JSON.parse(responseBody); |
80 | } | 80 | } |
81 | catch(e) { | 81 | catch(e) { |
82 | logger.warn('Error parsing response body'); | 82 | logger.warn('Error parsing response body'); |
83 | } | 83 | } |
84 | 84 | ||
85 | return response; | 85 | return response; |
86 | } | 86 | } |
87 | 87 | ||
88 | function _composeMessageFromResponseData(responseDataObj) { | 88 | function _composeMessageFromResponseData(responseDataObj) { |
89 | const diag = _getPropertyFromObjectSafe(responseDataObj, 'diag'); | 89 | const diag = _getPropertyFromObjectSafe(responseDataObj, 'diag'); |
90 | const msg = _getPropertyFromObjectSafe(responseDataObj, 'message'); | 90 | const msg = _getPropertyFromObjectSafe(responseDataObj, 'message'); |
91 | const balance = _getPropertyFromObjectSafe(responseDataObj, 'balance'); | 91 | const balance = _getPropertyFromObjectSafe(responseDataObj, 'balance'); |
92 | const timestamp = _getPropertyFromObjectSafe(responseDataObj, 'timestamp'); | 92 | const timestamp = _getPropertyFromObjectSafe(responseDataObj, 'timestamp'); |
93 | const price = _getPropertyFromObjectSafe(response.data, 'harga'); | ||
93 | 94 | ||
94 | let messages = []; | 95 | let messages = []; |
95 | 96 | ||
96 | if (timestamp) { | 97 | if (timestamp) { |
97 | messages.push(timestamp); | 98 | messages.push(timestamp); |
98 | } | 99 | } |
99 | 100 | ||
100 | if (diag) { | 101 | if (diag) { |
101 | messages.push(diag); | 102 | messages.push(diag); |
102 | } | 103 | } |
103 | 104 | ||
104 | if (msg) { | 105 | if (msg) { |
105 | messages.push(msg); | 106 | messages.push(msg); |
106 | } | 107 | } |
107 | 108 | ||
108 | if (balance) { | 109 | if (balance) { |
109 | messages.push('Balance: ' + balance); | 110 | messages.push('Balance: ' + balance); |
110 | } | 111 | } |
111 | 112 | ||
113 | if (price) { | ||
114 | messages.push('Price: ' + price); | ||
115 | } | ||
116 | |||
112 | return messages.join('. ') + '.'; | 117 | return messages.join('. ') + '.'; |
113 | } | 118 | } |
114 | 119 | ||
115 | function _composeCompleteSn(responseDataObj) { | 120 | function _composeCompleteSn(responseDataObj) { |
116 | const serial = _getPropertyFromObjectSafe(responseDataObj, 'serial'); | 121 | const serial = _getPropertyFromObjectSafe(responseDataObj, 'serial'); |
117 | const info = _getPropertyFromObjectSafe(responseDataObj, 'info'); | 122 | const info = _getPropertyFromObjectSafe(responseDataObj, 'info'); |
118 | 123 | ||
119 | if (!info) { | 124 | if (!info) { |
120 | //logger.warn('Undefined data.info on _composeCompleteSn'); | 125 | //logger.warn('Undefined data.info on _composeCompleteSn'); |
121 | return serial; | 126 | return serial; |
122 | } | 127 | } |
123 | 128 | ||
124 | const cleanedData = { | 129 | const cleanedData = { |
125 | token: serial, | 130 | token: serial, |
126 | cust_name: _getPropertyFromObjectSafe(info, 'cust_name'), | 131 | cust_name: _getPropertyFromObjectSafe(info, 'cust_name'), |
127 | tariff: _getPropertyFromObjectSafe(info, 'kelas') + 'VA', | 132 | tariff: _getPropertyFromObjectSafe(info, 'kelas') + 'VA', |
128 | total_kwh: _getPropertyFromObjectSafe(info, 'size') | 133 | total_kwh: _getPropertyFromObjectSafe(info, 'size') |
129 | } | 134 | } |
130 | 135 | ||
131 | if (cleanedData.token) { | 136 | if (cleanedData.token) { |
132 | cleanedData.token = cleanedData.token.replace(/ /g, '-'); | 137 | cleanedData.token = cleanedData.token.replace(/ /g, '-'); |
133 | } | 138 | } |
134 | 139 | ||
135 | if (cleanedData.cust_name) { | 140 | if (cleanedData.cust_name) { |
136 | cleanedData.cust_name = cleanedData.cust_name.replace(/\W+/g, ' ').trim().replace(/\W+/g, '-').toUpperCase(); | 141 | cleanedData.cust_name = cleanedData.cust_name.replace(/\W+/g, ' ').trim().replace(/\W+/g, '-').toUpperCase(); |
137 | } | 142 | } |
138 | 143 | ||
139 | if (cleanedData.total_kwh) { | 144 | if (cleanedData.total_kwh) { |
140 | cleanedData.total_kwh = cleanedData.total_kwh.replace(/kWh /g, ''); | 145 | cleanedData.total_kwh = cleanedData.total_kwh.replace(/kWh /g, ''); |
141 | } | 146 | } |
142 | 147 | ||
143 | 148 | ||
144 | logger.verbose('Detail token info extracted', {originalResponseInfo: info, cleanedData: cleanedData}); | 149 | logger.verbose('Detail token info extracted', {originalResponseInfo: info, cleanedData: cleanedData}); |
145 | 150 | ||
146 | return [ | 151 | return [ |
147 | cleanedData.token, cleanedData.cust_name, cleanedData.tariff, cleanedData.total_kwh | 152 | cleanedData.token, cleanedData.cust_name, cleanedData.tariff, cleanedData.total_kwh |
148 | ].join('/'); | 153 | ].join('/'); |
149 | } | 154 | } |
150 | 155 | ||
151 | function _responseBodyHandler(responseBody, task) { | 156 | function _responseBodyHandler(responseBody, task) { |
152 | let rc = '68'; | 157 | let rc = '68'; |
153 | let response = _decodeResponseBody(responseBody); | 158 | let response = _decodeResponseBody(responseBody); |
154 | 159 | ||
155 | logger.verbose('RESPONSE', {response: response}); | 160 | logger.verbose('RESPONSE', {response: response}); |
156 | 161 | ||
157 | const responseStatus = _getPropertyFromObjectSafe(response, 'status'); | 162 | const responseStatus = _getPropertyFromObjectSafe(response, 'status'); |
158 | const responseInfo = _getPropertyFromObjectSafe(response, 'info'); | 163 | const responseInfo = _getPropertyFromObjectSafe(response, 'info'); |
159 | 164 | ||
160 | if (responseStatus == 'Error') { | 165 | if (responseStatus == 'Error') { |
161 | if (['insufficient balance', 'System Cut-Off'].indexOf(responseInfo) >= 0) { | 166 | if (['insufficient balance', 'System Cut-Off'].indexOf(responseInfo) >= 0) { |
162 | rc = '91'; | 167 | rc = '91'; |
163 | } | 168 | } |
164 | callbackReport(task.requestId, '91', [responseStatus, responseInfo].join(': '), {task: task}); | 169 | callbackReport(task.requestId, '91', [responseStatus, responseInfo].join(': '), {task: task}); |
165 | return; | 170 | return; |
166 | } | 171 | } |
167 | 172 | ||
168 | const requestId = _getPropertyFromObjectSafe(response.data, 'request_id'); | 173 | const requestId = _getPropertyFromObjectSafe(response.data, 'request_id'); |
169 | const trxStatus = _getPropertyFromObjectSafe(response.data, 'trx_status'); | 174 | const trxStatus = _getPropertyFromObjectSafe(response.data, 'trx_status'); |
170 | const diag = _getPropertyFromObjectSafe(response.data, 'diag'); | 175 | const diag = _getPropertyFromObjectSafe(response.data, 'diag'); |
171 | let balance = _getPropertyFromObjectSafe(response.data, 'balance'); | 176 | let balance = _getPropertyFromObjectSafe(response.data, 'balance'); |
172 | 177 | ||
173 | if (balance && aaa.updateBalance) { | 178 | if (balance && aaa.updateBalance) { |
174 | balance = balance.replace(/\D/g, ''); | 179 | balance = balance.replace(/\D/g, ''); |
175 | if (balance) { | 180 | if (balance) { |
176 | aaa.updateBalance(balance); | 181 | aaa.updateBalance(balance); |
177 | } | 182 | } |
178 | } | 183 | } |
179 | 184 | ||
180 | let aaaMessage = _composeMessageFromResponseData(response.data); | 185 | let aaaMessage = _composeMessageFromResponseData(response.data); |
181 | if (!aaaMessage) { | 186 | if (!aaaMessage) { |
182 | aaaMessage = 'Transaksi sedang diproses'; | 187 | aaaMessage = 'Transaksi sedang diproses'; |
183 | } | 188 | } |
184 | 189 | ||
185 | if (trxStatus == 'P') { | 190 | if (trxStatus == 'P') { |
186 | logger.verbose('Got pending trx response', {response: response.data}); | 191 | logger.verbose('Got pending trx response', {response: response.data}); |
187 | rc = '68'; | 192 | rc = '68'; |
188 | } | 193 | } |
189 | else if (trxStatus == 'S') { | 194 | else if (trxStatus == 'S') { |
190 | logger.verbose('Got succcess trx response', {response: response.data}); | 195 | logger.verbose('Got succcess trx response', {response: response.data}); |
191 | 196 | ||
192 | rc = '00'; | 197 | rc = '00'; |
193 | aaaMessage = 'SN=' + _composeCompleteSn(response.data) + '; ' + aaaMessage; | 198 | aaaMessage = 'SN=' + _composeCompleteSn(response.data) + '; ' + aaaMessage; |
194 | } | 199 | } |
195 | else if (trxStatus == 'R') { | 200 | else if (trxStatus == 'R') { |
196 | logger.verbose('Got rejected trx response', {response: response.data}); | 201 | logger.verbose('Got rejected trx response', {response: response.data}); |
197 | 202 | ||
198 | const partnerRC = getPartnerRCFromDiagMessage(diag); | 203 | const partnerRC = getPartnerRCFromDiagMessage(diag); |
199 | if (partnerRC == '15') { | 204 | if (partnerRC == '15') { |
200 | rc = '14'; | 205 | rc = '14'; |
201 | } | 206 | } |
202 | else { | 207 | else { |
203 | rc = '40'; | 208 | rc = '40'; |
204 | } | 209 | } |
205 | } | 210 | } |
206 | 211 | ||
207 | callbackReport(requestId, rc, aaaMessage, {task: task}); | 212 | callbackReport(requestId, rc, aaaMessage, {task: task}); |
208 | } | 213 | } |
209 | 214 | ||
210 | function getPartnerRCFromDiagMessage(diag) { | 215 | function getPartnerRCFromDiagMessage(diag) { |
211 | let matches = diag.match(/^\s*\[(.*)\]/); | 216 | let matches = diag.match(/^\s*\[(.*)\]/); |
212 | if (!matches || matches.length < 2) { | 217 | if (!matches || matches.length < 2) { |
213 | return; | 218 | return; |
214 | } | 219 | } |
215 | 220 | ||
216 | return matches[1]; | 221 | return matches[1]; |
217 | } | 222 | } |
218 | 223 | ||
219 | function _hitTopup(task, isCheckStatus) { | 224 | function _hitTopup(task, isCheckStatus) { |
220 | 225 | ||
221 | const dt = moment().format('YYYY-MM-DD HH:mm:ss'); | 226 | const dt = moment().format('YYYY-MM-DD HH:mm:ss'); |
222 | const username = config.h2h_out.username || config.h2h_out.userid; | 227 | const username = config.h2h_out.username || config.h2h_out.userid; |
223 | const password = config.h2h_out.password || config.h2h_out.pin; | 228 | const password = config.h2h_out.password || config.h2h_out.pin; |
224 | const sign = calculateSign(dt, task.requestId, task.destination, username, password); | 229 | const sign = calculateSign(dt, task.requestId, task.destination, username, password); |
225 | 230 | ||
226 | logger.verbose('Sign for ' + dt + ', ' + task.requestId + ', ' + task.destination + ', ' + username + ', ' + password + ' is ' + sign); | 231 | logger.verbose('Sign for ' + dt + ', ' + task.requestId + ', ' + task.destination + ', ' + username + ', ' + password + ' is ' + sign); |
227 | const requestOptions = { | 232 | const requestOptions = { |
228 | url: config.h2h_out.partner, | 233 | url: config.h2h_out.partner, |
229 | form: { | 234 | form: { |
230 | username: username, | 235 | username: username, |
231 | datetime: dt, | 236 | datetime: dt, |
232 | code: task.remoteProduct, | 237 | code: task.remoteProduct, |
233 | trx_ref_id: task.requestId, | 238 | trx_ref_id: task.requestId, |
234 | cust_num: task.destination, | 239 | cust_num: task.destination, |
235 | sign: sign | 240 | sign: sign |
236 | } | 241 | } |
237 | } | 242 | } |
238 | 243 | ||
239 | logger.verbose('Requesting to partner', {requestOptions: requestOptions}); | 244 | logger.verbose('Requesting to partner', {requestOptions: requestOptions}); |
240 | 245 | ||
241 | request.post(requestOptions, function(error, response, body) { | 246 | request.post(requestOptions, function(error, response, body) { |
242 | if (error) { | 247 | if (error) { |
243 | let rc = '68'; | 248 | let rc = '68'; |
244 | 249 | ||
245 | if (!isCheckStatus && (error.syscall == 'connect')) { | 250 | if (!isCheckStatus && (error.syscall == 'connect')) { |
246 | rc = '91'; | 251 | rc = '91'; |
247 | } | 252 | } |
248 | 253 | ||
249 | logger.warn('Error requesting to partner', {task: task, rc: rc, error: error, isCheckStatus: isCheckStatus}); | 254 | logger.warn('Error requesting to partner', {task: task, rc: rc, error: error, isCheckStatus: isCheckStatus}); |
250 | callbackReport(task.requestId, rc, 'Error requesting to partner. ' + error, {task: task}); | 255 | callbackReport(task.requestId, rc, 'Error requesting to partner. ' + error, {task: task}); |
251 | return; | 256 | return; |
252 | } | 257 | } |
253 | 258 | ||
254 | if (response.statusCode != 200) { | 259 | if (response.statusCode != 200) { |
255 | let rc = '68'; | 260 | let rc = '68'; |
256 | 261 | ||
257 | logger.warn('HTTP status code is not 200', {task: task, http_status_code: response.statusCode, isCheckStatus: isCheckStatus}); | 262 | logger.warn('HTTP status code is not 200', {task: task, http_status_code: response.statusCode, isCheckStatus: isCheckStatus}); |
258 | callbackReport(task.requestId, rc, 'HTTP status code ' + response.statusCode, {task: task}); | 263 | callbackReport(task.requestId, rc, 'HTTP status code ' + response.statusCode, {task: task}); |
259 | return; | 264 | return; |
260 | } | 265 | } |
261 | 266 | ||
262 | logger.info('Transaksi sedang diproses', {task: task, response_body: body}); | 267 | logger.info('Transaksi sedang diproses', {task: task, response_body: body}); |
263 | 268 | ||
264 | _responseBodyHandler(body, task); | 269 | _responseBodyHandler(body, task); |
265 | 270 | ||
266 | }) | 271 | }) |
267 | } | 272 | } |
268 | 273 | ||
269 | function _getPropertyFromObjectSafe(obj, property) { | 274 | function _getPropertyFromObjectSafe(obj, property) { |
270 | let retval; | 275 | let retval; |
271 | 276 | ||
272 | if (!obj) { | 277 | if (!obj) { |
273 | logger.warn('Invalid object') | 278 | logger.warn('Invalid object') |
274 | return; | 279 | return; |
275 | } | 280 | } |
276 | 281 | ||
277 | try { | 282 | try { |
278 | retval = obj[property]; | 283 | retval = obj[property]; |
279 | } | 284 | } |
280 | catch(e) { | 285 | catch(e) { |
281 | logger.warn('Error getting ' + property + ' from object'); | 286 | logger.warn('Error getting ' + property + ' from object'); |
282 | } | 287 | } |
283 | 288 | ||
284 | return retval; | 289 | return retval; |
285 | } | 290 | } |
286 | 291 | ||
287 | function topupRequest(task) { | 292 | function topupRequest(task) { |
288 | aaa.insertTaskToMongoDb(task); | 293 | aaa.insertTaskToMongoDb(task); |
289 | _hitTopup(task); | 294 | _hitTopup(task); |
290 | } | 295 | } |
291 | 296 | ||
292 | function checkStatus(task) { | 297 | function checkStatus(task) { |
293 | _hitTopup(task, true); | 298 | _hitTopup(task, true); |
294 | } | 299 | } |
295 | 300 | ||
296 | 301 | ||
297 | function reverseReportHandler(body) { | 302 | function reverseReportHandler(body) { |
298 | logger.info('Got reverse report', {body: body}); | 303 | logger.info('Got reverse report', {body: body}); |
299 | } | 304 | } |
300 | 305 | ||
301 | function createReverseHttpServer() { | 306 | function createReverseHttpServer() { |
302 | var httpServer = http.createServer(function(request, response) { | 307 | var httpServer = http.createServer(function(request, response) { |
303 | 308 | ||
304 | logger.info('Got request from partner'); | 309 | logger.info('Got request from partner'); |
305 | 310 | ||
306 | var body = ""; | 311 | var body = ""; |
307 | request.on('data', function (chunk) { | 312 | request.on('data', function (chunk) { |
308 | body += chunk; | 313 | body += chunk; |
309 | }); | 314 | }); |
310 | 315 | ||
311 | request.on('end', function () { | 316 | request.on('end', function () { |
312 | response.writeHead(200); | 317 | response.writeHead(200); |
313 | response.end('OK'); | 318 | response.end('OK'); |
314 | 319 | ||
315 | reverseReportHandler(body); | 320 | reverseReportHandler(body); |
316 | }); | 321 | }); |
317 | 322 | ||
318 | }); | 323 | }); |
319 | 324 | ||
320 | httpServer.listen(config.h2h_out.listen_port, function() { | 325 | httpServer.listen(config.h2h_out.listen_port, function() { |
321 | logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port); | 326 | logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port); |
322 | }); | 327 | }); |
323 | } | 328 | } |
324 | 329 | ||
325 | 330 | ||
326 | exports.calculateSign = calculateSign; | 331 | exports.calculateSign = calculateSign; |
327 | exports.start = start; | 332 | exports.start = start; |
328 | exports.topupRequest = topupRequest; | 333 | exports.topupRequest = topupRequest; |
329 | exports.checkStatus = checkStatus; | 334 | exports.checkStatus = checkStatus; |
330 | 335 |