Commit 52fa01ba174d5c1d5f4c6624d58907348ea12b01

Authored by Adhidarma Hadiwinoto
1 parent dff0c67ee7
Exists in master

alpha

Showing 4 changed files with 309 additions and 1 deletions Inline Diff

File was created 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);
12
1 { 1 {
2 "name": "komodo-to-simplepay-pln", 2 "name": "komodo-to-simplepay-pln",
3 "version": "1.0.0", 3 "version": "1.0.0",
4 "description": "KOMODO Gateway to SIMPLEPAY - PLN PREPAID", 4 "description": "KOMODO Gateway to SIMPLEPAY - PLN PREPAID",
5 "main": "index.js", 5 "main": "index.js",
6 "scripts": { 6 "scripts": {
7 "test": "mocha" 7 "test": "mocha"
8 }, 8 },
9 "repository": { 9 "repository": {
10 "type": "git", 10 "type": "git",
11 "url": "git@gitlab.kodesumber.com:komodo/komodo-to-simplepay-pln-prepaid.git" 11 "url": "git@gitlab.kodesumber.com:komodo/komodo-to-simplepay-pln-prepaid.git"
12 }, 12 },
13 "keywords": [ 13 "keywords": [
14 "komodo", 14 "komodo",
15 "ppob", 15 "ppob",
16 "payment", 16 "payment",
17 "pln", 17 "pln",
18 "simplepay" 18 "simplepay"
19 ], 19 ],
20 "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", 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 }
23 28
File was created 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;
210
File was created 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;
84