Commit 3a7cccfa6fa74678e72a92c3d7dd3e319bc3ffaf
1 parent
560c22cdfc
Exists in
master
ready to run
Showing 6 changed files with 243 additions and 1 deletions Inline Diff
config.sample.json
File was created | 1 | { | |
2 | "handler_name": "KOMODO", | ||
3 | "products": [ | ||
4 | ], | ||
5 | "core_url": "http://127.0.0.1:32972/apikey/PLEASE_CHANGE_ME", | ||
6 | "pull_interval_ms": 1000, | ||
7 | "partner": { | ||
8 | "url": "http://PLEASE_CHANGE_ME:25614/", | ||
9 | "terminal_name": "PLEASE_CHANGE_ME", | ||
10 | "password": "PLEASE_CHANGE_ME" | ||
11 | }, | ||
12 | "remote_products": { | ||
13 | "XL5": "XJ5", | ||
14 | "XL10": "XJ10", | ||
15 | "XL15": "XJ15", | ||
16 | "XL25": "XJ25", | ||
17 | "XL30": "XJ30", | ||
18 | "XL50": "XJ50", | ||
19 | "XL100": "XJ100", | ||
20 | "XL200": "XJ200" | ||
21 | }, | ||
22 | "reverse_report_url": "http://PLEASE_CHANGE_ME:24867/", | ||
23 | "reverse_report_port": 24867, | ||
24 | "control_panel": { | ||
25 | "listen_port": 24868 | ||
26 | }, | ||
27 | "do_not_verbose_log_report": false | ||
28 | } | ||
29 |
index.js
File was created | 1 | "use strict"; | |
2 | process.chdir(__dirname); | ||
3 | |||
4 | /* | ||
5 | const config = require('komodo-sdk/config'); | ||
6 | const logger = require('komodo-sdk/logger'); | ||
7 | const matrix = require('komodo-sdk/matrix'); | ||
8 | */ | ||
9 | |||
10 | const pullgw = require('komodo-sdk/gateway/pull'); | ||
11 | const partner = require('./lib/partner'); | ||
12 | |||
13 | pullgw.setPartner(partner); | ||
14 |
lib/komodo-client.js
File was created | 1 | "use strict"; | |
2 | |||
3 | function parseResponse(body) { | ||
4 | let result; | ||
5 | try { | ||
6 | result = JSON.parse(body); | ||
7 | } | ||
8 | catch(e) { | ||
9 | result = null; | ||
10 | } | ||
11 | |||
12 | return result; | ||
13 | } | ||
14 | |||
15 | exports.parseResponse = parseResponse; | ||
16 |
lib/komodo-rc.js
File was created | 1 | "use strict"; | |
2 | |||
3 | module.exports = { | ||
4 | '00': '00', | ||
5 | '13': '90', | ||
6 | '14': '14', | ||
7 | '68': '68', | ||
8 | '88': '88', | ||
9 | '89': '89', | ||
10 | '90': '90', | ||
11 | '91': '90', | ||
12 | '93': '94', | ||
13 | '96': '68' | ||
14 | } | ||
15 |
lib/partner.js
File was created | 1 | "use strict"; | |
2 | |||
3 | const HTTP = require('http'); | ||
4 | const URL = require('url'); | ||
5 | const request = require('request'); | ||
6 | const uuidv4 = require('uuid/v4'); | ||
7 | |||
8 | const config = require('komodo-sdk/config'); | ||
9 | const logger = require('komodo-sdk/logger'); | ||
10 | const matrix = require('komodo-sdk/matrix'); | ||
11 | const pull = require('komodo-sdk/gateway/pull'); | ||
12 | |||
13 | const komodoClient = require('./komodo-client'); | ||
14 | const partnerRc = require('./komodo-rc'); | ||
15 | |||
16 | if (!matrix.pending_tasks) { | ||
17 | matrix.pending_tasks = {}; | ||
18 | } | ||
19 | |||
20 | function deleteFromPendingTasks(trx_id) { | ||
21 | if (matrix.pending_tasks && matrix.pending_tasks[trx_id]) { | ||
22 | delete matrix.pending_tasks[trx_id]; | ||
23 | } | ||
24 | } | ||
25 | |||
26 | function report(data) { | ||
27 | pull.report(data); | ||
28 | |||
29 | if (data.rc !== '68' && data.rc !== '96') { | ||
30 | deleteFromPendingTasks(data.trx_id); | ||
31 | } | ||
32 | } | ||
33 | |||
34 | function _hit(task, is_advice) { | ||
35 | |||
36 | if (matrix.pending_tasks && !matrix.pending_tasks[task.trx_id]) { | ||
37 | matrix.pending_tasks[task.trx_id] = task; | ||
38 | } | ||
39 | |||
40 | const request_options = { | ||
41 | url: config.partner.url, | ||
42 | qs: { | ||
43 | request_id: task.trx_id, | ||
44 | terminal_name: config.partner.terminal_name || config.partner.username, | ||
45 | password: config.partner.password, | ||
46 | reverse_url: config.reverse_report_url, | ||
47 | product_name: task.remote_product, | ||
48 | destination: task.destination | ||
49 | } | ||
50 | } | ||
51 | |||
52 | if (!request_options.qs.request_id || !request_options.qs.terminal_name || !request_options.qs.password || !request_options.qs.reverse_url || !request_options.qs.product_name || !request_options.qs.destination) { | ||
53 | logger.verbose('Missing parameter on request', request_options.qs); | ||
54 | return; | ||
55 | } | ||
56 | |||
57 | logger.info('Requesting to partner', {is_advice: is_advice, trx_id: task.trx_id, destination: task.destination, product: task.product, remote_product: task.remote_product}); | ||
58 | request(request_options, function(err, res, body) { | ||
59 | if (err) { | ||
60 | logger.warn('Error requesting to partner', {err: err, trx_id: task.trx_id, destination: task.destination, is_advice: is_advice}); | ||
61 | report({ | ||
62 | trx_id: task.trx_id, | ||
63 | rc: (!is_advice && (err.syscall === 'connect')) ? '91' : '68', | ||
64 | message: 'INTERNAL: REQUEST ERROR: ' + err.toString(), | ||
65 | misc: { | ||
66 | task: task | ||
67 | } | ||
68 | }); | ||
69 | return; | ||
70 | } | ||
71 | |||
72 | if (res.statusCode != 200) { | ||
73 | logger.warn('Partner returning non 200 HTTP status code', {trx_id: task.trx_id, destination: task.destination, is_advice: is_advice, http_status_code: res.statusCode, response_body: body}); | ||
74 | report({ | ||
75 | trx_id: task.trx_id, | ||
76 | rc: '68', | ||
77 | message: ('INTERNAL: Got non 200 HTTP status code: ' + res.statusCode + '\n\n' + body).trim(), | ||
78 | raw: body, | ||
79 | misc: { | ||
80 | task: task | ||
81 | } | ||
82 | }); | ||
83 | return; | ||
84 | } | ||
85 | |||
86 | logger.verbose('Got response from partner', {trx_id: task.trx_id, destination: task.destination, is_advice: is_advice, response_body: body}); | ||
87 | |||
88 | const result = komodoClient.parseResponse(body); | ||
89 | if (!result) { | ||
90 | logger.warn('Partner returning invalid JSON', {trx_id: task.trx_id, destination: task.destination, is_advice: is_advice, response_body: body}); | ||
91 | report({ | ||
92 | trx_id: task.trx_id, | ||
93 | rc: is_advice ? '68' : '90', | ||
94 | message: 'INTERNAL: Partner return invalid JSON:\n' + body, | ||
95 | raw: body, | ||
96 | misc: { | ||
97 | task: task | ||
98 | } | ||
99 | }); | ||
100 | return; | ||
101 | } | ||
102 | |||
103 | report({ | ||
104 | trx_id: task.trx_id, | ||
105 | rc: partnerRc[result.rc] || '40', | ||
106 | message: result.message, | ||
107 | sn: result.sn, | ||
108 | amount: Number(result.amount) || null, | ||
109 | raw: body, | ||
110 | misc: { | ||
111 | task: task | ||
112 | } | ||
113 | }); | ||
114 | }) | ||
115 | } | ||
116 | |||
117 | function buy(task) { | ||
118 | _hit(task, false); | ||
119 | } | ||
120 | |||
121 | function advice(task) { | ||
122 | _hit(task, true); | ||
123 | } | ||
124 | |||
125 | function reverseReportHandler(req, res) { | ||
126 | const report_id = uuidv4(); | ||
127 | |||
128 | logger.verbose('Incoming reverse report', {report_id: report_id, url: req.url}); | ||
129 | const qs = URL.parse(req.url, true).query; | ||
130 | |||
131 | if (!qs.request_id || !qs.rc) { | ||
132 | logger.verbose('No request_id and rc on reverse report message', {report_id: report_id, url: req.url, qs: qs}); | ||
133 | return; | ||
134 | } | ||
135 | |||
136 | const task = { | ||
137 | trx_id: qs.request_id, | ||
138 | destination: qs.destination, | ||
139 | remote_product: qs.product_name | ||
140 | } | ||
141 | |||
142 | report({ | ||
143 | trx_id: qs.request_id, | ||
144 | rc: partnerRc[qs.rc] || '40', | ||
145 | message: qs.message, | ||
146 | sn: qs.sn, | ||
147 | amount: qs.amount || null, | ||
148 | raw: req.url, | ||
149 | misc: { | ||
150 | task: task | ||
151 | } | ||
152 | }); | ||
153 | } | ||
154 | |||
155 | function createReverseReportHttpServer() { | ||
156 | const http_server = HTTP.createServer(reverseReportHandler); | ||
157 | http_server.listen(config.reverse_report_port, function(err) { | ||
158 | if (err) { | ||
159 | logger.warn('Error creating reverse report HTTP server: ' + err.toString()); | ||
160 | process.exit(1); | ||
161 | return; | ||
162 | } | ||
163 | |||
164 | logger.info('Reverse report HTTP server listening on port ' + config.reverse_report_port); | ||
165 | }) | ||
166 | } | ||
167 | |||
168 | createReverseReportHttpServer(); | ||
169 | |||
170 | exports.buy = buy; | ||
171 | exports.advice = advice; | ||
172 |
package.json
1 | { | 1 | { |
2 | "name": "komodo-gw-komodo", | 2 | "name": "komodo-gw-komodo", |
3 | "version": "0.1.0", | 3 | "version": "0.1.0", |
4 | "description": "Komodo Gateway to other Komodo", | 4 | "description": "Komodo Gateway to other Komodo", |
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-gw-komodo.git" | 11 | "url": "git@gitlab.kodesumber.com:komodo/komodo-gw-komodo.git" |
12 | }, | 12 | }, |
13 | "keywords": [ | 13 | "keywords": [ |
14 | "komodo", | 14 | "komodo", |
15 | "tektrans", | 15 | "tektrans", |
16 | "ppob" | 16 | "ppob" |
17 | ], | 17 | ], |
18 | "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", | 18 | "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", |
19 | "license": "ISC", | 19 | "license": "ISC", |
20 | "dependencies": { | 20 | "dependencies": { |
21 | "json-stringify-pretty-compact": "^1.1.0", | 21 | "json-stringify-pretty-compact": "^1.1.0", |
22 | "komodo-sdk": "git+http://gitlab.kodesumber.com/komodo/komodo-sdk.git", | 22 | "komodo-sdk": "git+http://gitlab.kodesumber.com/komodo/komodo-sdk.git", |
23 | "request": "^2.85.0" | 23 | "request": "^2.85.0", |
24 | "uuid": "^3.2.1" | ||
24 | } | 25 | } |
25 | } | 26 | } |
26 | 27 |