Commit cbcf0d22401591c0d277b7d4e31c0d2c4286641d
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 |