Commit cbcf0d22401591c0d277b7d4e31c0d2c4286641d

Authored by Adhidarma Hadiwinoto
1 parent 7dcdea777d
Exists in master

Gagalkan transaksi yang remote product blm terdefinisi

Showing 1 changed file with 9 additions and 0 deletions Inline Diff

lib/partner-mkios.js
1 "use strict"; 1 "use strict";
2 2
3 const moment = require('moment'); 3 const moment = require('moment');
4 4
5 const Modem = require('./modem'); 5 const Modem = require('./modem');
6 6
7 const pullgw = require('komodo-sdk/gateway/pull'); 7 const pullgw = require('komodo-sdk/gateway/pull');
8 8
9 const config = require('komodo-sdk/config'); 9 const config = require('komodo-sdk/config');
10 const logger = require('komodo-sdk/logger'); 10 const logger = require('komodo-sdk/logger');
11 const matrix = require('komodo-sdk/matrix'); 11 const matrix = require('komodo-sdk/matrix');
12 12
13 const modemDashboard = require('./modem-dashboard'); 13 const modemDashboard = require('./modem-dashboard');
14 14
15 if (!config || !config.partner || !config.partner.pin) { 15 if (!config || !config.partner || !config.partner.pin) {
16 logger.warn('Undefined PIN'); 16 logger.warn('Undefined PIN');
17 process.exit(1); 17 process.exit(1);
18 } 18 }
19 19
20 matrix.modem = {}; 20 matrix.modem = {};
21 matrix.not_ready = true; 21 matrix.not_ready = true;
22 matrix.stock = {}; 22 matrix.stock = {};
23 23
24 const db = require('./local-db').getConnection(); 24 const db = require('./local-db').getConnection();
25 const pendingArchive = require('./pending-archive'); 25 const pendingArchive = require('./pending-archive');
26 const patternMatcher = require('./pattern-rule-matcher'); 26 const patternMatcher = require('./pattern-rule-matcher');
27 const smsHandler = require('./sms-handler'); 27 const smsHandler = require('./sms-handler');
28 28
29 const modem = new Modem(config.partner.modem.dev); 29 const modem = new Modem(config.partner.modem.dev);
30 30
31 const resumeHandlers = {}; 31 const resumeHandlers = {};
32 32
33 let last_trx_id = null; 33 let last_trx_id = null;
34 34
35 modem.on('open', function() { 35 modem.on('open', function() {
36 logger.info('Modem opened'); 36 logger.info('Modem opened');
37 37
38 const ussd_command = '*776*' + config.partner.pin + '#'; 38 const ussd_command = '*776*' + config.partner.pin + '#';
39 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) { 39 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) {
40 if (err) { 40 if (err) {
41 logger.warn('Error inserting ussd command (stock check) to local database', {err: err}); 41 logger.warn('Error inserting ussd command (stock check) to local database', {err: err});
42 } 42 }
43 }); 43 });
44 44
45 modem.sendUSSD(ussd_command, function() {}); 45 modem.sendUSSD(ussd_command, function() {});
46 }) 46 })
47 47
48 modem.on('imsi', function(imsi) { 48 modem.on('imsi', function(imsi) {
49 logger.verbose('IMSI: ' + imsi); 49 logger.verbose('IMSI: ' + imsi);
50 matrix.modem.imsi = imsi; 50 matrix.modem.imsi = imsi;
51 }) 51 })
52 52
53 53
54 function onIncomingSMS(sms) { 54 function onIncomingSMS(sms) {
55 logger.info('Incoming SMS', {sms: sms}); 55 logger.info('Incoming SMS', {sms: sms});
56 db.run("INSERT INTO sms VALUES (?, ?, 'IN', ?, ?, ?)", sms.created, moment(sms.created).format('YYYY-MM-DD'), matrix.modem.imsi, sms.sender, sms.msg, function(err) { 56 db.run("INSERT INTO sms VALUES (?, ?, 'IN', ?, ?, ?)", sms.created, moment(sms.created).format('YYYY-MM-DD'), matrix.modem.imsi, sms.sender, sms.msg, function(err) {
57 if (err) { 57 if (err) {
58 logger.warn('Error inserting sms to local database', {err: err}); 58 logger.warn('Error inserting sms to local database', {err: err});
59 } 59 }
60 }); 60 });
61 61
62 if (!smsHandler.isAllowedSender(sms.sender)) { 62 if (!smsHandler.isAllowedSender(sms.sender)) {
63 logger.verbose('Ignoring SMS from unknown sender', {sender: sms.sender}); 63 logger.verbose('Ignoring SMS from unknown sender', {sender: sms.sender});
64 return; 64 return;
65 } 65 }
66 66
67 const stocks = smsHandler.getMultiStockBalance(sms.msg); 67 const stocks = smsHandler.getMultiStockBalance(sms.msg);
68 if (stocks && Array.isArray(stocks) && stocks.length) { 68 if (stocks && Array.isArray(stocks) && stocks.length) {
69 stocks.forEach(function(stock) { 69 stocks.forEach(function(stock) {
70 const vals = stock.split('='); 70 const vals = stock.split('=');
71 updateStock(vals[0], vals[1]); 71 updateStock(vals[0], vals[1]);
72 }) 72 })
73 } 73 }
74 else { 74 else {
75 const stock = smsHandler.getStockBalance(sms.msg); 75 const stock = smsHandler.getStockBalance(sms.msg);
76 if (stock.name && stock.balance) { 76 if (stock.name && stock.balance) {
77 updateStock(stock.name, stock.balance); 77 updateStock(stock.name, stock.balance);
78 } 78 }
79 } 79 }
80 80
81 const destination = smsHandler.getDestination(sms.msg); 81 const destination = smsHandler.getDestination(sms.msg);
82 if (!destination) { 82 if (!destination) {
83 logger.verbose('Ignoring SMS with unknown trx destination'); 83 logger.verbose('Ignoring SMS with unknown trx destination');
84 return; 84 return;
85 } 85 }
86 86
87 const product = smsHandler.getProduct(sms.msg); 87 const product = smsHandler.getProduct(sms.msg);
88 if (!product) { 88 if (!product) {
89 logger.verbose('Ignoring SMS with unknown trx product'); 89 logger.verbose('Ignoring SMS with unknown trx product');
90 return; 90 return;
91 } 91 }
92 92
93 const trx_date = smsHandler.getTrxDate(sms.msg); 93 const trx_date = smsHandler.getTrxDate(sms.msg);
94 if (!trx_date) { 94 if (!trx_date) {
95 logger.verbose('Ignoring SMS with unknown trx date'); 95 logger.verbose('Ignoring SMS with unknown trx date');
96 return; 96 return;
97 } 97 }
98 98
99 logger.verbose('SMS message parsed and extracted', {destination: destination, product: product, trx_date: trx_date}); 99 logger.verbose('SMS message parsed and extracted', {destination: destination, product: product, trx_date: trx_date});
100 pendingArchive.get(destination, product, trx_date, function(err, trx_id) { 100 pendingArchive.get(destination, product, trx_date, function(err, trx_id) {
101 if (!trx_id) { 101 if (!trx_id) {
102 logger.verbose('No pending trx suits with SMS', {destination: destination, product: product, trx_date: trx_date}); 102 logger.verbose('No pending trx suits with SMS', {destination: destination, product: product, trx_date: trx_date});
103 return; 103 return;
104 } 104 }
105 105
106 deleteResumeHandler(trx_id); 106 deleteResumeHandler(trx_id);
107 pendingArchive.remove(trx_id); 107 pendingArchive.remove(trx_id);
108 108
109 report({ 109 report({
110 trx_id: trx_id, 110 trx_id: trx_id,
111 rc: smsHandler.getRc(sms.msg) || '68', 111 rc: smsHandler.getRc(sms.msg) || '68',
112 sn: smsHandler.getSn(sms.msg), 112 sn: smsHandler.getSn(sms.msg),
113 message: 'SMS: ' + sms.msg 113 message: 'SMS: ' + sms.msg
114 }) 114 })
115 }) 115 })
116 116
117 } 117 }
118 modem.on('incoming sms', onIncomingSMS); 118 modem.on('incoming sms', onIncomingSMS);
119 119
120 modem.on('signal strength', function(signal_strength) { 120 modem.on('signal strength', function(signal_strength) {
121 matrix.modem.signal_strength = signal_strength; 121 matrix.modem.signal_strength = signal_strength;
122 logger.verbose('Signal strength: ' + signal_strength); 122 logger.verbose('Signal strength: ' + signal_strength);
123 }) 123 })
124 124
125 function onUSSDResponse(data) { 125 function onUSSDResponse(data) {
126 logger.verbose('Got USSD response', {response: data}); 126 logger.verbose('Got USSD response', {response: data});
127 127
128 db.run("INSERT INTO ussd VALUES (?, ?, 'IN', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, data, function(err) { 128 db.run("INSERT INTO ussd VALUES (?, ?, 'IN', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, data, function(err) {
129 if (err) { 129 if (err) {
130 logger.warn('Error inserting ussd response to local database', {err: err}); 130 logger.warn('Error inserting ussd response to local database', {err: err});
131 } 131 }
132 }); 132 });
133 133
134 134
135 if (!last_trx_id) return; 135 if (!last_trx_id) return;
136 136
137 137
138 const rc = getRcFromMessage(data, config.ussd_parser.rc) || '68'; 138 const rc = getRcFromMessage(data, config.ussd_parser.rc) || '68';
139 if (rc !== '68') { 139 if (rc !== '68') {
140 onTrxFinish(last_trx_id); 140 onTrxFinish(last_trx_id);
141 } 141 }
142 142
143 report({ 143 report({
144 trx_id: last_trx_id, 144 trx_id: last_trx_id,
145 rc: rc, 145 rc: rc,
146 sn: getSnFromMessage(data), 146 sn: getSnFromMessage(data),
147 message: data 147 message: data
148 }); 148 });
149 149
150 const stock_name = getStockProductFromMessage(data); 150 const stock_name = getStockProductFromMessage(data);
151 const stock_balance = getStockBalanceFromMesssage(data); 151 const stock_balance = getStockBalanceFromMesssage(data);
152 152
153 updateStock(stock_name, stock_balance); 153 updateStock(stock_name, stock_balance);
154 last_trx_id = null; 154 last_trx_id = null;
155 } 155 }
156 156
157 modem.on('ussd response', onUSSDResponse); 157 modem.on('ussd response', onUSSDResponse);
158 158
159 159
160 logger.info('Opening MODEM'); 160 logger.info('Opening MODEM');
161 modem.open(function(err) { 161 modem.open(function(err) {
162 if (err) { 162 if (err) {
163 logger.warn('Error opening modem port', {err: err}); 163 logger.warn('Error opening modem port', {err: err});
164 process.exit(1); 164 process.exit(1);
165 } 165 }
166 166
167 logger.info('Modem open successfully'); 167 logger.info('Modem open successfully');
168 matrix.not_ready = false; 168 matrix.not_ready = false;
169 }) 169 })
170 170
171 function updateStock(stock_name, stock_balance) { 171 function updateStock(stock_name, stock_balance) {
172 if (stock_name && (stock_balance !== undefined || stock_balance !== null)) { 172 if (stock_name && (stock_balance !== undefined || stock_balance !== null)) {
173 logger.verbose('Updating stock', {stock_name: stock_name, stock_balance: stock_balance}); 173 logger.verbose('Updating stock', {stock_name: stock_name, stock_balance: stock_balance});
174 174
175 const new_stock_name = config && config.remote_product_alias && config.remote_product_alias[stock_name] ? config.remote_product_alias[stock_name] : stock_name; 175 const new_stock_name = config && config.remote_product_alias && config.remote_product_alias[stock_name] ? config.remote_product_alias[stock_name] : stock_name;
176 176
177 matrix.stock[new_stock_name] = Number(stock_balance); 177 matrix.stock[new_stock_name] = Number(stock_balance);
178 178
179 logger.verbose('Stock balance updated', {stock: matrix.stock}); 179 logger.verbose('Stock balance updated', {stock: matrix.stock});
180 } 180 }
181 } 181 }
182 182
183 function getRcFromMessage(msg, rules) { 183 function getRcFromMessage(msg, rules) {
184 if (!rules || !Array.isArray(rules)) { 184 if (!rules || !Array.isArray(rules)) {
185 return '68'; 185 return '68';
186 } 186 }
187 187
188 for (let rule of rules) { 188 for (let rule of rules) {
189 if (!rule.pattern) return '68'; 189 if (!rule.pattern) return '68';
190 190
191 const re = new RegExp(rule.pattern); 191 const re = new RegExp(rule.pattern);
192 if (msg.search(re) >= 0) { 192 if (msg.search(re) >= 0) {
193 return rule.rc ? rule.rc : '68'; 193 return rule.rc ? rule.rc : '68';
194 } 194 }
195 } 195 }
196 196
197 return '68'; 197 return '68';
198 } 198 }
199 199
200 function getPatternMatchFromMessage(msg, rules) { 200 function getPatternMatchFromMessage(msg, rules) {
201 if (!rules || !Array.isArray(rules)) { 201 if (!rules || !Array.isArray(rules)) {
202 return; 202 return;
203 } 203 }
204 204
205 for (let rule of rules) { 205 for (let rule of rules) {
206 if (!rule.pattern) return; 206 if (!rule.pattern) return;
207 207
208 const re = new RegExp(rule.pattern); 208 const re = new RegExp(rule.pattern);
209 const matches = msg.match(re); 209 const matches = msg.match(re);
210 210
211 if (matches && matches.length >= 2) { 211 if (matches && matches.length >= 2) {
212 return matches[1]; 212 return matches[1];
213 } 213 }
214 } 214 }
215 } 215 }
216 216
217 function getSnFromMessage(msg, rules) { 217 function getSnFromMessage(msg, rules) {
218 return patternMatcher(msg, config.ussd_parser.sn); 218 return patternMatcher(msg, config.ussd_parser.sn);
219 } 219 }
220 220
221 function getStockProductFromMessage(msg, rules) { 221 function getStockProductFromMessage(msg, rules) {
222 return patternMatcher(msg, config.ussd_parser.stock.product); 222 return patternMatcher(msg, config.ussd_parser.stock.product);
223 } 223 }
224 224
225 function getStockBalanceFromMesssage(msg, rules) { 225 function getStockBalanceFromMesssage(msg, rules) {
226 return patternMatcher(msg, config.ussd_parser.stock.balance); 226 return patternMatcher(msg, config.ussd_parser.stock.balance);
227 } 227 }
228 228
229 function suspendPull(trx_id) { 229 function suspendPull(trx_id) {
230 logger.verbose('Set modem to not ready so no other task can be entered and registering delayed resume'); 230 logger.verbose('Set modem to not ready so no other task can be entered and registering delayed resume');
231 matrix.not_ready = true; 231 matrix.not_ready = true;
232 232
233 resumeHandlers['trx' + trx_id] = setTimeout(function() { 233 resumeHandlers['trx' + trx_id] = setTimeout(function() {
234 logger.verbose('Resuming supsended modem', {trx_id: trx_id}); 234 logger.verbose('Resuming supsended modem', {trx_id: trx_id});
235 matrix.not_ready = false; 235 matrix.not_ready = false;
236 report({ 236 report({
237 trx_id: trx_id, 237 trx_id: trx_id,
238 rc: '68', 238 rc: '68',
239 message: 'USSD timeout' 239 message: 'USSD timeout'
240 }) 240 })
241 }, Number(config.partner.modem.suspend_time_ms) || 20 * 1000 ) 241 }, Number(config.partner.modem.suspend_time_ms) || 20 * 1000 )
242 } 242 }
243 243
244 function deleteResumeHandler(trx_id) { 244 function deleteResumeHandler(trx_id) {
245 if (resumeHandlers['trx' + trx_id]) { 245 if (resumeHandlers['trx' + trx_id]) {
246 logger.verbose('Unregistering delayed resume of trx id ' + trx_id); 246 logger.verbose('Unregistering delayed resume of trx id ' + trx_id);
247 clearTimeout(resumeHandlers['trx' + trx_id]); 247 clearTimeout(resumeHandlers['trx' + trx_id]);
248 delete resumeHandlers['trx' + trx_id]; 248 delete resumeHandlers['trx' + trx_id];
249 } 249 }
250 } 250 }
251 251
252 function onTrxFinish(trx_id) { 252 function onTrxFinish(trx_id) {
253 deleteResumeHandler(trx_id); 253 deleteResumeHandler(trx_id);
254 pendingArchive.remove(trx_id); 254 pendingArchive.remove(trx_id);
255 matrix.not_ready = false; 255 matrix.not_ready = false;
256 } 256 }
257 257
258 function buy(task) { 258 function buy(task) {
259 if (task.product === task.remote_product) {
260 report({
261 trx_id: task.trx_id,
262 rc: '40',
263 message: 'INTERNAL: Gagal melakukan transaksi. Kode USSD belum terdefinisi.'
264 });
265 return;
266 }
267
259 suspendPull(task.trx_id); 268 suspendPull(task.trx_id);
260 last_trx_id = task.trx_id; 269 last_trx_id = task.trx_id;
261 270
262 pendingArchive.put(task, function(err) { 271 pendingArchive.put(task, function(err) {
263 if (err) { 272 if (err) {
264 logger.verbose('Error inserting task to pending archive', {trx_id: task.trx_id, destination: task.destination, product: task.product, err: err}); 273 logger.verbose('Error inserting task to pending archive', {trx_id: task.trx_id, destination: task.destination, product: task.product, err: err});
265 onTrxFinish(task.trx_id); 274 onTrxFinish(task.trx_id);
266 report({ 275 report({
267 trx_id: task.trx_id, 276 trx_id: task.trx_id,
268 rc: '40', 277 rc: '40',
269 message: 'INTERNAL: Gagal melakukan transaksi. Mungkin ada transaksi dengan nomor dengan produk yang sama yang masih diproses. Silahkan ulangi beberapa saat lagi.' 278 message: 'INTERNAL: Gagal melakukan transaksi. Mungkin ada transaksi dengan nomor dengan produk yang sama yang masih diproses. Silahkan ulangi beberapa saat lagi.'
270 }); 279 });
271 280
272 return; 281 return;
273 } 282 }
274 283
275 const ussd_command = task.remote_product.split(',')[0].replace("<DESTINATION>", task.destination).replace("<PIN>", config.partner.pin); 284 const ussd_command = task.remote_product.split(',')[0].replace("<DESTINATION>", task.destination).replace("<PIN>", config.partner.pin);
276 logger.verbose('Going to execute USSD', {trx_id: task.trx_id, destination: task.destination, product: task.product, ussd: ussd_command}); 285 logger.verbose('Going to execute USSD', {trx_id: task.trx_id, destination: task.destination, product: task.product, ussd: ussd_command});
277 286
278 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) { 287 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) {
279 if (err) { 288 if (err) {
280 logger.warn('Error inserting ussd command to local database', {err: err}); 289 logger.warn('Error inserting ussd command to local database', {err: err});
281 } 290 }
282 }); 291 });
283 modem.sendUSSD(ussd_command, function() {}); 292 modem.sendUSSD(ussd_command, function() {});
284 }) 293 })
285 294
286 295
287 } 296 }
288 297
289 function report(data) { 298 function report(data) {
290 pullgw.report(data); 299 pullgw.report(data);
291 } 300 }
292 301
293 exports.buy = buy; 302 exports.buy = buy;
294 303