Commit 52fa01ba174d5c1d5f4c6624d58907348ea12b01
1 parent
dff0c67ee7
Exists in
master
alpha
Showing 4 changed files with 309 additions and 1 deletions Side-by-side Diff
index.js
... | ... | @@ -0,0 +1,11 @@ |
1 | +"use strict"; | |
2 | +process.chdir(__dirname); | |
3 | + | |
4 | +const config = require('komodo-sdk/config'); | |
5 | +const logger = require('komodo-sdk/logger'); | |
6 | +const matrix = require('komodo-sdk/matrix'); | |
7 | + | |
8 | +const pullgw = require('komodo-sdk/gateway/pull'); | |
9 | +const partner = require('./partner'); | |
10 | + | |
11 | +pullgw.setPartner(partner); |
package.json
... | ... | @@ -18,5 +18,10 @@ |
18 | 18 | "simplepay" |
19 | 19 | ], |
20 | 20 | "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", |
21 | - "license": "ISC" | |
21 | + "license": "ISC", | |
22 | + "dependencies": { | |
23 | + "komodo-sdk": "git+http://gitlab.kodesumber.com/komodo/komodo-sdk.git", | |
24 | + "moment": "^2.18.1", | |
25 | + "request": "^2.81.0" | |
26 | + } | |
22 | 27 | } |
partner-misc.js
... | ... | @@ -0,0 +1,209 @@ |
1 | +"use strict"; | |
2 | + | |
3 | +const crypto = require('crypto'); | |
4 | +const moment = require('moment'); | |
5 | + | |
6 | +const config = require('komodo-sdk/config'); | |
7 | +const logger = require('komodo-sdk/logger'); | |
8 | +const pull = require('komodo-sdk/gateway/pull'); | |
9 | + | |
10 | +const partner = require('./partner'); | |
11 | + | |
12 | +function calculateSign(dt, trx_ref_id, cust_num, username, password) { | |
13 | + const cryptoHashPassword = crypto.createHash('sha1'); | |
14 | + const cryptoHashUsernamePassword = crypto.createHash('sha1'); | |
15 | + const cryptoHashOutest = crypto.createHash('sha1'); | |
16 | + | |
17 | + cryptoHashPassword.update(password); | |
18 | + const hashPassword = cryptoHashPassword.digest('hex'); | |
19 | + | |
20 | + cryptoHashUsernamePassword.update(username + hashPassword); | |
21 | + const hashUsernamePassword = cryptoHashUsernamePassword.digest('hex'); | |
22 | + | |
23 | + cryptoHashOutest.update(dt + trx_ref_id + cust_num + hashUsernamePassword); | |
24 | + return cryptoHashOutest.digest('hex'); | |
25 | +} | |
26 | + | |
27 | +function createRequestOptions(task, config) { | |
28 | + const dt = moment().format('YYYY-MM-DD HH:mm:ss'); | |
29 | + const sign = calculateSign(dt, task.trx_id, task.destination, config.partner.username, config.partner.password); | |
30 | + | |
31 | + return = { | |
32 | + url: config.partner.url, | |
33 | + form: { | |
34 | + username: config.partner.username, | |
35 | + datetime: dt, | |
36 | + code: task.remote_product, | |
37 | + trx_ref_id: task.trx_id, | |
38 | + cust_num: task.destination, | |
39 | + sign: sign | |
40 | + } | |
41 | + } | |
42 | +} | |
43 | + | |
44 | +function decodeResponseBody(responseBody) { | |
45 | + let response; | |
46 | + | |
47 | + try { | |
48 | + response = JSON.parse(responseBody) | |
49 | + } | |
50 | + catch(e) { | |
51 | + logger.warn('Error parsing response body'); | |
52 | + } | |
53 | + | |
54 | + return response; | |
55 | +} | |
56 | + | |
57 | +function cleanBalance(balance) { | |
58 | + try { | |
59 | + balance = balance.replace(/\D/g, ''); | |
60 | + } | |
61 | + catch(e) { }; | |
62 | + | |
63 | + return balance; | |
64 | + | |
65 | +} | |
66 | + | |
67 | +function processPartnerResponseBody(body, task) { | |
68 | + let response = decodeResponseBody(responseBody); | |
69 | + let messages = []; | |
70 | + | |
71 | + if (!response) { | |
72 | + return; | |
73 | + } | |
74 | + | |
75 | + logger.verbose('RESPONSE', {response: response}); | |
76 | + | |
77 | + const responseStatus = response.status; | |
78 | + const responseInfo = response.info; | |
79 | + | |
80 | + let taskTrxId; | |
81 | + if (task && task.trx_id) { | |
82 | + taskTrxId = task.trx_id; | |
83 | + } | |
84 | + | |
85 | + let responseRequestId; | |
86 | + if (response.data && response.data.request_id) { | |
87 | + responseRequestId = response.data.request_id; | |
88 | + } | |
89 | + | |
90 | + | |
91 | + let responseTrxStatus; | |
92 | + if (response.data && response.data.trx_status) { | |
93 | + responseTrxStatus = response.data.trx_status; | |
94 | + } | |
95 | + | |
96 | + let responseTimestamp; | |
97 | + if (response.data && response.data.timestamp) { | |
98 | + responseTimestamp = response.data.timestamp; | |
99 | + messages.push(responseTimestamp); | |
100 | + } | |
101 | + | |
102 | + let responseDiag; | |
103 | + if (response.data && response.data.diag) { | |
104 | + responseDiag = response.data.diag; | |
105 | + messages.push(responseDiag); | |
106 | + } | |
107 | + | |
108 | + let responseMessage; | |
109 | + if (response.data && response.data.message) { | |
110 | + responseMessage = response.data.message; | |
111 | + messages.push(responseMessage); | |
112 | + } | |
113 | + | |
114 | + let responseBalance; | |
115 | + if (response.data && response.data.balance) { | |
116 | + responseBalance = response.data.balance; | |
117 | + messages.push('Balance: ' + responseBalance); | |
118 | + if (responseBalance) { | |
119 | + responseBalance = cleanBalance(responseBalance); | |
120 | + } | |
121 | + } | |
122 | + | |
123 | + if (messages.length <= 0) { messages.push('Transaksi anda sedang diproses'); } | |
124 | + | |
125 | + let coreReportData = { | |
126 | + trx_id: taskTrxId || responseRequestId, | |
127 | + rc: '68', | |
128 | + message: messages.join('. ') + '.', | |
129 | + sn: '', | |
130 | + handler: config.handler_name, | |
131 | + balance: responseBalance, | |
132 | + core_task: task, | |
133 | + raw: body | |
134 | + } | |
135 | + | |
136 | + if (responseStatus == 'Error') { | |
137 | + if (responseInfo == 'insufficient balance') { | |
138 | + coreReportData.rc = '91'; | |
139 | + } | |
140 | + | |
141 | + coreReportData.message = [responseStatus, responseInfo].join(': '); | |
142 | + | |
143 | + partner.reportToCore(coreReportData); | |
144 | + return; | |
145 | + } | |
146 | + | |
147 | + | |
148 | + if (responseTrxStatus == 'P') { | |
149 | + logger.verbose('Got pending trx response', {response: response.data}); | |
150 | + coreReportData.rc = '68'; | |
151 | + } | |
152 | + else if (responseTrxStatus == 'S') { | |
153 | + logger.verbose('Got succcess trx response', {response: response.data}); | |
154 | + | |
155 | + coreReportData.rc = '00' | |
156 | + | |
157 | + coreReportData.sn = composeSn(response); | |
158 | + coreReportData.message += ' SN=' + coreReportData.sn + '.'; | |
159 | + } | |
160 | + else if (trxStatus == 'R') { | |
161 | + logger.verbose('Got rejected trx response', {response: response.data}); | |
162 | + | |
163 | + const partnerRC = getPartnerRCFromDiagMessage(responseDiag); | |
164 | + if (partnerRC == '15') { | |
165 | + coreReportData.rc = '14'; | |
166 | + } | |
167 | + else { | |
168 | + coreReportData.rc = '40'; | |
169 | + } | |
170 | + } | |
171 | + | |
172 | + partner.reportToCore(coreReportData); | |
173 | +} | |
174 | + | |
175 | +function composeSn(response) { | |
176 | + if (!response && !response.data) { return; } | |
177 | + | |
178 | + if (!response.data.info) { | |
179 | + return response.data.serial; | |
180 | + } | |
181 | + | |
182 | + let token = response.data.serial; | |
183 | + let cust_name = response.data.info.cust_name; | |
184 | + let tariff = response.data.info.kelas; | |
185 | + let total_kwh = response.data.info.size; | |
186 | + | |
187 | + if (tariff.indexOf('VA') < 0) { | |
188 | + tariff += 'VA'; | |
189 | + } | |
190 | + | |
191 | + if (cust_name) { | |
192 | + cust_name = cust_name.replace(/\W+/g, ' ').trim().replace(/\W+/g, '-').toUpperCase(); | |
193 | + } | |
194 | + | |
195 | + return [ token, cust_name, tariff, total_kwh ].join('/'); | |
196 | +} | |
197 | + | |
198 | +function getPartnerRCFromDiagMessage(diag) { | |
199 | + let matches = diag.match(/^\s*\[(.*)\]/); | |
200 | + if (!matches || matches.length < 2) { | |
201 | + return; | |
202 | + } | |
203 | + | |
204 | + return matches[1]; | |
205 | +} | |
206 | + | |
207 | + | |
208 | +exports.calculateSign = calculateSign; | |
209 | +exports.createRequestOptions = createRequestOptions; |
partner.js
... | ... | @@ -0,0 +1,83 @@ |
1 | +"use strict"; | |
2 | + | |
3 | +const config = require('komodo-sdk/config'); | |
4 | +const logger = require('komodo-sdk/logger'); | |
5 | +const matrix = require('komodo-sdk/matrix'); | |
6 | +const pull = require('komodo-sdk/gateway/pull'); | |
7 | + | |
8 | +const misc = require('./partner-misc'); | |
9 | + | |
10 | +/** | |
11 | + * Pembelian ke partner | |
12 | + * | |
13 | + * Merupakan fungsi mandatory yang harus dimiliki oleh setiap gateway. | |
14 | + */ | |
15 | +function buy(task) { | |
16 | + _requestToPartner(task, false); | |
17 | +} | |
18 | + | |
19 | +/** | |
20 | + * Pemeriksaan status transaksi ke partner | |
21 | + * Merupakan fungsi mandatory yang harus dimiliki oleh setiap gateway. | |
22 | + */ | |
23 | +function statusCheck(task) { | |
24 | + _requestToPartner(task, true); | |
25 | +} | |
26 | + | |
27 | +/** | |
28 | + * Mengirim request ke partner. | |
29 | + * | |
30 | + * Untuk digunakan oleh buy dan checkStatus. | |
31 | + */ | |
32 | +function _requestToPartner(task, pendingOnError) { | |
33 | + | |
34 | + const requestOptions = misc.createRequestOptions(task, config); | |
35 | + | |
36 | + logger.verbose('Requeting to partner', {requestOptions: requestOptions}); | |
37 | + request.post(requestOptions, function(err, res, body) { | |
38 | + | |
39 | + if (err) { | |
40 | + | |
41 | + let rc = '68'; | |
42 | + if (!pendingOnError) { rc = '91'; } | |
43 | + | |
44 | + logger.warn('Error requesting to partner', {task: task, err: err}) | |
45 | + | |
46 | + _reportToCore({ | |
47 | + trx_id: task.trx_id, | |
48 | + rc: rc, | |
49 | + message: 'Error requesting to partner: ' + err, | |
50 | + handler: config.handler_name | |
51 | + }) | |
52 | + | |
53 | + return; | |
54 | + } | |
55 | + | |
56 | + if (res.statusCode != 200) { | |
57 | + let rc = '68'; | |
58 | + logger.warn('Partner returning non 200 HTTP STATUS CODE', {task: task, http_status_code: res.statusCode}) | |
59 | + | |
60 | + _reportToCore({ | |
61 | + trx_id: task.trx_id, | |
62 | + rc: rc, | |
63 | + message: 'Partner returning HTTP STATUS CODE ' + res.statusCode + ' instead of 200'; | |
64 | + handler: config.handler_name | |
65 | + }) | |
66 | + | |
67 | + return; | |
68 | + } | |
69 | + | |
70 | + misc.processPartnerResponseBody(body, task); | |
71 | + }) | |
72 | +} | |
73 | + | |
74 | +/** | |
75 | + * Mengirim report hasil transaksi ke CORE | |
76 | + */ | |
77 | +function reportToCore(data) { | |
78 | + pull.report(data); | |
79 | +} | |
80 | + | |
81 | +exports.buy = buy; | |
82 | +exports.statusCheck = statusCheck; | |
83 | +exports.reportToCore = reportToCore; |