Commit 5735fb277d878e6c4735b9cbc587a7badf3fcfcc

Authored by Adhidarma Hadiwinoto
1 parent 4ce3f5feeb
Exists in master

config.responsecode_tag

Showing 3 changed files with 6 additions and 2 deletions Inline Diff

1 { 1 {
2 "handler_name": "ST24", 2 "handler_name": "ST24",
3 "products": [ 3 "products": [
4 "PLEASE_CHANGE_ME" 4 "PLEASE_CHANGE_ME"
5 ], 5 ],
6 "core_url": "http://127.0.0.1:32972/apikey/PLEASE_CHANGE_ME", 6 "core_url": "http://127.0.0.1:32972/apikey/PLEASE_CHANGE_ME",
7 "pull_interval_ms": 1000, 7 "pull_interval_ms": 1000,
8 "partner": { 8 "partner": {
9 "url": "https://PLEASE_CHANGE_ME:6789/", 9 "url": "https://PLEASE_CHANGE_ME:6789/",
10 "msisdn": "PLEASE_CHANGE_ME", 10 "msisdn": "PLEASE_CHANGE_ME",
11 "pin": "PLEASE_CHANGE_ME", 11 "pin": "PLEASE_CHANGE_ME",
12 "use_sslv3": false 12 "use_sslv3": false
13 }, 13 },
14 "push_server": { 14 "push_server": {
15 "apikey": "PLEASE_CHANGE_ME", 15 "apikey": "PLEASE_CHANGE_ME",
16 "advice": { 16 "advice": {
17 "port": 14283, 17 "port": 14283,
18 "url": "http://PLEASE_CHANGE_ME:14283/apikey/PLEASE_CHANGE_ME/advice" 18 "url": "http://PLEASE_CHANGE_ME:14283/apikey/PLEASE_CHANGE_ME/advice"
19 } 19 }
20 }, 20 },
21 "remote_products": { 21 "remote_products": {
22 "PLEASE_CHANGE_ME": "PLEASE_CHANGE_ME" 22 "PLEASE_CHANGE_ME": "PLEASE_CHANGE_ME"
23 }, 23 },
24 "reverse_report_port": 27440, 24 "reverse_report_port": 27440,
25 "control_panel": { 25 "control_panel": {
26 "listen_port": 27441 26 "listen_port": 27441
27 }, 27 },
28 "advice_max_age_ms": 0, 28 "advice_max_age_ms": 0,
29 "advice_is_topuprequest": false, 29 "advice_is_topuprequest": false,
30 "responsecode_tag": null,
30 "do_not_verbose_log_report": true, 31 "do_not_verbose_log_report": true,
31 "force_all_to_pending": false 32 "force_all_to_pending": false
32 } 33 }
33 34
examples/kopnus/config.json
1 { 1 {
2 "auto_resend": { 2 "auto_resend": {
3 "delay_ms": 60000, 3 "delay_ms": 60000,
4 "max_retry": 1 4 "max_retry": 1
5 }, 5 },
6 "sn_pattern": { 6 "sn_pattern": {
7 "pattern": "SN=(.*?)\\.", 7 "pattern": "SN=(.*?)\\.",
8 "match_idx": 1 8 "match_idx": 1
9 }, 9 },
10 "remote_products": { 10 "remote_products": {
11 "S10": "3043051", 11 "S10": "3043051",
12 "S20": "3023092" 12 "S20": "3023092"
13 }, 13 },
14 "advice_max_age_ms": 240000, 14 "advice_max_age_ms": 240000,
15 "advice_is_topuprequest": true 15 "advice_is_topuprequest": true,
16 "responsecode_tag": "RESPONCODE"
16 } 17 }
17 18
1 "use strict"; 1 "use strict";
2 2
3 process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 3 process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
4 4
5 const fs = require('fs'); 5 const fs = require('fs');
6 const url = require('url'); 6 const url = require('url');
7 const https = require('https'); 7 const https = require('https');
8 const xmlrpc = require('xmlrpc'); 8 const xmlrpc = require('xmlrpc');
9 const moment = require('moment'); 9 const moment = require('moment');
10 const stringify = require("json-stringify-pretty-compact"); 10 const stringify = require("json-stringify-pretty-compact");
11 11
12 const config = require('komodo-sdk/config'); 12 const config = require('komodo-sdk/config');
13 const logger = require('komodo-sdk/logger'); 13 const logger = require('komodo-sdk/logger');
14 const matrix = require('komodo-sdk/matrix'); 14 const matrix = require('komodo-sdk/matrix');
15 const pull = require('komodo-sdk/gateway/pull'); 15 const pull = require('komodo-sdk/gateway/pull');
16 const resendDelay = require('komodo-sdk/gateway/resend-delay'); 16 const resendDelay = require('komodo-sdk/gateway/resend-delay');
17 17
18 const st24 = require('./st24'); 18 const st24 = require('./st24');
19 19
20 const partnerRc = fs.existsSync(__dirname + '/../rc-local.json') ? require('../rc-local.json') : require('./partner-rc.json'); 20 const partnerRc = fs.existsSync(__dirname + '/../rc-local.json') ? require('../rc-local.json') : require('./partner-rc.json');
21 logger.verbose('Partner RC dictionary loaded', {partner_rc: partnerRc}); 21 logger.verbose('Partner RC dictionary loaded', {partner_rc: partnerRc});
22 22
23 if (config.partner.use_sslv3) { 23 if (config.partner.use_sslv3) {
24 https.globalAgent.options.secureProtocol = 'SSLv3_method'; 24 https.globalAgent.options.secureProtocol = 'SSLv3_method';
25 } 25 }
26 26
27 function createXmlRpcClient(endpoint) { 27 function createXmlRpcClient(endpoint) {
28 const partnerUrl = url.parse(endpoint); 28 const partnerUrl = url.parse(endpoint);
29 const clientOptions = { 29 const clientOptions = {
30 host: partnerUrl.hostname, 30 host: partnerUrl.hostname,
31 port: partnerUrl.port, 31 port: partnerUrl.port,
32 path: partnerUrl.pathname 32 path: partnerUrl.pathname
33 }; 33 };
34 34
35 logger.verbose('Creating XML-RPC client using ' + partnerUrl.protocol, clientOptions); 35 logger.verbose('Creating XML-RPC client using ' + partnerUrl.protocol, clientOptions);
36 36
37 return (partnerUrl.protocol === 'https:') ? xmlrpc.createSecureClient(clientOptions) : xmlrpc.createClient(clientOptions); 37 return (partnerUrl.protocol === 'https:') ? xmlrpc.createSecureClient(clientOptions) : xmlrpc.createClient(clientOptions);
38 } 38 }
39 39
40 function buy(task) { 40 function buy(task) {
41 _topUpRequest(task); 41 _topUpRequest(task);
42 } 42 }
43 43
44 function _topUpRequest(task, isAdvice) { 44 function _topUpRequest(task, isAdvice) {
45 const params = { 45 const params = {
46 MSISDN: config.partner.msisdn || config.partner.userid, 46 MSISDN: config.partner.msisdn || config.partner.userid,
47 REQUESTID: task.trx_id, 47 REQUESTID: task.trx_id,
48 PIN: config.partner.pin || config.partner.password, 48 PIN: config.partner.pin || config.partner.password,
49 NOHP: task.destination, 49 NOHP: task.destination,
50 NOM: task.remote_product 50 NOM: task.remote_product
51 }; 51 };
52 52
53 const xmlrpcMethod = 'topUpRequest'; 53 const xmlrpcMethod = 'topUpRequest';
54 logger.info('Preparing XMLRPC request', {method: xmlrpcMethod, params: params, partnerUrl: config.partner.url}); 54 logger.info('Preparing XMLRPC request', {method: xmlrpcMethod, params: params, partnerUrl: config.partner.url});
55 55
56 const client = createXmlRpcClient(config.partner.url); 56 const client = createXmlRpcClient(config.partner.url);
57 client.methodCall(xmlrpcMethod, [ params ], function (err, value) { 57 client.methodCall(xmlrpcMethod, [ params ], function (err, value) {
58 58
59 if (err) { 59 if (err) {
60 60
61 let msg = 'XMLRPC Client Error: ' + err; 61 let msg = 'XMLRPC Client Error: ' + err;
62 let rc = '68'; 62 let rc = '68';
63 63
64 if ( 64 if (
65 !isAdvice && 65 !isAdvice &&
66 ( 66 (
67 err.code === 'ECONNREFUSED' 67 err.code === 'ECONNREFUSED'
68 || err.code === 'EHOSTUNREACH' 68 || err.code === 'EHOSTUNREACH'
69 || (err.code === 'ETIMEDOUT' && err.syscall === "connect") 69 || (err.code === 'ETIMEDOUT' && err.syscall === "connect")
70 || (err.code === 'EPROTO' && err.syscall === "write") 70 || (err.code === 'EPROTO' && err.syscall === "write")
71 ) 71 )
72 ) { 72 ) {
73 rc = '91'; 73 rc = '91';
74 } 74 }
75 75
76 logger.warn(msg, {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, err: err}); 76 logger.warn(msg, {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, err: err});
77 report({ 77 report({
78 trx_id: task.trx_id, 78 trx_id: task.trx_id,
79 rc: rc, 79 rc: rc,
80 message: 'INTERNAL: ' + msg, 80 message: 'INTERNAL: ' + msg,
81 misc: { 81 misc: {
82 task: task 82 task: task
83 } 83 }
84 }); 84 });
85 85
86 return; 86 return;
87 } 87 }
88 88
89 logger.info('Got XMLRPC response from partner for', {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, response: value}); 89 logger.info('Got XMLRPC response from partner for', {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, response: value});
90 matrix.last_topupRequest_ack = value; 90 matrix.last_topupRequest_ack = value;
91 91
92 const RESPONSECODE_TAG = config.responsecode_tag ? config.responsecode_tag : 'RESPONSECODE';
93
92 report({ 94 report({
93 trx_id: task.trx_id, 95 trx_id: task.trx_id,
94 rc: partnerRc[value.RESPONSECODE] || '40', 96 rc: partnerRc[value[RESPONSECODE_TAG]] || '40',
95 message: stringify(value), 97 message: stringify(value),
96 sn: (value.SN || '').replace(/;$/, '') || st24.extractSnFromMessage(value.MESSAGE), 98 sn: (value.SN || '').replace(/;$/, '') || st24.extractSnFromMessage(value.MESSAGE),
97 amount: value.PRICE || st24.extractPriceFromMsg(value.MESSAGE), 99 amount: value.PRICE || st24.extractPriceFromMsg(value.MESSAGE),
98 raw: value, 100 raw: value,
99 misc: { 101 misc: {
100 task: task 102 task: task
101 } 103 }
102 }); 104 });
103 }); 105 });
104 } 106 }
105 107
106 function _topUpInquiry(task) { 108 function _topUpInquiry(task) {
107 const params = { 109 const params = {
108 REQUESTID: task.trx_id, 110 REQUESTID: task.trx_id,
109 MSISDN: config.partner.msisdn || config.partner.userid, 111 MSISDN: config.partner.msisdn || config.partner.userid,
110 PIN: config.partner.pin || config.partner.password, 112 PIN: config.partner.pin || config.partner.password,
111 NOHP: task.destination 113 NOHP: task.destination
112 }; 114 };
113 115
114 const xmlrpcMethod = 'topUpInquiry'; 116 const xmlrpcMethod = 'topUpInquiry';
115 logger.info('Preparing XMLRPC request', {method: xmlrpcMethod, params: params, partnerUrl: config.partner.url}); 117 logger.info('Preparing XMLRPC request', {method: xmlrpcMethod, params: params, partnerUrl: config.partner.url});
116 118
117 const client = createXmlRpcClient(config.partner.url); 119 const client = createXmlRpcClient(config.partner.url);
118 client.methodCall(xmlrpcMethod, [ params ], function (err, value) { 120 client.methodCall(xmlrpcMethod, [ params ], function (err, value) {
119 121
120 if (err) { 122 if (err) {
121 123
122 const msg = 'XMLRPC Client Error: ' + err; 124 const msg = 'XMLRPC Client Error: ' + err;
123 125
124 logger.warn(msg, {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, err: err}); 126 logger.warn(msg, {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, err: err});
125 report({ 127 report({
126 trx_id: task.trx_id, 128 trx_id: task.trx_id,
127 rc: '68', 129 rc: '68',
128 message: 'INTERNAL: ' + msg, 130 message: 'INTERNAL: ' + msg,
129 misc: { 131 misc: {
130 task: task 132 task: task
131 } 133 }
132 }); 134 });
133 135
134 return; 136 return;
135 } 137 }
136 138
137 logger.info('Got XMLRPC response from partner for', {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, response: value}); 139 logger.info('Got XMLRPC response from partner for', {method: xmlrpcMethod, trx_id: task.trx_id, destination: task.destination, response: value});
138 //matrix.last_topupRequest_ack = value; 140 //matrix.last_topupRequest_ack = value;
139 141
140 report({ 142 report({
141 trx_id: task.trx_id, 143 trx_id: task.trx_id,
142 rc: partnerRc[value.RESPONSECODE] || '40', 144 rc: partnerRc[value.RESPONSECODE] || '40',
143 message: stringify(value), 145 message: stringify(value),
144 sn: (value.SN || '').replace(/;$/, '') || st24.extractSnFromMessage(value.MESSAGE, config.sn_pattern), 146 sn: (value.SN || '').replace(/;$/, '') || st24.extractSnFromMessage(value.MESSAGE, config.sn_pattern),
145 amount: value.PRICE || st24.extractPriceFromMsg(value.MESSAGE), 147 amount: value.PRICE || st24.extractPriceFromMsg(value.MESSAGE),
146 raw: value, 148 raw: value,
147 misc: { 149 misc: {
148 task: task 150 task: task
149 } 151 }
150 }); 152 });
151 }); 153 });
152 } 154 }
153 155
154 function advice(task) { 156 function advice(task) {
155 if (config && config.advice_is_not_allowed) { 157 if (config && config.advice_is_not_allowed) {
156 return; 158 return;
157 } 159 }
158 160
159 if (config && config.advice_max_age_ms) { 161 if (config && config.advice_max_age_ms) {
160 if (moment() - moment(task.created) > config.advice_max_age_ms) { 162 if (moment() - moment(task.created) > config.advice_max_age_ms) {
161 logger.verbose('Ignoring advice request because of expired task', {trx_id: task.trx_id, destination: task.destination, product: task.product, created: task.created, max_age: config.advice_max_age_ms}); 163 logger.verbose('Ignoring advice request because of expired task', {trx_id: task.trx_id, destination: task.destination, product: task.product, created: task.created, max_age: config.advice_max_age_ms});
162 return; 164 return;
163 } 165 }
164 } 166 }
165 167
166 if (config && config.advice_is_topuprequest) { 168 if (config && config.advice_is_topuprequest) {
167 _topUpRequest(task, true); 169 _topUpRequest(task, true);
168 } 170 }
169 else { 171 else {
170 _topUpInquiry(task); 172 _topUpInquiry(task);
171 } 173 }
172 } 174 }
173 175
174 function report(data) { 176 function report(data) {
175 if (!data) { 177 if (!data) {
176 return; 178 return;
177 } 179 }
178 180
179 if (config && config.force_all_to_pending) { 181 if (config && config.force_all_to_pending) {
180 data.rc = '68'; 182 data.rc = '68';
181 } 183 }
182 184
183 matrix.last_report_to_core = data; 185 matrix.last_report_to_core = data;
184 pull.report(data); 186 pull.report(data);
185 187
186 if (!resendDelay.isEnabled()) return; 188 if (!resendDelay.isEnabled()) return;
187 189
188 if (data.task && data.rc && data.rc === '68') { 190 if (data.task && data.rc && data.rc === '68') {
189 resendDelay.register(data.task, advice); 191 resendDelay.register(data.task, advice);
190 } 192 }
191 else { 193 else {
192 resendDelay.cancel(data.trx_id); 194 resendDelay.cancel(data.trx_id);
193 } 195 }
194 } 196 }
195 197
196 exports.buy = buy; 198 exports.buy = buy;
197 exports.advice = advice; 199 exports.advice = advice;
198 exports.report = report; 200 exports.report = report;
199 201