Commit cd4feda874d40350602bf6821c4d9bef6e0d8bed
1 parent
05d016dd09
Exists in
master
and in
1 other branch
APISERVER and MATRIX
Showing 11 changed files with 132 additions and 3 deletions Inline Diff
config.sample.json
1 | { | 1 | { |
2 | "name": "HTTPGETX", | 2 | "name": "HTTPGETX", |
3 | 3 | ||
4 | "# core_url": "Silahkan diisi dengan core url, bisa dikosongkan jika ingin membaca dari main config komodo", | 4 | "# core_url": "Silahkan diisi dengan core url, bisa dikosongkan jika ingin membaca dari main config komodo", |
5 | "core_url": "", | 5 | "core_url": "", |
6 | 6 | ||
7 | "listener": { | 7 | "listener": { |
8 | "partner": { | 8 | "partner": { |
9 | "trust_proxy": ["loopback", "linklocal"], | 9 | "trust_proxy": ["loopback", "linklocal"], |
10 | "port": 25614 | 10 | "port": 25614 |
11 | }, | 11 | }, |
12 | "core": { | 12 | "core": { |
13 | "port": 25615 | 13 | "port": 25615 |
14 | }, | ||
15 | "apiserver": { | ||
16 | "port": 25617, | ||
17 | "apikey": "PLEASE_CHANGE_ME" | ||
14 | } | 18 | } |
15 | }, | 19 | }, |
16 | 20 | ||
17 | "callback_sender": { | 21 | "callback_sender": { |
18 | "http_timeout_ms": 30000, | 22 | "http_timeout_ms": 30000, |
19 | "sleep_before_retry_ms": 10000, | 23 | "sleep_before_retry_ms": 10000, |
20 | "max_retry": 10 | 24 | "max_retry": 10 |
21 | }, | 25 | }, |
22 | 26 | ||
23 | "# cluster": "Isi dengan boolean atau angka jumlah anak yang akan dibuat. Jika diisi boolean true, jumlah anak akan dihitung otomatis", | 27 | "# cluster": "Isi dengan boolean atau angka jumlah anak yang akan dibuat. Jika diisi boolean true, jumlah anak akan dihitung otomatis", |
24 | "cluster": false | 28 | "cluster": false |
25 | } | 29 | } |
index.js
1 | process.chdir(__dirname); | 1 | process.chdir(__dirname); |
2 | 2 | ||
3 | const fs = require('fs'); | 3 | const fs = require('fs'); |
4 | const numCPUs = require('os').cpus().length; | 4 | const numCPUs = require('os').cpus().length; |
5 | 5 | ||
6 | fs.writeFileSync('pid.txt', process.pid.toString()); | 6 | fs.writeFileSync('pid.txt', process.pid.toString()); |
7 | 7 | ||
8 | const config = require('komodo-sdk/config'); | 8 | const config = require('komodo-sdk/config'); |
9 | 9 | ||
10 | global.KOMODO_LOG_LABEL = `KOMODO-CENTER@${config.name || 'HTTPGETX'}`; | 10 | global.KOMODO_LOG_LABEL = `KOMODO-CENTER@${config.name || 'HTTPGETX'}`; |
11 | process.title = global.KOMODO_LOG_LABEL; | 11 | process.title = global.KOMODO_LOG_LABEL; |
12 | 12 | ||
13 | const cluster = require('cluster'); | 13 | const cluster = require('cluster'); |
14 | const logger = require('komodo-sdk/logger'); | 14 | const logger = require('komodo-sdk/logger'); |
15 | 15 | ||
16 | if (config.cluster && cluster.isMaster) { | 16 | if (config.cluster && cluster.isMaster) { |
17 | const numberOfChildren = config.cluster === true ? numCPUs + 1 : config.cluster; | 17 | const numberOfChildren = config.cluster === true ? numCPUs + 1 : config.cluster; |
18 | 18 | ||
19 | logger.info('Running on cluster mode', { | 19 | logger.info('Running on cluster mode', { |
20 | masterPid: process.pid, | 20 | masterPid: process.pid, |
21 | numberOfChildren, | 21 | numberOfChildren, |
22 | }); | 22 | }); |
23 | 23 | ||
24 | for (let i = 0; i < numberOfChildren; i += 1) { | 24 | for (let i = 0; i < numberOfChildren; i += 1) { |
25 | cluster.fork(); | 25 | cluster.fork(); |
26 | } | 26 | } |
27 | } else { | 27 | } else { |
28 | if (config.cluster) { | 28 | if (config.cluster) { |
29 | logger.info(`Worker ${process.pid} started`); | 29 | logger.info(`Worker ${process.pid} started`); |
30 | } | 30 | } |
31 | 31 | ||
32 | // eslint-disable-next-line global-require | 32 | // eslint-disable-next-line global-require |
33 | require('./lib/apiserver'); | ||
34 | // eslint-disable-next-line global-require | ||
33 | require('./lib/core-callback'); | 35 | require('./lib/core-callback'); |
34 | // eslint-disable-next-line global-require | 36 | // eslint-disable-next-line global-require |
35 | require('./lib/partner-listener'); | 37 | require('./lib/partner-listener'); |
36 | } | 38 | } |
37 | 39 |
lib/apiserver/index.js
File was created | 1 | const MODULE_NAME = 'APISERVER'; | |
2 | |||
3 | const express = require('express'); | ||
4 | const config = require('komodo-sdk/config'); | ||
5 | const logger = require('komodo-sdk/logger'); | ||
6 | |||
7 | const validApikey = require('./valid-apikey'); | ||
8 | |||
9 | const middlewareCommon = require('../middlewares/common'); | ||
10 | const routerMatrix = require('./routers/matrix'); | ||
11 | |||
12 | const app = express(); | ||
13 | |||
14 | app.use((req, res, next) => { | ||
15 | res.locals.httpgetx_subsystem = MODULE_NAME; | ||
16 | next(); | ||
17 | }); | ||
18 | |||
19 | const apikeyChecker = (req, res, next) => { | ||
20 | if (validApikey(req.params.apikey)) { | ||
21 | next(); | ||
22 | } else { | ||
23 | res.status(403).end('Invalid APIKEY'); | ||
24 | } | ||
25 | }; | ||
26 | |||
27 | app.use(middlewareCommon); | ||
28 | |||
29 | app.use('/apikey/:apikey', apikeyChecker); | ||
30 | app.use('/apikey/:apikey/matrix', routerMatrix); | ||
31 | |||
32 | app.use((req, res) => { | ||
33 | const { xid } = res.locals; | ||
34 | res.status(404).end(`KOMODO-HTTP-GET-X CENTER (APISERVER).\n404: Method not found.\n\nXID: ${xid}.\n`); | ||
35 | }); | ||
36 | |||
37 | const listenPort = config.listener && config.listener.apiserver && config.listener.apiserver.port; | ||
38 | if ( | ||
39 | listenPort | ||
40 | && config.listener && config.listener.apiserver && config.listener.apiserver.apikey | ||
41 | ) { | ||
42 | app.listen(listenPort, () => { | ||
43 | logger.info(`${MODULE_NAME} FAEE1E47: Listening`, { port: listenPort }); | ||
44 | }); | ||
45 | } else { | ||
46 | logger.info(`${MODULE_NAME}: Disabled because of missing configuration`); | ||
47 | } | ||
48 |
lib/apiserver/routers/matrix.js
File was created | 1 | const express = require('express'); | |
2 | |||
3 | const os = require('os'); | ||
4 | const matrix = require('../../matrix'); | ||
5 | |||
6 | const router = express.Router(); | ||
7 | module.exports = router; | ||
8 | |||
9 | const pageIndex = (req, res) => { | ||
10 | matrix.uptime = process.uptime(); | ||
11 | matrix.loadavg = os.loadavg(); | ||
12 | matrix.workdir = process.cwd; | ||
13 | matrix.memory_usage = process.memoryUsage(); | ||
14 | |||
15 | res.json(matrix); | ||
16 | }; | ||
17 | |||
18 | router.all('/', pageIndex); | ||
19 |
lib/apiserver/valid-apikey.js
File was created | 1 | const config = require('komodo-sdk/config'); | |
2 | |||
3 | module.exports = (apikey) => apikey | ||
4 | && apikey === ( | ||
5 | config.listener && config.listener.apiserver && config.listener.apiserver.apikey | ||
6 | ); | ||
7 |
lib/core-callback/index.js
1 | const MODULE_NAME = 'CORE-CALLBACK'; | 1 | const MODULE_NAME = 'CORE-CALLBACK'; |
2 | 2 | ||
3 | const DEFAULT_LISTENER_FROM_CORE = 25613; | 3 | const DEFAULT_LISTENER_FROM_CORE = 25613; |
4 | 4 | ||
5 | const express = require('express'); | 5 | const express = require('express'); |
6 | const config = require('komodo-sdk/config'); | 6 | const config = require('komodo-sdk/config'); |
7 | const logger = require('komodo-sdk/logger'); | 7 | const logger = require('komodo-sdk/logger'); |
8 | const middlewareCommon = require('../middlewares/common'); | 8 | const middlewareCommon = require('../middlewares/common'); |
9 | const sender = require('./sender'); | 9 | const sender = require('./sender'); |
10 | const matrix = require('../matrix'); | ||
11 | |||
10 | 12 | ||
11 | const app = express(); | 13 | const app = express(); |
12 | 14 | ||
13 | app.use(express.json({ extended: true })); | 15 | app.use(express.json({ extended: true })); |
14 | app.use(express.urlencoded({ extended: true })); | 16 | app.use(express.urlencoded({ extended: true })); |
15 | 17 | ||
16 | app.use((req, res, next) => { | 18 | app.use((req, res, next) => { |
17 | res.locals.httpgetx_subsystem = MODULE_NAME; | 19 | res.locals.httpgetx_subsystem = MODULE_NAME; |
18 | next(); | 20 | next(); |
19 | }); | 21 | }); |
20 | 22 | ||
21 | app.use(middlewareCommon); | 23 | app.use(middlewareCommon); |
22 | 24 | ||
23 | app.use((req, res) => { | 25 | app.use((req, res) => { |
26 | matrix.messages_from_core += 1; | ||
24 | res.end('OK'); | 27 | res.end('OK'); |
25 | sender(req.query, res.locals.xid); | 28 | sender(req.query, res.locals.xid); |
26 | }); | 29 | }); |
27 | 30 | ||
28 | const port = (config.listener && config.listener.core && config.listener.core.port) | 31 | const port = (config.listener && config.listener.core && config.listener.core.port) |
29 | || DEFAULT_LISTENER_FROM_CORE; | 32 | || DEFAULT_LISTENER_FROM_CORE; |
30 | 33 | ||
31 | app.listen(port, () => { | 34 | app.listen(port, () => { |
32 | logger.info(`${MODULE_NAME} 0375DC4E: Listen from CORE callback on port ${port}`); | 35 | logger.info(`${MODULE_NAME} 0375DC4E: Listen from CORE callback on port ${port}`); |
33 | }).on('error', (e) => { | 36 | }).on('error', (e) => { |
34 | logger.error(`${MODULE_NAME} A90E42D5: Can not listen CORE callback on port ${port}. ${e.toString()}`); | 37 | logger.error(`${MODULE_NAME} A90E42D5: Can not listen CORE callback on port ${port}. ${e.toString()}`); |
35 | process.exit(1); | 38 | process.exit(1); |
36 | }); | 39 | }); |
37 | 40 |
lib/core-callback/sender.js
1 | const MODULE_NAME = 'CORE-CALLBACK.SENDER'; | 1 | const MODULE_NAME = 'CORE-CALLBACK.SENDER'; |
2 | 2 | ||
3 | const axios = require('axios').default; | 3 | const axios = require('axios').default; |
4 | const config = require('komodo-sdk/config'); | 4 | const config = require('komodo-sdk/config'); |
5 | const logger = require('komodo-sdk/logger'); | 5 | const logger = require('komodo-sdk/logger'); |
6 | 6 | ||
7 | const matrix = require('../matrix'); | ||
8 | |||
7 | const HTTP_TIMEOUT = Number( | 9 | const HTTP_TIMEOUT = Number( |
8 | config.callback_sender && config.callback_sender.http_timeout_ms, | 10 | config.callback_sender && config.callback_sender.http_timeout_ms, |
9 | ) || 30 * 1000; | 11 | ) || 30 * 1000; |
10 | 12 | ||
11 | const SLEEP_BEFORE_RETRY_MS = Number( | 13 | const SLEEP_BEFORE_RETRY_MS = Number( |
12 | config.callback_sender && config.callback_sender.sleep_before_retry_ms, | 14 | config.callback_sender && config.callback_sender.sleep_before_retry_ms, |
13 | ) || 10 * 1000; | 15 | ) || 10 * 1000; |
14 | 16 | ||
15 | const MAX_RETRY = Number( | 17 | const MAX_RETRY = Number( |
16 | config.callback_sender && config.callback_sender.max_retry, | 18 | config.callback_sender && config.callback_sender.max_retry, |
17 | ) || 10; | 19 | ) || 10; |
18 | 20 | ||
19 | logger.verbose(`${MODULE_NAME} 848B9104: Initialized`, { | 21 | logger.verbose(`${MODULE_NAME} 848B9104: Initialized`, { |
20 | HTTP_TIMEOUT, | 22 | HTTP_TIMEOUT, |
21 | SLEEP_BEFORE_RETRY_MS, | 23 | SLEEP_BEFORE_RETRY_MS, |
22 | MAX_RETRY, | 24 | MAX_RETRY, |
23 | }); | 25 | }); |
24 | 26 | ||
25 | const axiosHeaders = { | 27 | const axiosHeaders = { |
26 | 'Content-Type': 'application/json', | 28 | 'Content-Type': 'application/json', |
27 | 'User-Agent': 'KOMODO-HTTPGETX callback sender', | 29 | 'User-Agent': 'KOMODO-HTTPGETX callback sender', |
28 | }; | 30 | }; |
29 | 31 | ||
30 | const sleep = require('../sleep'); | 32 | const sleep = require('../sleep'); |
31 | const urlConcatQs = require('../url-concat-qs'); | 33 | const urlConcatQs = require('../url-concat-qs'); |
32 | 34 | ||
33 | const sender = async (data, xid, retry) => { | 35 | const sender = async (data, xid, retry) => { |
34 | if (!data.reverse_url) { | 36 | if (!data.reverse_url) { |
35 | logger.verbose(`${MODULE_NAME} C4FF18FB: Ignoring missing reverse url`, { | 37 | logger.verbose(`${MODULE_NAME} C4FF18FB: Ignoring missing reverse url`, { |
36 | xid, | 38 | xid, |
37 | dataFromCore: data, | 39 | dataFromCore: data, |
38 | }); | 40 | }); |
39 | 41 | ||
40 | return; | 42 | return; |
41 | } | 43 | } |
42 | 44 | ||
43 | const params = { | 45 | const params = { |
44 | httpgetx_xid: xid, | 46 | httpgetx_xid: xid, |
45 | command: data.command, | 47 | command: data.command, |
46 | 48 | ||
47 | request_id: data.request_id && data.request_id.toString(), | 49 | request_id: data.request_id && data.request_id.toString(), |
48 | transaction_id: data.transaction_id && data.transaction_id.toString(), | 50 | transaction_id: data.transaction_id && data.transaction_id.toString(), |
49 | transaction_date: data.transaction_date, | 51 | transaction_date: data.transaction_date, |
50 | 52 | ||
51 | store_name: data.store_name, | 53 | store_name: data.store_name, |
52 | terminal_name: data.terminal_name, | 54 | terminal_name: data.terminal_name, |
53 | 55 | ||
54 | product_name: data.product_name, | 56 | product_name: data.product_name, |
55 | destination: data.destination, | 57 | destination: data.destination, |
56 | 58 | ||
57 | rc: data.rc, | 59 | rc: data.rc, |
58 | sn: data.sn || undefined, | 60 | sn: data.sn || undefined, |
59 | amount: Number(data.amount) || undefined, | 61 | amount: Number(data.amount) || undefined, |
60 | ending_balance: Number(data.ending_balance) || undefined, | 62 | ending_balance: Number(data.ending_balance) || undefined, |
61 | 63 | ||
62 | message: data.message, | 64 | message: data.message, |
63 | 65 | ||
64 | bill_count: Number(data.bill_count) || undefined, | 66 | bill_count: Number(data.bill_count) || undefined, |
65 | bill_amount: Number(data.bill_amount) || undefined, | 67 | bill_amount: Number(data.bill_amount) || undefined, |
66 | fee_per_bill: Number(data.fee) || undefined, | 68 | fee_per_bill: Number(data.fee) || undefined, |
67 | fee_total: Number(data.fee_total) || undefined, | 69 | fee_total: Number(data.fee_total) || undefined, |
68 | 70 | ||
69 | bill_detail: data.bill_detail || undefined, | 71 | bill_detail: data.bill_detail || undefined, |
70 | }; | 72 | }; |
71 | 73 | ||
72 | if (data.command === 'INQUIRY' && data.amount_to_charge) { | 74 | if (data.command === 'INQUIRY' && data.amount_to_charge) { |
73 | params.amount_to_charge = data.amount_to_charge; | 75 | params.amount_to_charge = data.amount_to_charge; |
74 | } | 76 | } |
75 | 77 | ||
76 | const isPostpaid = ['INQUIRY', 'PAY'].indexOf(data.command) >= 0; | 78 | const isPostpaid = ['INQUIRY', 'PAY'].indexOf(data.command) >= 0; |
77 | const isHttpPost = isPostpaid; | 79 | const isHttpPost = isPostpaid; |
78 | 80 | ||
79 | const endpointUrl = isHttpPost ? data.reverse_url : urlConcatQs(data.reverse_url, params); | 81 | const endpointUrl = isHttpPost ? data.reverse_url : urlConcatQs(data.reverse_url, params); |
80 | 82 | ||
81 | logger.info(`${MODULE_NAME} 8B6A4CEC: Sending to PARTNER`, { | 83 | logger.info(`${MODULE_NAME} 8B6A4CEC: Sending to PARTNER`, { |
82 | xid, | 84 | xid, |
83 | retry, | 85 | retry, |
84 | isPostpaid, | 86 | isPostpaid, |
85 | isHttpPost, | 87 | isHttpPost, |
86 | endpointUrl, | 88 | endpointUrl, |
87 | }); | 89 | }); |
88 | 90 | ||
89 | try { | 91 | try { |
90 | const response = isHttpPost | 92 | const response = isHttpPost |
91 | ? await axios.post(data.reverse_url, params, { | 93 | ? await axios.post(data.reverse_url, params, { |
92 | timeout: HTTP_TIMEOUT, | 94 | timeout: HTTP_TIMEOUT, |
93 | headers: axiosHeaders, | 95 | headers: axiosHeaders, |
94 | }) | 96 | }) |
95 | : await axios.get(data.reverse_url, { | 97 | : await axios.get(data.reverse_url, { |
96 | params, | 98 | params, |
97 | timeout: HTTP_TIMEOUT, | 99 | timeout: HTTP_TIMEOUT, |
98 | headers: axiosHeaders, | 100 | headers: axiosHeaders, |
99 | }); | 101 | }); |
100 | 102 | ||
103 | matrix.callback_sender.message_sent += 1; | ||
104 | |||
105 | if (isPostpaid) { | ||
106 | matrix.callback_sender.message_sent_using_post_method += 1; | ||
107 | } else { | ||
108 | matrix.callback_sender.message_sent_using_get_method += 1; | ||
109 | } | ||
110 | |||
101 | logger.info(`${MODULE_NAME} 3641FBD7: Has been sent to PARTNER successfully`, { | 111 | logger.info(`${MODULE_NAME} 3641FBD7: Has been sent to PARTNER successfully`, { |
102 | xid, | 112 | xid, |
103 | retry, | 113 | retry, |
104 | httpStatus: response.status, | 114 | httpStatus: response.status, |
105 | responseBody: response && response.data, | 115 | responseBody: response && response.data, |
106 | }); | 116 | }); |
107 | } catch (e) { | 117 | } catch (e) { |
118 | matrix.callback_sender.message_sent_failed += 1; | ||
119 | |||
108 | logger.warn(`${MODULE_NAME} A1EC9E70: Failed on sending to PARTNER`, { | 120 | logger.warn(`${MODULE_NAME} A1EC9E70: Failed on sending to PARTNER`, { |
109 | xid, | 121 | xid, |
110 | retry, | 122 | retry, |
111 | maxRetry: MAX_RETRY, | 123 | maxRetry: MAX_RETRY, |
112 | errCode: e.code, | 124 | errCode: e.code, |
113 | errMessage: e.message, | 125 | errMessage: e.message, |
114 | reverseUrl: data.reverse_url, | 126 | reverseUrl: data.reverse_url, |
115 | endpointUrl, | 127 | endpointUrl, |
116 | httpStatus: e.response && e.response.status, | 128 | httpStatus: e.response && e.response.status, |
117 | responseBody: e.response && e.response.data, | 129 | responseBody: e.response && e.response.data, |
118 | }); | 130 | }); |
119 | 131 | ||
120 | if ((retry || 0) < MAX_RETRY) { | 132 | if ((retry || 0) < MAX_RETRY) { |
121 | await sleep(SLEEP_BEFORE_RETRY_MS); | 133 | await sleep(SLEEP_BEFORE_RETRY_MS); |
122 | logger.verbose(`${MODULE_NAME} D8958695: Going to retry sending CORE-CALLBACK TO PARTNER`, { | 134 | logger.verbose(`${MODULE_NAME} D8958695: Going to retry sending CORE-CALLBACK TO PARTNER`, { |
123 | xid, sleepTime: SLEEP_BEFORE_RETRY_MS, | 135 | xid, sleepTime: SLEEP_BEFORE_RETRY_MS, |
124 | }); | 136 | }); |
125 | sender(data, xid, (retry || 0) + 1); | 137 | sender(data, xid, (retry || 0) + 1); |
126 | } | 138 | } |
127 | } | 139 | } |
128 | }; | 140 | }; |
129 | 141 | ||
130 | module.exports = sender; | 142 | module.exports = sender; |
131 | 143 |
lib/matrix.js
File was created | 1 | const os = require('os'); | |
2 | // const config = require('komodo-sdk/config'); | ||
3 | |||
4 | module.exports = { | ||
5 | pid: process.pid, | ||
6 | start_time: new Date(), | ||
7 | uptime: process.uptime(), | ||
8 | hostname: os.hostname(), | ||
9 | loadavg: os.loadavg(), | ||
10 | workdir: process.cwd(), | ||
11 | memory_usage: process.memoryUsage(), | ||
12 | messages_from_core: 0, | ||
13 | messages_to_core: 0, | ||
14 | callback_sender: { | ||
15 | message_sent: 0, | ||
16 | message_sent_failed: 0, | ||
17 | message_sent_using_get_method: 0, | ||
18 | message_sent_using_post_method: 0, | ||
19 | }, | ||
20 | }; | ||
21 |
lib/middlewares/common.js
1 | // const uuidv1 = require('uuid/v1'); | 1 | const MODULE_NAME = 'MIDDLEWARES'; |
2 | |||
3 | const uuidv1 = require('uuid/v1'); | ||
2 | const uniqid = require('uniqid'); | 4 | const uniqid = require('uniqid'); |
5 | |||
6 | const config = require('komodo-sdk/config'); | ||
3 | const logger = require('komodo-sdk/logger'); | 7 | const logger = require('komodo-sdk/logger'); |
4 | 8 | ||
5 | module.exports = function common(req, res, next) { | 9 | module.exports = function common(req, res, next) { |
6 | if (req.url.search('/favicon.ico') === 0) { | 10 | if (req.url.search('/favicon.ico') === 0) { |
7 | res.sendStatus(404); | 11 | res.sendStatus(404); |
8 | return; | 12 | return; |
9 | } | 13 | } |
10 | 14 | ||
11 | res.locals.xid = uniqid(); | 15 | res.locals.xid = config.xid_from_uuid ? uuidv1() |
16 | : uniqid(); | ||
17 | |||
12 | res.locals.x_http_request_ts = new Date(); | 18 | res.locals.x_http_request_ts = new Date(); |
13 | 19 | ||
14 | logger.info(`Got request from ${res.locals.httpgetx_subsystem === 'CORE-CALLBACK' ? 'CORE' : 'PARTNER'}`, { | 20 | logger.info(`${MODULE_NAME}.COMMON B6257542: Got a request`, { |
15 | xid: res.locals.xid, | 21 | xid: res.locals.xid, |
16 | pid: process.pid, | 22 | pid: process.pid, |
23 | subsystem: res.locals.httpgetx_subsystem, | ||
17 | requester_ip: req.ip, | 24 | requester_ip: req.ip, |
18 | method: req.method, | 25 | method: req.method, |
19 | path: req.path, | 26 | path: req.path, |
20 | url: req.url, | 27 | url: req.url, |
21 | qs: req.query, | 28 | qs: req.query, |
22 | body: req.body, | 29 | body: req.body, |
23 | }); | 30 | }); |
24 | next(); | 31 | next(); |
25 | }; | 32 | }; |
26 | 33 |
lib/partner-listener/index.js
1 | const MODULE_NAME = 'PARTNER-LISTENER'; | ||
2 | |||
1 | const DEFAULT_LISTENER_FROM_PARTNER_PORT = 25614; | 3 | const DEFAULT_LISTENER_FROM_PARTNER_PORT = 25614; |
2 | 4 | ||
3 | const express = require('express'); | 5 | const express = require('express'); |
4 | 6 | ||
5 | const config = require('komodo-sdk/config'); | 7 | const config = require('komodo-sdk/config'); |
6 | const logger = require('komodo-sdk/logger'); | 8 | const logger = require('komodo-sdk/logger'); |
7 | 9 | ||
8 | const middlewareCommon = require('../middlewares/common'); | 10 | const middlewareCommon = require('../middlewares/common'); |
9 | 11 | ||
10 | const routerInquiry = require('./routers/inquiry'); | 12 | const routerInquiry = require('./routers/inquiry'); |
11 | const routerPay = require('./routers/pay'); | 13 | const routerPay = require('./routers/pay'); |
12 | const routerTopup = require('./routers/topup'); | 14 | const routerTopup = require('./routers/topup'); |
13 | const routerTrxStatus = require('./routers/trx-status'); | 15 | const routerTrxStatus = require('./routers/trx-status'); |
14 | 16 | ||
15 | const app = express(); | 17 | const app = express(); |
16 | 18 | ||
17 | if (config.partner && config.partner.trust_proxy) { | 19 | if (config.partner && config.partner.trust_proxy) { |
18 | app.set('trust proxy', config.partner.trust_proxy); | 20 | app.set('trust proxy', config.partner.trust_proxy); |
19 | } | 21 | } |
20 | 22 | ||
23 | app.use((req, res, next) => { | ||
24 | res.locals.httpgetx_subsystem = MODULE_NAME; | ||
25 | next(); | ||
26 | }); | ||
27 | |||
21 | app.use(express.json({ extended: true })); | 28 | app.use(express.json({ extended: true })); |
22 | app.use(express.urlencoded({ extended: true })); | 29 | app.use(express.urlencoded({ extended: true })); |
23 | app.use(middlewareCommon); | 30 | app.use(middlewareCommon); |
24 | 31 | ||
25 | app.use('/ping', (req, res) => { | 32 | app.use('/ping', (req, res) => { |
26 | res.json({ | 33 | res.json({ |
27 | error: false, | 34 | error: false, |
28 | xid: res.locals.xid, | 35 | xid: res.locals.xid, |
29 | from_ip: req.ip, | 36 | from_ip: req.ip, |
30 | ts: new Date(), | 37 | ts: new Date(), |
31 | request: { | 38 | request: { |
32 | method: req.method, | 39 | method: req.method, |
33 | userAgent: req.get('user-agent'), | 40 | userAgent: req.get('user-agent'), |
34 | contentType: req.get('content-type'), | 41 | contentType: req.get('content-type'), |
35 | qs: req.query, | 42 | qs: req.query, |
36 | body: req.body, | 43 | body: req.body, |
37 | }, | 44 | }, |
38 | }); | 45 | }); |
39 | }); | 46 | }); |
40 | app.use('/inquiry', routerInquiry); | 47 | app.use('/inquiry', routerInquiry); |
41 | app.use('/pay', routerPay); | 48 | app.use('/pay', routerPay); |
42 | app.use('/topup', routerTopup); | 49 | app.use('/topup', routerTopup); |
43 | app.use('/trx-status', routerTrxStatus); | 50 | app.use('/trx-status', routerTrxStatus); |
44 | 51 | ||
45 | app.use((req, res) => { | 52 | app.use((req, res) => { |
46 | const { xid } = res.locals; | 53 | const { xid } = res.locals; |
47 | res.status(404).end(`KOMODO-HTTP-GET-X CENTER.\n404: Method not found.\n\nXID: ${xid}.\n`); | 54 | res.status(404).end(`KOMODO-HTTP-GET-X CENTER.\n404: Method not found.\n\nXID: ${xid}.\n`); |
48 | }); | 55 | }); |
49 | 56 | ||
50 | app.listen(config.listener.partner.port || DEFAULT_LISTENER_FROM_PARTNER_PORT, () => { | 57 | app.listen(config.listener.partner.port || DEFAULT_LISTENER_FROM_PARTNER_PORT, () => { |
51 | logger.info(`Listen from partner request on port ${config.listener.partner.port}`); | 58 | logger.info(`Listen from partner request on port ${config.listener.partner.port}`); |
52 | }).on('error', (e) => { | 59 | }).on('error', (e) => { |
53 | logger.error(`Can not listen request from partner on port ${config.listener.partner.port || DEFAULT_LISTENER_FROM_PARTNER_PORT}. ${e.toString()}`); | 60 | logger.error(`Can not listen request from partner on port ${config.listener.partner.port || DEFAULT_LISTENER_FROM_PARTNER_PORT}. ${e.toString()}`); |
54 | process.exit(1); | 61 | process.exit(1); |
55 | }); | 62 | }); |
56 | 63 |
lib/partner-listener/routers/topup.js
1 | const MODULE_NAME = 'PARTNER-LISTENER.ROUTER.TOPUP'; | 1 | const MODULE_NAME = 'PARTNER-LISTENER.ROUTER.TOPUP'; |
2 | 2 | ||
3 | const express = require('express'); | 3 | const express = require('express'); |
4 | 4 | ||
5 | const config = require('komodo-sdk/config'); | 5 | const config = require('komodo-sdk/config'); |
6 | const logger = require('komodo-sdk/logger'); | 6 | const logger = require('komodo-sdk/logger'); |
7 | const coreapi = require('komodo-sdk/coreapi'); | 7 | const coreapi = require('komodo-sdk/coreapi'); |
8 | // const coreapi = require('../../coreapi'); | 8 | // const coreapi = require('../../coreapi'); |
9 | const matrix = require('../../matrix'); | ||
9 | 10 | ||
10 | const router = express.Router(); | 11 | const router = express.Router(); |
11 | module.exports = router; | 12 | module.exports = router; |
12 | 13 | ||
13 | function onInvalidParameter(missingParameter, req, res) { | 14 | function onInvalidParameter(missingParameter, req, res) { |
14 | logger.verbose(`${MODULE_NAME} 1536D577: Undefined ${missingParameter} parameter`, { | 15 | logger.verbose(`${MODULE_NAME} 1536D577: Undefined ${missingParameter} parameter`, { |
15 | xid: res.locals.xid, | 16 | xid: res.locals.xid, |
16 | ip: req.ip, | 17 | ip: req.ip, |
17 | terminal_name: req.body.terminal_name || req.query.terminal_name, | 18 | terminal_name: req.body.terminal_name || req.query.terminal_name, |
18 | request_id: req.body.request_id || req.query.request_id, | 19 | request_id: req.body.request_id || req.query.request_id, |
19 | product_name: req.body.product_name || req.query.product_name, | 20 | product_name: req.body.product_name || req.query.product_name, |
20 | destination: req.body.destination || req.query.destination, | 21 | destination: req.body.destination || req.query.destination, |
21 | }); | 22 | }); |
22 | res.end('INVALID REQUEST'); | 23 | res.end('INVALID REQUEST'); |
23 | } | 24 | } |
24 | 25 | ||
25 | function pagePrerequisite(req, res, next) { | 26 | function pagePrerequisite(req, res, next) { |
26 | if (!req.body) req.body = {}; | 27 | if (!req.body) req.body = {}; |
27 | 28 | ||
28 | if (!req.body.terminal_name && !req.query.terminal_name) { | 29 | if (!req.body.terminal_name && !req.query.terminal_name) { |
29 | onInvalidParameter('terminal_name', req, res); | 30 | onInvalidParameter('terminal_name', req, res); |
30 | return; | 31 | return; |
31 | } | 32 | } |
32 | 33 | ||
33 | if (!req.body.password && !req.query.password) { | 34 | if (!req.body.password && !req.query.password) { |
34 | onInvalidParameter('password', req, res); | 35 | onInvalidParameter('password', req, res); |
35 | return; | 36 | return; |
36 | } | 37 | } |
37 | 38 | ||
38 | if (!req.body.request_id && !req.query.request_id) { | 39 | if (!req.body.request_id && !req.query.request_id) { |
39 | onInvalidParameter('request_id', req, res); | 40 | onInvalidParameter('request_id', req, res); |
40 | return; | 41 | return; |
41 | } | 42 | } |
42 | 43 | ||
43 | if (!req.body.product_name && !req.query.product_name) { | 44 | if (!req.body.product_name && !req.query.product_name) { |
44 | onInvalidParameter('product_name', req, res); | 45 | onInvalidParameter('product_name', req, res); |
45 | return; | 46 | return; |
46 | } | 47 | } |
47 | 48 | ||
48 | if (!req.body.destination && !req.query.destination) { | 49 | if (!req.body.destination && !req.query.destination) { |
49 | onInvalidParameter('destination', req, res); | 50 | onInvalidParameter('destination', req, res); |
50 | return; | 51 | return; |
51 | } | 52 | } |
52 | 53 | ||
53 | next(); | 54 | next(); |
54 | } | 55 | } |
55 | 56 | ||
56 | async function pageIndex(req, res) { | 57 | async function pageIndex(req, res) { |
57 | const { xid } = res.locals; | 58 | const { xid } = res.locals; |
58 | 59 | ||
59 | const terminalName = `${req.body.terminal_name || req.query.terminal_name}@${req.ip.replace(/^::ffff:/, '')}`; | 60 | const terminalName = `${req.body.terminal_name || req.query.terminal_name}@${req.ip.replace(/^::ffff:/, '')}`; |
60 | 61 | ||
62 | matrix.messages_to_core += 1; | ||
63 | |||
61 | const [err, coreResponse] = await coreapi({ | 64 | const [err, coreResponse] = await coreapi({ |
62 | xid, | 65 | xid, |
63 | path: '/prepaid/buy', | 66 | path: '/prepaid/buy', |
64 | qs: { | 67 | qs: { |
65 | terminal_name: terminalName, | 68 | terminal_name: terminalName, |
66 | password: req.body.password || req.query.password, | 69 | password: req.body.password || req.query.password, |
67 | request_id: req.body.request_id || req.query.request_id, | 70 | request_id: req.body.request_id || req.query.request_id, |
68 | product_name: req.body.product_name || req.query.product_name, | 71 | product_name: req.body.product_name || req.query.product_name, |
69 | destination: req.body.destination || req.query.destination, | 72 | destination: req.body.destination || req.query.destination, |
70 | origin: config.name || 'HTTPGETX', | 73 | origin: config.name || 'HTTPGETX', |
71 | report_ip: config.listener.core.ip || null, | 74 | report_ip: config.listener.core.ip || null, |
72 | report_port: config.listener.core.port, | 75 | report_port: config.listener.core.port, |
73 | reverse_url: req.body.reverse_url || req.query.reverse_url || null, | 76 | reverse_url: req.body.reverse_url || req.query.reverse_url || null, |
74 | }, | 77 | }, |
75 | }); | 78 | }); |
76 | 79 | ||
77 | if (err || !coreResponse) { | 80 | if (err || !coreResponse) { |
78 | logger.warn(`${MODULE_NAME} 8DEBE15F: ERROR on /prepaid/buy response`, { | 81 | logger.warn(`${MODULE_NAME} 8DEBE15F: ERROR on /prepaid/buy response`, { |
79 | xid, | 82 | xid, |
80 | err, | 83 | err, |
81 | coreResponseTypeof: typeof coreResponse, | 84 | coreResponseTypeof: typeof coreResponse, |
82 | coreResponse, | 85 | coreResponse, |
83 | }); | 86 | }); |
84 | res.end('INVALID CORE RESPONSE'); | 87 | res.end('INVALID CORE RESPONSE'); |
85 | return; | 88 | return; |
86 | } | 89 | } |
87 | 90 | ||
88 | logger.verbose(`${MODULE_NAME} 2528A9B4: Got CORE response`, { | 91 | logger.verbose(`${MODULE_NAME} 2528A9B4: Got CORE response`, { |
89 | xid, | 92 | xid, |
90 | coreResponse, | 93 | coreResponse, |
91 | }); | 94 | }); |
92 | 95 | ||
93 | const responseToPartner = { | 96 | const responseToPartner = { |
94 | httpgetx_xid: xid, | 97 | httpgetx_xid: xid, |
95 | request_id: coreResponse.request_id, | 98 | request_id: coreResponse.request_id, |
96 | transaction_id: coreResponse.transaction_id, | 99 | transaction_id: coreResponse.transaction_id, |
97 | transaction_date: coreResponse.transaction_date, | 100 | transaction_date: coreResponse.transaction_date, |
98 | store_name: coreResponse.store_name, | 101 | store_name: coreResponse.store_name, |
99 | terminal_name: coreResponse.terminal_name, | 102 | terminal_name: coreResponse.terminal_name, |
100 | product_name: coreResponse.product_name, | 103 | product_name: coreResponse.product_name, |
101 | destination: coreResponse.destination, | 104 | destination: coreResponse.destination, |
102 | rc: coreResponse.rc, | 105 | rc: coreResponse.rc, |
103 | sn: coreResponse.sn || undefined, | 106 | sn: coreResponse.sn || undefined, |
104 | amount: Number(coreResponse.amount) || undefined, | 107 | amount: Number(coreResponse.amount) || undefined, |
105 | ending_balance: Number(coreResponse.ending_balance) || undefined, | 108 | ending_balance: Number(coreResponse.ending_balance) || undefined, |
106 | message: coreResponse.message, | 109 | message: coreResponse.message, |
107 | }; | 110 | }; |
108 | 111 | ||
109 | res.json(responseToPartner); | 112 | res.json(responseToPartner); |
110 | } | 113 | } |
111 | 114 | ||
112 | // router.all('/', (req, res) => { res.status(404).end('404: Not implemented yet'); }); | 115 | // router.all('/', (req, res) => { res.status(404).end('404: Not implemented yet'); }); |
113 | router.get('/', pagePrerequisite, pageIndex); | 116 | router.get('/', pagePrerequisite, pageIndex); |
114 | 117 |