Commit 8c258ccd9821fd7f7081bf1b3b49665dbd6e4e1f
1 parent
f02e2a18b2
Exists in
master
advice prepaid
Showing 6 changed files with 299 additions and 142 deletions Side-by-side Diff
lib/hit/axios-error-is-safe.js
... | ... | @@ -0,0 +1,13 @@ |
1 | +const safeErrorCodes = [ | |
2 | + 'EHOSTUNREACH', | |
3 | + 'ENOTFOUND', | |
4 | + 'ECONNREFUSED', | |
5 | + 'ETIMEDOUT', // timeout on connecting | |
6 | + | |
7 | + // Berikut adalah error code yang tidak aman untuk dianggap gagal | |
8 | + | |
9 | + // terjadi karena timeout setelah konek (akibat pengaturan timeout pada request axios) | |
10 | + // 'ECONNABORTED', | |
11 | +]; | |
12 | + | |
13 | +module.exports = (e) => safeErrorCodes.indexOf(e.code) >= 0; |
lib/hit/dump-req-res.js
... | ... | @@ -22,7 +22,7 @@ if (!fs.existsSync(requestDumpDir)) { |
22 | 22 | } |
23 | 23 | |
24 | 24 | module.exports = async ( |
25 | - xid, task, httpMethod, endpointUrl, params, responseBody, responseStatus, | |
25 | + xid, task, httpMethod, endpointUrl, params, responseBody, responseStatus, isAdvice, | |
26 | 26 | ) => { |
27 | 27 | if ( |
28 | 28 | !config |
... | ... | @@ -40,6 +40,7 @@ DATE: ${moment().format('YYYY-MM-DD HH:mm:ss.SSS')} |
40 | 40 | TASK: |
41 | 41 | ${JSON.stringify(task, null, 2)} |
42 | 42 | |
43 | +IS-ADVICE: !!${isAdvice} | |
43 | 44 | |
44 | 45 | HTTP-METHOD: ${httpMethod} |
45 | 46 | URL: ${endpointUrl} |
... | ... | @@ -59,6 +60,7 @@ ${ |
59 | 60 | || JSON.stringify(responseBody, null, 2) |
60 | 61 | ) |
61 | 62 | } |
63 | + | |
62 | 64 | `; |
63 | 65 | |
64 | 66 | const dumpDir = path.join( |
... | ... | @@ -72,7 +74,7 @@ ${ |
72 | 74 | await fsPromise.mkdir(dumpDir, { recursive: true }); |
73 | 75 | } |
74 | 76 | |
75 | - await fsPromise.writeFile( | |
77 | + await fsPromise.appendFile( | |
76 | 78 | path.join( |
77 | 79 | dumpDir, |
78 | 80 | `trx_${task.trx_id}`, |
lib/hit/prepaid-advice.js
... | ... | @@ -0,0 +1,140 @@ |
1 | +const MODULE_NAME = 'HIT.PREPAID-ADVICE'; | |
2 | + | |
3 | +const axios = require('axios').default; | |
4 | +const urljoin = require('url-join'); | |
5 | +const uniqid = require('uniqid'); | |
6 | + | |
7 | +const config = require('komodo-sdk/config'); | |
8 | +const logger = require('komodo-sdk/logger'); | |
9 | + | |
10 | +const report = require('../report/prepaid'); | |
11 | +const translateRc = require('../translate-rc'); | |
12 | +const dumpReqRes = require('./dump-req-res'); | |
13 | + | |
14 | +module.exports = async (task) => { | |
15 | + const xid = uniqid(); | |
16 | + | |
17 | + logger.verbose(`${MODULE_NAME} 90350EF7: Processing task`, { | |
18 | + xid, | |
19 | + task, | |
20 | + }); | |
21 | + | |
22 | + const params = { | |
23 | + request_id: task.trx_id, | |
24 | + terminal_name: config.partner.terminal_name, | |
25 | + password: config.partner.password, | |
26 | + }; | |
27 | + | |
28 | + const endpointUrl = urljoin(config.partner.url, '/trx-status'); | |
29 | + let lastResponse = null; | |
30 | + | |
31 | + try { | |
32 | + logger.verbose(`${MODULE_NAME} 453A48DC: Going to HIT ADVICE endpoint`, { | |
33 | + xid, | |
34 | + endpointUrl, | |
35 | + params, | |
36 | + }); | |
37 | + | |
38 | + const response = await axios.get(endpointUrl, { | |
39 | + headers: { | |
40 | + 'User-Agent': 'KOMODO-GW-HTTPGETX', | |
41 | + }, | |
42 | + timeout: config.partner.hit_timeout_ms || 10 * 1000, | |
43 | + params, | |
44 | + }); | |
45 | + | |
46 | + if (!response) { | |
47 | + const e = new Error(`${MODULE_NAME} FD20A1AF: Empty response`); | |
48 | + e.rc = '68'; | |
49 | + e.response = response; | |
50 | + throw e; | |
51 | + } | |
52 | + | |
53 | + if (!response.data) { | |
54 | + const e = new Error(`${MODULE_NAME} 17FF8971: Empty response data`); | |
55 | + e.rc = '68'; | |
56 | + e.response = response; | |
57 | + throw e; | |
58 | + } | |
59 | + | |
60 | + if (typeof response.data !== 'object') { | |
61 | + const e = new Error(`${MODULE_NAME} A6761E9F: Response data is not a JSON`); | |
62 | + e.rc = '68'; | |
63 | + e.response = response; | |
64 | + throw e; | |
65 | + } | |
66 | + | |
67 | + lastResponse = response; | |
68 | + | |
69 | + logger.verbose(`${MODULE_NAME} 3B5C70C4: Got a direct response`, { | |
70 | + xid, | |
71 | + responseBody: response.data, | |
72 | + }); | |
73 | + | |
74 | + if (!response.data.trx_found || !response.data.trx) { | |
75 | + report(xid, { | |
76 | + trx_id: task.trx_id, | |
77 | + rc: '40', | |
78 | + message: { | |
79 | + xid, | |
80 | + 'ADVICE-DIRECT-RESPONSE': response.data, | |
81 | + }, | |
82 | + }); | |
83 | + | |
84 | + return; | |
85 | + } | |
86 | + | |
87 | + report(xid, { | |
88 | + trx_id: task.trx_id, | |
89 | + rc: response.data.trx.rc ? translateRc[response.data.trx.rc] || response.data.trx.rc | |
90 | + : '68', | |
91 | + sn: response.data.trx.sn || null, | |
92 | + amount: Number(response.data.trx.amount) || undefined, | |
93 | + balance: Number(response.data.trx.ending_balance) || undefined, | |
94 | + message: { | |
95 | + xid, | |
96 | + 'ADVICE-DIRECT-RESPONSE': response.data, | |
97 | + }, | |
98 | + }); | |
99 | + } catch (e) { | |
100 | + const rc = e.rc || '68'; | |
101 | + | |
102 | + logger.warn(`${MODULE_NAME} 1427175A: Exception`, { | |
103 | + xid, | |
104 | + eCode: e.code, | |
105 | + eMessage: e.message, | |
106 | + eRc: e.rc, | |
107 | + rc, | |
108 | + responseHttpStatus: e.response && e.response.status, | |
109 | + responseBody: e.response && e.response.data, | |
110 | + }); | |
111 | + | |
112 | + lastResponse = e.response; | |
113 | + | |
114 | + report(xid, { | |
115 | + trx_id: task.trx_id, | |
116 | + rc, | |
117 | + message: { | |
118 | + xid, | |
119 | + 'KOMODO-GW-ADVICE-ERROR': { | |
120 | + eCode: e.code, | |
121 | + eMessage: e.message, | |
122 | + responseHttpStatus: e.response && e.response.status, | |
123 | + responseBody: e.response && e.response.data, | |
124 | + }, | |
125 | + }, | |
126 | + }); | |
127 | + } finally { | |
128 | + dumpReqRes( | |
129 | + xid, | |
130 | + task, | |
131 | + 'GET', | |
132 | + endpointUrl, | |
133 | + params, | |
134 | + lastResponse && lastResponse.data, | |
135 | + lastResponse && lastResponse.status, | |
136 | + lastResponse, | |
137 | + true, | |
138 | + ); | |
139 | + } | |
140 | +}; |
lib/hit/prepaid-topup.js
... | ... | @@ -0,0 +1,138 @@ |
1 | +const MODULE_NAME = 'HIT.PREPAID-TOPUP'; | |
2 | + | |
3 | +const axios = require('axios').default; | |
4 | +const urljoin = require('url-join'); | |
5 | +const uniqid = require('uniqid'); | |
6 | + | |
7 | +const config = require('komodo-sdk/config'); | |
8 | +const logger = require('komodo-sdk/logger'); | |
9 | + | |
10 | +const report = require('../report/prepaid'); | |
11 | +const translateRc = require('../translate-rc'); | |
12 | +const composeCallbackUrl = require('./compose-callback-url'); | |
13 | +const dumpReqRes = require('./dump-req-res'); | |
14 | +const axiosErrorIsSafe = require('./axios-error-is-safe'); | |
15 | + | |
16 | +module.exports = async (task) => { | |
17 | + const xid = uniqid(); | |
18 | + | |
19 | + logger.verbose(`${MODULE_NAME} 2272F01F: Processing task`, { | |
20 | + xid, | |
21 | + task, | |
22 | + }); | |
23 | + | |
24 | + const params = { | |
25 | + request_id: task.trx_id, | |
26 | + terminal_name: config.partner.terminal_name, | |
27 | + password: config.partner.password, | |
28 | + destination: task.destination, | |
29 | + product_name: task.remote_product, | |
30 | + reverse_url: composeCallbackUrl(xid, false), | |
31 | + }; | |
32 | + | |
33 | + // const endpointUrl = isAdvice | |
34 | + // ? urljoin(config.partner.url, '/trx-status') | |
35 | + // : urljoin(config.partner.url, '/topup'); | |
36 | + | |
37 | + const endpointUrl = urljoin(config.partner.url, '/topup'); | |
38 | + let lastResponse = null; | |
39 | + | |
40 | + try { | |
41 | + logger.verbose(`${MODULE_NAME} 4AAD4F99: Going to HIT prepaid endpoint`, { | |
42 | + xid, | |
43 | + endpointUrl, | |
44 | + params, | |
45 | + }); | |
46 | + | |
47 | + const response = await axios.get(endpointUrl, { | |
48 | + headers: { | |
49 | + 'User-Agent': 'KOMODO-GW-HTTPGETX', | |
50 | + }, | |
51 | + timeout: config.partner.hit_timeout_ms || 60 * 1000, | |
52 | + params, | |
53 | + }); | |
54 | + | |
55 | + if (!response) { | |
56 | + const e = new Error(`${MODULE_NAME} 8CF4E04D: Empty response`); | |
57 | + e.rc = '68'; | |
58 | + e.response = response; | |
59 | + throw e; | |
60 | + } | |
61 | + | |
62 | + if (!response.data) { | |
63 | + const e = new Error(`${MODULE_NAME} E72B5A53: Empty response data`); | |
64 | + e.rc = '68'; | |
65 | + e.response = response; | |
66 | + throw e; | |
67 | + } | |
68 | + | |
69 | + if (typeof response.data !== 'object') { | |
70 | + const e = new Error(`${MODULE_NAME} 507680AB: Response data is not a JSON`); | |
71 | + e.rc = '68'; | |
72 | + e.response = response; | |
73 | + throw e; | |
74 | + } | |
75 | + | |
76 | + lastResponse = response; | |
77 | + | |
78 | + logger.verbose(`${MODULE_NAME} E51AFBBA: Got a direct response`, { | |
79 | + xid, | |
80 | + responseBody: response.data, | |
81 | + }); | |
82 | + | |
83 | + report(xid, { | |
84 | + trx_id: task.trx_id, | |
85 | + rc: response.data.rc ? translateRc[response.data.rc] || response.data.rc | |
86 | + : '68', | |
87 | + sn: response.data.sn || null, | |
88 | + amount: Number(response.data.amount) || undefined, | |
89 | + balance: Number(response.data.ending_balance) || undefined, | |
90 | + message: { | |
91 | + xid, | |
92 | + 'DIRECT-RESPONSE': response.data, | |
93 | + }, | |
94 | + }); | |
95 | + } catch (e) { | |
96 | + const rc = e.rc | |
97 | + || (axiosErrorIsSafe(e) && '91') | |
98 | + || '68'; | |
99 | + | |
100 | + logger.warn(`${MODULE_NAME} 8E8E49F5: Exception`, { | |
101 | + xid, | |
102 | + eCode: e.code, | |
103 | + eMessage: e.message, | |
104 | + eRc: e.rc, | |
105 | + rc, | |
106 | + responseHttpStatus: e.response && e.response.status, | |
107 | + responseBody: e.response && e.response.data, | |
108 | + }); | |
109 | + | |
110 | + lastResponse = e.response; | |
111 | + | |
112 | + report(xid, { | |
113 | + trx_id: task.trx_id, | |
114 | + rc, | |
115 | + message: { | |
116 | + xid, | |
117 | + 'KOMODO-GW-ERROR': { | |
118 | + eCode: e.code, | |
119 | + eMessage: e.message, | |
120 | + responseHttpStatus: e.response && e.response.status, | |
121 | + responseBody: e.response && e.response.data, | |
122 | + }, | |
123 | + }, | |
124 | + }); | |
125 | + } finally { | |
126 | + dumpReqRes( | |
127 | + xid, | |
128 | + task, | |
129 | + 'GET', | |
130 | + endpointUrl, | |
131 | + params, | |
132 | + lastResponse && lastResponse.data, | |
133 | + lastResponse && lastResponse.status, | |
134 | + lastResponse, | |
135 | + false, | |
136 | + ); | |
137 | + } | |
138 | +}; |
lib/hit/prepaid.js
... | ... | @@ -1,137 +0,0 @@ |
1 | -const MODULE_NAME = 'HIT.PREPAID'; | |
2 | - | |
3 | -const axios = require('axios').default; | |
4 | -const urljoin = require('url-join'); | |
5 | -const uniqid = require('uniqid'); | |
6 | - | |
7 | -const config = require('komodo-sdk/config'); | |
8 | -const logger = require('komodo-sdk/logger'); | |
9 | - | |
10 | -const report = require('../report/prepaid'); | |
11 | -const translateRc = require('../translate-rc'); | |
12 | -const composeCallbackUrl = require('./compose-callback-url'); | |
13 | -const dumpReqRes = require('./dump-req-res'); | |
14 | - | |
15 | -module.exports = async (task, isAdvice) => { | |
16 | - const xid = uniqid(); | |
17 | - | |
18 | - logger.verbose(`${MODULE_NAME} 2272F01F: Processing task`, { | |
19 | - xid, | |
20 | - isAdvice, | |
21 | - task, | |
22 | - }); | |
23 | - | |
24 | - const params = { | |
25 | - request_id: task.trx_id, | |
26 | - terminal_name: config.partner.terminal_name, | |
27 | - password: config.partner.password, | |
28 | - destination: task.destination, | |
29 | - product_name: task.remote_product, | |
30 | - reverse_url: composeCallbackUrl(xid, false), | |
31 | - }; | |
32 | - | |
33 | - const endpointUrl = isAdvice | |
34 | - ? urljoin(config.partner.url, '/trx-status') | |
35 | - : urljoin(config.partner.url, '/topup'); | |
36 | - | |
37 | - let lastResponse = null; | |
38 | - | |
39 | - try { | |
40 | - logger.verbose(`${MODULE_NAME} 4AAD4F99: Going to HIT prepaid endpoint`, { | |
41 | - xid, | |
42 | - endpointUrl, | |
43 | - params, | |
44 | - }); | |
45 | - | |
46 | - const response = await axios.get(endpointUrl, { | |
47 | - headers: { | |
48 | - 'User-Agent': 'KOMODO-GW-HTTPGETX', | |
49 | - }, | |
50 | - timeout: config.partner.hit_timeout_ms || 60 * 1000, | |
51 | - params, | |
52 | - }); | |
53 | - | |
54 | - if (!response) { | |
55 | - const e = new Error(`${MODULE_NAME} 8CF4E04D: Empty response`); | |
56 | - e.rc = '68'; | |
57 | - e.response = response; | |
58 | - throw e; | |
59 | - } | |
60 | - | |
61 | - if (!response.data) { | |
62 | - const e = new Error(`${MODULE_NAME} E72B5A53: Empty response data`); | |
63 | - e.rc = '68'; | |
64 | - e.response = response; | |
65 | - throw e; | |
66 | - } | |
67 | - | |
68 | - if (typeof response.data !== 'object') { | |
69 | - const e = new Error(`${MODULE_NAME} 507680AB: Response data is not a JSON`); | |
70 | - e.rc = '68'; | |
71 | - e.response = response; | |
72 | - throw e; | |
73 | - } | |
74 | - | |
75 | - lastResponse = response; | |
76 | - | |
77 | - logger.verbose(`${MODULE_NAME} E51AFBBA: Got a direct response`, { | |
78 | - xid, | |
79 | - responseBody: response.data, | |
80 | - }); | |
81 | - | |
82 | - report(xid, { | |
83 | - trx_id: task.trx_id, | |
84 | - rc: response.data.rc ? translateRc[response.data.rc] || response.data.rc | |
85 | - : '68', | |
86 | - sn: response.data.sn || null, | |
87 | - amount: Number(response.data.amount) || undefined, | |
88 | - balance: Number(response.data.ending_balance) || undefined, | |
89 | - message: { | |
90 | - xid, | |
91 | - 'DIRECT-RESPONSE': response.data, | |
92 | - 'IS-ADVICE': !!isAdvice, | |
93 | - }, | |
94 | - }); | |
95 | - } catch (e) { | |
96 | - const rc = e.rc || '68'; | |
97 | - | |
98 | - logger.warn(`${MODULE_NAME} 8E8E49F5: Exception`, { | |
99 | - xid, | |
100 | - eCode: e.code, | |
101 | - eMessage: e.message, | |
102 | - eRc: e.rc, | |
103 | - rc, | |
104 | - isAdvice, | |
105 | - responseHttpStatus: e.response && e.response.status, | |
106 | - responseBody: e.response && e.response.data, | |
107 | - }); | |
108 | - | |
109 | - lastResponse = e.response; | |
110 | - | |
111 | - report(xid, { | |
112 | - trx_id: task.trx_id, | |
113 | - rc, | |
114 | - message: { | |
115 | - xid, | |
116 | - 'KOMODO-GW-ERROR': { | |
117 | - eCode: e.code, | |
118 | - eMessage: e.message, | |
119 | - responseHttpStatus: e.response && e.response.status, | |
120 | - responseBody: e.response && e.response.data, | |
121 | - }, | |
122 | - 'IS-ADVICE': !!isAdvice, | |
123 | - }, | |
124 | - }); | |
125 | - } finally { | |
126 | - dumpReqRes( | |
127 | - xid, | |
128 | - task, | |
129 | - 'GET', | |
130 | - endpointUrl, | |
131 | - params, | |
132 | - lastResponse && lastResponse.data, | |
133 | - lastResponse && lastResponse.status, | |
134 | - lastResponse, | |
135 | - ); | |
136 | - } | |
137 | -}; |
lib/partner-prepaid.js
1 | -const hit = require('./hit/prepaid'); | |
1 | +const hitAdvice = require('./hit/prepaid-advice'); | |
2 | +const hitTopup = require('./hit/prepaid-topup'); | |
2 | 3 | |
3 | 4 | exports.buy = (task) => { |
4 | - hit(task, false); | |
5 | + hitTopup(task); | |
5 | 6 | }; |
6 | 7 | |
7 | 8 | exports.advice = (task) => { |
8 | - hit(task, true); | |
9 | + hitAdvice(task); | |
9 | 10 | }; |