Default cashier and other changes

- Encrypt UPI ID of users before sending to cashier
- Cashier: reconnect status ws at a regular interval to avoid timeout
- return the default cashier ID if no cashier is online (configured via blockchain)
This commit is contained in:
sairajzero 2022-05-24 01:54:04 +05:30
parent 7760570c95
commit 6c17afb913
2 changed files with 97 additions and 59 deletions

View File

@ -4,12 +4,13 @@ const TYPE_MONEY_REQUEST = "MoneyRequests",
TYPE_CASHIER_UPI = "CashierUPI"; TYPE_CASHIER_UPI = "CashierUPI";
const cashierUPI = {}; const cashierUPI = {};
const cashierPubKeys = {};
//For regular users //For regular users
const User = {}; const User = {};
const cashierStatus = {}; const cashierStatus = {};
User.init = function () { User.init = function() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let promises; let promises;
//Request cashier for token-cash exchange //Request cashier for token-cash exchange
@ -55,22 +56,24 @@ User.init = function () {
}) })
} }
User.getCashierUPI = function () { User.getCashierUPI = function() {
return new Promise((resolve) => { return new Promise((resolve) => {
Promise.allSettled(floGlobals.subAdmins.map(cashierID => floCloudAPI.requestApplicationData(TYPE_CASHIER_UPI, { Promise.allSettled(floGlobals.subAdmins.map(cashierID => floCloudAPI.requestApplicationData(TYPE_CASHIER_UPI, {
senderID: cashierID, senderID: cashierID,
mostRecent: true mostRecent: true
}))).then(result => { }))).then(result => {
for (let r of result) for (let r of result)
if (r.status === "fulfilled" && r.value.length) if (r.status === "fulfilled" && r.value.length) {
cashierUPI[r.value[0].senderID] = floCloudAPI.util.decodeMessage(r.value[0].message).upi; cashierUPI[r.value[0].senderID] = floCloudAPI.util.decodeMessage(r.value[0].message).upi;
cashierPubKeys[r.value[0].senderID] = r.value[0].pubKey; //get pubKey of cashier from messages for encryption
}
resolve(cashierUPI); resolve(cashierUPI);
}) })
}) })
} }
Object.defineProperty(User, 'cashierRequests', { Object.defineProperty(User, 'cashierRequests', {
get: function () { get: function() {
let fk = floCloudAPI.util.filterKey(TYPE_CASHIER_REQUEST, { let fk = floCloudAPI.util.filterKey(TYPE_CASHIER_REQUEST, {
senderID: myFloID, senderID: myFloID,
group: "Cashiers", group: "Cashiers",
@ -80,7 +83,7 @@ Object.defineProperty(User, 'cashierRequests', {
}); });
Object.defineProperty(User, 'moneyRequests', { Object.defineProperty(User, 'moneyRequests', {
get: function () { get: function() {
let fk = floCloudAPI.util.filterKey(TYPE_MONEY_REQUEST, { let fk = floCloudAPI.util.filterKey(TYPE_MONEY_REQUEST, {
receiverID: myFloID, receiverID: myFloID,
}); });
@ -88,84 +91,110 @@ Object.defineProperty(User, 'moneyRequests', {
} }
}); });
User.findCashier = function () { User.findCashier = function() {
let online = []; let online = [];
for (let c in cashierStatus) for (let c in cashierStatus)
if (cashierStatus[c] && cashierUPI[c]) if (cashierStatus[c] && cashierUPI[c])
online.push(c); online.push(c);
if (!online.length) if (!online.length) {
return null; if (floGlobals.settings.defaultCashier && floGlobals.settings.defaultCashier in cashierUPI)
else { return floGlobals.settings.defaultCashier;
else
return null;
} else {
const random = floCrypto.randInt(0, online.length - 1) const random = floCrypto.randInt(0, online.length - 1)
return online[random]; return online[random];
} }
} }
User.cashToToken = function (cashier, amount, upiTxID) { User.cashToToken = function(cashier, amount, upiTxID) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!floGlobals.subAdmins.includes(cashier)) if (!floGlobals.subAdmins.includes(cashier))
return reject("Invalid cashier"); return reject("Invalid cashier");
floCloudAPI.sendGeneralData({ floCloudAPI.sendGeneralData({
mode: "cash-to-token", mode: "cash-to-token",
amount: amount, amount: amount,
upi_txid: upiTxID upi_txid: upiTxID
}, TYPE_CASHIER_REQUEST, { }, TYPE_CASHIER_REQUEST, {
receiverID: cashier receiverID: cashier
}).then(result => resolve(result)) }).then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))
}) })
} }
User.tokenToCash = function (cashier, amount, blkTxID, upiID) { User.tokenToCash = function(cashier, amount, blkTxID, upiID) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!floGlobals.subAdmins.includes(cashier)) if (!floGlobals.subAdmins.includes(cashier))
return reject("Invalid cashier"); return reject("Invalid cashier");
floCloudAPI.sendGeneralData({ floCloudAPI.sendGeneralData({
mode: "token-to-cash", mode: "token-to-cash",
amount: amount, amount: amount,
token_txid: blkTxID, token_txid: blkTxID,
upi_id: upiID upi_id: floCrypto.encryptData(upiID, cashierPubKeys[cashier])
}, TYPE_CASHIER_REQUEST, { }, TYPE_CASHIER_REQUEST, {
receiverID: cashier receiverID: cashier
}).then(result => resolve(result)) }).then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))
}) })
} }
User.sendToken = function (receiverID, amount, remark = '') { User.sendToken = function(receiverID, amount, remark = '') {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
tokenAPI.sendToken(myPrivKey, amount, receiverID, remark) floTokenAPI.sendToken(myPrivKey, amount, receiverID, remark)
.then(result => resolve(result)) .then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))
}) })
} }
User.requestToken = function (floID, amount, remark = '') { User.requestToken = function(floID, amount, remark = '') {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
floCloudAPI.sendGeneralData({ floCloudAPI.sendGeneralData({
amount: amount, amount: amount,
remark: remark remark: remark
}, TYPE_MONEY_REQUEST, { }, TYPE_MONEY_REQUEST, {
receiverID: floID receiverID: floID
}).then(result => resolve(result)) }).then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))
}) })
} }
User.decideRequest = function (request, note) { User.decideRequest = function(request, note) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
floCloudAPI.noteApplicationData(request.vectorClock, note, { floCloudAPI.noteApplicationData(request.vectorClock, note, {
receiverID: myFloID receiverID: myFloID
}).then(result => resolve(result)) }).then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))
}) })
} }
const Cashier = {}; const Cashier = {};
Cashier.init = function () { 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 => { delegate(getRef('cashier_pending_request_list'), 'click', '.process-cashier-request', e => {
const requestID = e.delegateTarget.closest('.cashier-request').dataset.vc; const requestID = e.delegateTarget.closest('.cashier-request').id;
cashierUI.completeRequest(requestID) cashierUI.completeRequest(requestID)
}) })
getRef('cashier_requests_selector').addEventListener('change', e => { getRef('cashier_requests_selector').addEventListener('change', e => {
@ -179,31 +208,32 @@ Cashier.init = function () {
callback: cashierUI.renderRequests //UI_fn callback: cashierUI.renderRequests //UI_fn
})); }));
//Set online status of cashier(self) //Set online status of cashier(self)
promises.push(floCloudAPI.setStatus()); //promises.push(floCloudAPI.setStatus());
/* /*
promises.push(floCloudAPI.requestObjectData("UPI", { //Is this needed? promises.push(floCloudAPI.requestObjectData("UPI", { //Is this needed?
callback: UI_RENDER_FN callback: UI_RENDER_FN
})); }));
*/ */
promises.push(User.getCashierUPI()); promises.push(User.getCashierUPI());
Promise.all(promises) Promise.all(promises).then(result => {
.then(result => resolve(result)) startStatusInterval(); //Set online status of cashier(self) [connection refreshes on interval]
.catch(error => reject(error)) resolve(result)
}).catch(error => reject(error));
}) })
} }
Cashier.updateUPI = function (upi_id) { Cashier.updateUPI = function(upi_id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
floCloudAPI.sendApplicationData({ floCloudAPI.sendApplicationData({
upi: upi_id upi: upi_id
}, TYPE_CASHIER_UPI) }, TYPE_CASHIER_UPI)
.then(result => resolve(result)) .then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))
}) })
} }
Object.defineProperty(Cashier, 'Requests', { Object.defineProperty(Cashier, 'Requests', {
get: function () { get: function() {
let fk = floCloudAPI.util.filterKey(TYPE_CASHIER_REQUEST, { let fk = floCloudAPI.util.filterKey(TYPE_CASHIER_REQUEST, {
receiverID: myFloID receiverID: myFloID
}); });
@ -212,29 +242,29 @@ Object.defineProperty(Cashier, 'Requests', {
} }
}); });
Cashier.finishRequest = function (request, txid) { Cashier.finishRequest = function(request, txid) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
floCloudAPI.tagApplicationData(request.vectorClock, 'COMPLETED', { floCloudAPI.tagApplicationData(request.vectorClock, 'COMPLETED', {
receiverID: myFloID receiverID: myFloID
}).then(result => { }).then(result => {
floCloudAPI.noteApplicationData(request.vectorClock, txid, { floCloudAPI.noteApplicationData(request.vectorClock, txid, {
receiverID: myFloID receiverID: myFloID
}).then(result => resolve(result)) }).then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))
}).catch(error => reject(error)) }).catch(error => reject(error))
}) })
} }
Cashier.rejectRequest = function (request, reason) { Cashier.rejectRequest = function(request, reason) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
floCloudAPI.noteApplicationData(request.vectorClock, "REJECTED:" + reason, { floCloudAPI.noteApplicationData(request.vectorClock, "REJECTED:" + reason, {
receiverID: myFloID receiverID: myFloID
}).then(result => resolve(result)) }).then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))
}) })
} }
Cashier.checkIfUpiTxIsValid = function (upiTxID) { Cashier.checkIfUpiTxIsValid = function(upiTxID) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let requests = Cashier.Requests; let requests = Cashier.Requests;
for (let r in requests) for (let r in requests)
@ -245,15 +275,15 @@ Cashier.checkIfUpiTxIsValid = function (upiTxID) {
}) })
} }
Cashier.checkIfTokenTxIsValid = function (tokenTxID, sender, amount) { Cashier.checkIfTokenTxIsValid = function(tokenTxID, sender, amount) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let requests = Cashier.Requests; let requests = Cashier.Requests;
for (let r in requests) for (let r in requests)
if (requests[r].message.mode === "token-to-cash" && requests[r].note) if (requests[r].message.mode === "token-to-cash" && requests[r].note)
if (requests[r].message.token_txid === tokenTxID) if (requests[r].message.token_txid === tokenTxID)
return reject([true, "Token transaction is already used for another request"]); return reject([true, "Token transaction is already used for another request"]);
tokenAPI.getTx(tokenTxID).then(tx => { floTokenAPI.getTx(tokenTxID).then(tx => {
let parsedTxData = tokenAPI.util.parseTxData(tx); let parsedTxData = floTokenAPI.util.parseTxData(tx);
console.debug(parsedTxData); console.debug(parsedTxData);
if (parsedTxData.type !== "transfer" || parsedTxData.transferType !== "token") if (parsedTxData.type !== "transfer" || parsedTxData.transferType !== "token")
reject([true, "Invalid token transfer type"]); reject([true, "Invalid token transfer type"]);

View File

@ -409,6 +409,14 @@ function declineTopUp() {
function completeTokenToCashRequest(request) { function completeTokenToCashRequest(request) {
const { vectorClock, senderID, message: { token_txid, amount, upi_id } } = request const { vectorClock, senderID, message: { token_txid, amount, upi_id } } = request
if (upi_id instanceof Object && "secret" in upi_id){
try {
upi_id = floCrypto.decryptData(upi_id, myPrivKey);
} catch(error) {
console.error("UPI ID is not encrypted with a proper key", error);
return notify("Invalid UPI ID", 'error');
}
}
Cashier.checkIfTokenTxIsValid(token_txid, senderID, amount).then(result => { Cashier.checkIfTokenTxIsValid(token_txid, senderID, amount).then(result => {
getPromptInput('Process', `Token transfer is verified!\n Send ${formatAmount(amount)}\n to ${upi_id}\n Enter UPI transaction ID`, { getPromptInput('Process', `Token transfer is verified!\n Send ${formatAmount(amount)}\n to ${upi_id}\n Enter UPI transaction ID`, {
placeholder: 'UPI transaction ID', placeholder: 'UPI transaction ID',
@ -550,7 +558,7 @@ const render = {
return clone; return clone;
}, },
transactionMessage(details) { transactionMessage(details) {
const { tokenAmount, time, sender, receiver, flodata } = tokenAPI.util.parseTxData(details) const { tokenAmount, time, sender, receiver, flodata } = floTokenAPI.util.parseTxData(details)
let messageType = sender === receiver ? 'self' : sender === myFloID ? 'sent' : 'received'; let messageType = sender === receiver ? 'self' : sender === myFloID ? 'sent' : 'received';
const clone = getRef('transaction_message_template').content.cloneNode(true).firstElementChild; const clone = getRef('transaction_message_template').content.cloneNode(true).firstElementChild;
clone.classList.add(messageType); clone.classList.add(messageType);
@ -585,10 +593,10 @@ const render = {
if (paymentsHistoryLoader) if (paymentsHistoryLoader)
paymentsHistoryLoader.clear() paymentsHistoryLoader.clear()
getRef('payments_history').innerHTML = '<sm-spinner></sm-spinner>'; getRef('payments_history').innerHTML = '<sm-spinner></sm-spinner>';
tokenAPI.getAllTxs(myFloID).then(({ transactions }) => { floTokenAPI.getAllTxs(myFloID).then(({ transactions }) => {
for (const transactionId in transactions) { for (const transactionId in transactions) {
paymentTransactions.push({ paymentTransactions.push({
...tokenAPI.util.parseTxData(transactions[transactionId]), ...floTokenAPI.util.parseTxData(transactions[transactionId]),
txid: transactionId txid: transactionId
}) })
} }
@ -647,7 +655,7 @@ function buttonLoader(id, show) {
function refreshBalance(button) { function refreshBalance(button) {
if (button) if (button)
buttonLoader(button, true) buttonLoader(button, true)
tokenAPI.getBalance(myFloID).then((balance = 0) => { floTokenAPI.getBalance(myFloID).then((balance = 0) => {
const [beforeDecimal, afterDecimal] = formatAmount(balance).split('₹')[1].split('.') const [beforeDecimal, afterDecimal] = formatAmount(balance).split('₹')[1].split('.')
getRef('rupee_balance').innerHTML = `<span><b>${beforeDecimal}</b></span>.<span>${afterDecimal}</span>` getRef('rupee_balance').innerHTML = `<span><b>${beforeDecimal}</b></span>.<span>${afterDecimal}</span>`
if (button) if (button)