/*jshint esversion: 6 */ const TYPE_MONEY_REQUEST = "MoneyRequests", TYPE_CASHIER_REQUEST = "CashierRequests", TYPE_CASHIER_UPI = "CashierUPI"; const cashierUPI = {}; const cashierPubKeys = {}; //For regular users const User = {}; const cashierStatus = {}; User.init = function () { return new Promise((resolve, reject) => { let promises; //Request cashier for token-cash exchange promises = floGlobals.subAdmins.map(cashierID => floCloudAPI.requestGeneralData(TYPE_CASHIER_REQUEST, { senderID: myFloID, receiverID: cashierID, group: "Cashiers", callback: userUI.renderCashierRequests //UI_fn })); //Request received from other Users for token promises.push(floCloudAPI.requestGeneralData(TYPE_MONEY_REQUEST, { receiverID: myFloID, callback: userUI.renderMoneyRequests //UI_fn })); //Check online status of cashiers promises.push(floCloudAPI.requestStatus(Array.from(floGlobals.subAdmins), { callback: (d, e) => { if (e) return console.error(e); for (let i in d) cashierStatus[i] = d[i]; //Add any UI_fn if any if (User.findCashier()) { getRef('topup_wallet_process').classList.remove('hide') getRef('withdraw_wallet_process').classList.remove('hide') document.querySelectorAll('.cashier-status').forEach(elem => elem.classList.add('hide')) } else { getRef('topup_wallet_process').classList.add('hide') getRef('withdraw_wallet_process').classList.add('hide') document.querySelectorAll('.cashier-status').forEach(elem => elem.classList.remove('hide')) } } })) /* promises.push(floCloudAPI.requestObjectData("UPI", { //Is this needed? callback: UI_RENDER_FN })); */ promises.push(User.getCashierUPI()); promises.push(organizeSyncedData('savedUserData')); Promise.all(promises) .then(result => resolve(result)) .catch(error => reject(error)) }) } User.getCashierUPI = function () { return new Promise((resolve) => { Promise.allSettled(floGlobals.subAdmins.map(cashierID => floCloudAPI.requestApplicationData(TYPE_CASHIER_UPI, { senderID: cashierID, mostRecent: true }))).then(result => { for (let r of result) if (r.status === "fulfilled" && Object.keys(r.value).length) { let vc = Object.keys(r.value).sort().pop() cashierUPI[r.value[vc].senderID] = r.value[vc].message.upi; cashierPubKeys[r.value[vc].senderID] = r.value[vc].pubKey; //get pubKey of cashier from messages for encryption } resolve(cashierUPI); }) }) } Object.defineProperty(User, 'cashierRequests', { get: function () { let fk = floCloudAPI.util.filterKey(TYPE_CASHIER_REQUEST, { senderID: myFloID, group: "Cashiers", }); return floGlobals.generalData[fk]; } }); Object.defineProperty(User, 'moneyRequests', { get: function () { let fk = floCloudAPI.util.filterKey(TYPE_MONEY_REQUEST, { receiverID: myFloID, }); return floGlobals.generalData[fk]; } }); User.findCashier = function () { let online = []; for (let c in cashierStatus) if (cashierStatus[c] && cashierUPI[c]) online.push(c); if (!online.length) { if (floGlobals.settings.default_cashier && floGlobals.settings.default_cashier in cashierUPI) return floGlobals.settings.default_cashier; else return null; } else { const random = floCrypto.randInt(0, online.length - 1) return online[random]; } } User.cashToToken = function (cashier, amount, txCode, upiID) { return new Promise((resolve, reject) => { if (!floGlobals.subAdmins.includes(cashier)) return reject("Invalid cashier"); floCloudAPI.sendGeneralData({ mode: "cash-to-token", amount: amount, // upi_txid: upiTxID, upiID, txCode }, TYPE_CASHIER_REQUEST, { receiverID: cashier }).then(result => resolve(result)) .catch(error => reject(error)) }) } User.tokenToCash = function (cashier, amount, blkTxID, upiID) { return new Promise((resolve, reject) => { if (!floGlobals.subAdmins.includes(cashier)) return reject("Invalid cashier"); floCloudAPI.sendGeneralData({ mode: "token-to-cash", amount: amount, token_txid: blkTxID, upi_id: floCrypto.encryptData(upiID, cashierPubKeys[cashier]) }, TYPE_CASHIER_REQUEST, { receiverID: cashier }).then(result => resolve(result)) .catch(error => reject(error)) }) } User.sendToken = function (receiverID, amount, remark = '', options = {}) { return new Promise((resolve, reject) => { floDapps.user.private.then(privateKey => { floTokenAPI.sendToken(privateKey, amount, receiverID, remark, floTokenAPI.currency, options) .then(result => resolve(result)) .catch(error => reject(error)) }).catch(error => { console.log(error); notify('Invalid password', 'error'); reject(error); }) }) } User.requestToken = function (floID, amount, remark = '') { return new Promise((resolve, reject) => { floCloudAPI.sendGeneralData({ amount: amount, remark: remark }, TYPE_MONEY_REQUEST, { receiverID: floID }).then(result => resolve(result)) .catch(error => reject(error)) }) } User.decideRequest = function (request, note) { return new Promise((resolve, reject) => { floCloudAPI.noteApplicationData(request.vectorClock, note, { receiverID: myFloID }).then(result => resolve(result)) .catch(error => reject(error)) }) } const Cashier = {}; var status_conn_id = null; function statusReconnect() { if (status_conn_id) { floCloudAPI.closeRequest(status_conn_id) status_conn_id = null; } floCloudAPI.setStatus() .then(result => status_conn_id = result) .catch(error => console.error(error)) } var status_interval_instance = null; function startStatusInterval() { if (status_interval_instance) { clearInterval(status_interval_instance); status_interval_instance = null; } statusReconnect(); status_interval_instance = setInterval(statusReconnect, 15 * 60 * 1000); } Cashier.init = function () { delegate(getRef('cashier_pending_request_list'), 'click', '.process-cashier-request', e => { const requestID = e.delegateTarget.closest('.cashier-request').dataset.vc; cashierUI.completeRequest(requestID) }) getRef('cashier_requests_selector').addEventListener('change', e => { showChildElement('cashier_requests_wrapper', e.target.value === 'pending' ? 0 : 1) }) return new Promise((resolve, reject) => { let promises = []; //Requests from user to cashier(self) for token-cash exchange promises.push(floCloudAPI.requestGeneralData(TYPE_CASHIER_REQUEST, { receiverID: myFloID, callback: cashierUI.renderRequests //UI_fn })); //Set online status of cashier(self) //promises.push(floCloudAPI.setStatus()); /* promises.push(floCloudAPI.requestObjectData("UPI", { //Is this needed? callback: UI_RENDER_FN })); */ promises.push(User.getCashierUPI()); Promise.all(promises).then(result => { startStatusInterval(); //Set online status of cashier(self) [connection refreshes on interval] resolve(result) }).catch(error => reject(error)); }) } Cashier.updateUPI = function (upi_id) { return new Promise((resolve, reject) => { floCloudAPI.sendApplicationData({ upi: upi_id }, TYPE_CASHIER_UPI) .then(result => resolve(result)) .catch(error => reject(error)) }) } Object.defineProperty(Cashier, 'Requests', { get: function () { let fk = floCloudAPI.util.filterKey(TYPE_CASHIER_REQUEST, { receiverID: myFloID }); console.debug(fk, floGlobals.generalData[fk]); return floGlobals.generalData[fk]; } }); Cashier.finishRequest = function (request, txid) { return new Promise((resolve, reject) => { floCloudAPI.tagApplicationData(request.vectorClock, 'COMPLETED', { receiverID: myFloID }).then(result => { floCloudAPI.noteApplicationData(request.vectorClock, txid, { receiverID: myFloID }).then(result => resolve(result)) .catch(error => reject(error)) }).catch(error => reject(error)) }) } Cashier.rejectRequest = function (request, reason) { return new Promise((resolve, reject) => { floCloudAPI.noteApplicationData(request.vectorClock, "REJECTED:" + reason, { receiverID: myFloID }).then(result => resolve(result)) .catch(error => reject(error)) }) } Cashier.checkIfUpiTxIsValid = function (upiTxID) { return new Promise((resolve, reject) => { let requests = Cashier.Requests; for (let r in requests) if (requests[r].message.mode === "cash-to-token" && requests[r].note) if (requests[r].message.upi_txid === upiTxID) return reject([true, "UPI transaction is already used for another request"]); return resolve(true); }) } Cashier.checkIfTokenTxIsValid = function (tokenTxID, sender, amount) { return new Promise((resolve, reject) => { let requests = Cashier.Requests; for (let r in requests) if (requests[r].message.mode === "token-to-cash" && requests[r].note) if (requests[r].message.token_txid === tokenTxID) return reject([true, "Token transaction is already used for another request"]); floTokenAPI.getTx(tokenTxID).then(tx => { let parsedTxData = floTokenAPI.util.parseTxData(tx); console.debug(parsedTxData); if (tx.type !== "transfer" || tx.transferType !== "token") reject([true, "Invalid token transfer type"]); else if (tx.tokenAmount !== amount) reject([true, "Incorrect token amount: " + tx.tokenAmount]); else if (tx.tokenIdentification !== floGlobals.currency) reject([true, "Incorrect token: " + tx.tokenIdentification]); else if (parsedTxData.sender !== sender) reject([true, "Incorrect senderID: " + parsedTxData.sender]); else if (parsedTxData.receiver !== myFloID) reject([true, "Incorrect receiverID: " + parsedTxData.receive]) else resolve(true); }).catch(error => reject([null, error])) }) }