Commit fb2ba8c5da083cb5d9558773bf7ffb2a0a81fcaf
1 parent
d0cd029e97
Exists in
master
ready to run
Showing 9 changed files with 247 additions and 0 deletions Side-by-side Diff
.gitignore
README
... | ... | @@ -0,0 +1 @@ |
1 | +Komodo gateway to ST24 XML-RPC |
config.sample.json
... | ... | @@ -0,0 +1,21 @@ |
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 | +} |
index.js
lib/partner-rc.json
lib/partner.js
... | ... | @@ -0,0 +1,92 @@ |
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; |
lib/reverse-report.js
... | ... | @@ -0,0 +1,50 @@ |
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(); |
lib/st24.js
... | ... | @@ -0,0 +1,35 @@ |
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; |
package.json
... | ... | @@ -0,0 +1,28 @@ |
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 | +} |