Commit fb2ba8c5da083cb5d9558773bf7ffb2a0a81fcaf
1 parent
d0cd029e97
Exists in
master
ready to run
Showing 9 changed files with 247 additions and 0 deletions Inline Diff
.gitignore
File was created | 1 | /node_modules/ | |
2 | /config.json | ||
3 | /tmp/ | ||
4 | /logs/ | ||
5 |
README
1 | Komodo gateway to ST24 XML-RPC | ||
2 |
config.sample.json
File was created | 1 | { | |
2 | "handler_name": "ST24", | ||
3 | "products": [ | ||
4 | "PLEASE_CHANGE_ME" | ||
5 | ], | ||
6 | "core_url": "http://127.0.0.1:32972/apikey/PLEASE_CHANGE_ME", | ||
7 | "pull_interval_ms": 1000, | ||
8 | "partner": { | ||
9 | "url": "https://PLEASE_CHANGE_ME:6789/", | ||
10 | "msidn": "PLEASE_CHANGE_ME", | ||
11 | "pin": "PLEASE_CHANGE_ME" | ||
12 | }, | ||
13 | "remote_products": { | ||
14 | "PLEASE_CHANGE_ME": "PLEASE_CHANGE_ME" | ||
15 | }, | ||
16 | "reverse_report_port": 27440, | ||
17 | "control_panel": { | ||
18 | "listen_port": 27441 | ||
19 | }, | ||
20 | "do_not_verbose_log_report": true | ||
21 | } | ||
22 |
index.js
File was created | 1 | "use strict"; | |
2 | process.chdir(__dirname); | ||
3 | |||
4 | const pullgw = require('komodo-sdk/gateway/pull'); | ||
5 | const partner = require('./lib/partner'); | ||
6 | const reverseReport = require('./lib/reverse-report'); | ||
7 | |||
8 | pullgw.setPartner(partner); | ||
9 |
lib/partner-rc.json
File was created | 1 | { | |
2 | "00": "00", | ||
3 | "13": "90", | ||
4 | "14": "14", | ||
5 | "68": "68", | ||
6 | "91": "90", | ||
7 | "94": "68" | ||
8 | } | ||
9 |
lib/partner.js
File was created | 1 | "use strict"; | |
2 | |||
3 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; | ||
4 | |||
5 | const url = require('url'); | ||
6 | const xmlrpc = require('xmlrpc'); | ||
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 st24 = require('./st24'); | ||
14 | const partnerRc = require('./partner-rc.json'); | ||
15 | |||
16 | function buy(task) { | ||
17 | const partnerUrl = url.parse(config.partner.url); | ||
18 | const clientOptions = { | ||
19 | host: partnerUrl.hostname, | ||
20 | port: partnerUrl.port, | ||
21 | path: partnerUrl.pathname | ||
22 | }; | ||
23 | |||
24 | let client; | ||
25 | if (partnerUrl.protocol == 'https:') { | ||
26 | client = xmlrpc.createSecureClient(clientOptions); | ||
27 | } else { | ||
28 | client = xmlrpc.createClient(clientOptions); | ||
29 | } | ||
30 | |||
31 | const params = { | ||
32 | MSISDN: config.partner.msisdn || config.partner.userid, | ||
33 | REQUESTID: task.trx_id, | ||
34 | PIN: config.partner.pin || config.partner.password, | ||
35 | NOHP: task.destination, | ||
36 | NOM: task.remote_product | ||
37 | }; | ||
38 | |||
39 | const xmlrpcMethod = 'topUpRequest'; | ||
40 | logger.info('Preparing XMLRPC request', {method: xmlrpcMethod, params: params, partnerUrl: partnerUrl.href}); | ||
41 | |||
42 | client.methodCall(xmlrpcMethod, [ params ], function (err, value) { | ||
43 | |||
44 | if (err) { | ||
45 | let rc = '68'; | ||
46 | let msg = 'XMLRPC Client Error: ' + err; | ||
47 | |||
48 | if (error.code == 'ECONNREFUSED' || error.code == 'EHOSTUNREACH' || (error.code == 'ETIMEDOUT' && error.syscall == "connect")) { | ||
49 | rc = '91'; | ||
50 | } | ||
51 | |||
52 | logger.warn(msg, {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, err: err}); | ||
53 | report({ | ||
54 | trx_id: task.trx_id, | ||
55 | rc: rc, | ||
56 | message: 'INTERNAL: ' + msg, | ||
57 | misc: { | ||
58 | task: task | ||
59 | } | ||
60 | }); | ||
61 | |||
62 | return; | ||
63 | } | ||
64 | |||
65 | logger.info('Got XMLRPC response from partner for', {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, response: value}); | ||
66 | matrix.last_topupRequest_ack = value; | ||
67 | |||
68 | report({ | ||
69 | trx_id: task.trx_id, | ||
70 | rc: partnerRc[value.RESPONSECODE] || '40', | ||
71 | message: value.MESSAGE, | ||
72 | sn: value.SN || st24.extractSnFromMessage(value.MESSAGE), | ||
73 | amount: value.PRICE || st24.extractPriceFromMsg(value.MESSAGE), | ||
74 | raw: value, | ||
75 | misc: { | ||
76 | task: task | ||
77 | } | ||
78 | }); | ||
79 | }); | ||
80 | } | ||
81 | |||
82 | function advice(task) { | ||
83 | } | ||
84 | |||
85 | function report(data) { | ||
86 | matrix.last_report_to_core = data; | ||
87 | pull.report(data); | ||
88 | } | ||
89 | |||
90 | exports.buy = buy; | ||
91 | exports.advice = advice; | ||
92 | exports.report = report; | ||
93 |
lib/reverse-report.js
File was created | 1 | "use strict"; | |
2 | |||
3 | const xmlrpc = require('xmlrpc'); | ||
4 | |||
5 | const config = require('komodo-sdk/config'); | ||
6 | const logger = require('komodo-sdk/logger'); | ||
7 | const matrix = require('komodo-sdk/matrix'); | ||
8 | const pull = require('komodo-sdk/gateway/pull'); | ||
9 | |||
10 | const st24 = require('./st24'); | ||
11 | const partner = require('./partner'); | ||
12 | const partnerRc = require('./partner-rc.json'); | ||
13 | |||
14 | function create() { | ||
15 | const server = xmlrpc.createServer({ | ||
16 | port: config.reverse_report_port | ||
17 | }); | ||
18 | |||
19 | logger.info('Reverse report server listen on port ' + config.reverse_report_port); | ||
20 | |||
21 | server.on('NotFound', function (method, params) { | ||
22 | logger.warn('REVERSEREPORT: Unknown method recevied on XMLRPC server', {method: method, params: params}); | ||
23 | }); | ||
24 | |||
25 | server.on('topUpReport', function (err, params, callback) { | ||
26 | |||
27 | logger.info('REVERSEREPORT: Got XMLRPC topUpReport request from partner', {method: 'topUpReport', params: params}); | ||
28 | matrix.last_topupReport_params = params; | ||
29 | |||
30 | const paramsCount = params.length; | ||
31 | for (let i = 0; i < paramsCount; i++) { | ||
32 | let value = params[i]; | ||
33 | |||
34 | partner.report({ | ||
35 | trx_id: value.REQUESTID, | ||
36 | rc: partnerRc[value.RESPONSECODE] || '40', | ||
37 | message: value.MESSAGE, | ||
38 | sn: value.SN || st24.extractSnFromMessage(value.MESSAGE), | ||
39 | amount: value.PRICE || st24.extractPriceFromMsg(value.MESSAGE), | ||
40 | raw: value, | ||
41 | misc: { | ||
42 | } | ||
43 | }); | ||
44 | } | ||
45 | |||
46 | callback(null, 'ACK REPORT OK'); | ||
47 | }) | ||
48 | } | ||
49 | |||
50 | create(); | ||
51 |
lib/st24.js
File was created | 1 | "use strict"; | |
2 | |||
3 | function extractSnFromMessage(msg) { | ||
4 | if (!msg || typeof msg !== 'string') { | ||
5 | return; | ||
6 | } | ||
7 | |||
8 | let match = msg.match(/^SN=(.*?);/); | ||
9 | if (!match || match.length < 2) { | ||
10 | return; | ||
11 | } | ||
12 | |||
13 | return match[1]; | ||
14 | } | ||
15 | |||
16 | function extractPriceFromMsg(msg) { | ||
17 | if (!msg || typeof msg !== 'string') { | ||
18 | return; | ||
19 | } | ||
20 | |||
21 | let match = msg.match(/\d,HRG=(.*?),ID=/); | ||
22 | if (!match || match.length < 2) { | ||
23 | return; | ||
24 | } | ||
25 | |||
26 | if (!match[1]) { | ||
27 | return; | ||
28 | } | ||
29 | |||
30 | return parseInt(match[1].replace(/\./g, '')); | ||
31 | } | ||
32 | |||
33 | |||
34 | exports.extractSnFromMessage = extractSnFromMessage; | ||
35 | exports.extractPriceFromMsg = extractPriceFromMsg; | ||
36 |
package.json
File was created | 1 | { | |
2 | "name": "komodo-gw-st24", | ||
3 | "version": "0.1.0", | ||
4 | "description": "Komodo Gateway to ST24 XML-RPC", | ||
5 | "main": "index.js", | ||
6 | "scripts": { | ||
7 | "test": "mocha", | ||
8 | "postversion": "git push && git push --tags" | ||
9 | }, | ||
10 | "repository": { | ||
11 | "type": "git", | ||
12 | "url": "git@gitlab.kodesumber.com:komodo/komodo-gw-st24.git" | ||
13 | }, | ||
14 | "keywords": [ | ||
15 | "komodo", | ||
16 | "tektrans", | ||
17 | "ppob", | ||
18 | "st24", | ||
19 | "xmlrpc", | ||
20 | "xml-rpc" | ||
21 | ], | ||
22 | "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", | ||
23 | "license": "ISC", | ||
24 | "dependencies": { | ||
25 | "komodo-sdk": "git+http://gitlab.kodesumber.com/komodo/komodo-sdk.git", | ||
26 | "xmlrpc": "^1.3.2" | ||
27 | } | ||
28 | } | ||
29 |