diff --git a/fn_pay.js b/fn_pay.js
new file mode 100644
index 0000000..c331b25
--- /dev/null
+++ b/fn_pay.js
@@ -0,0 +1,225 @@
+const TYPE_MONEY_REQUEST = "MoneyRequests",
+ TYPE_CASHIER_REQUEST = "CashierRequests";
+
+const cashierUPI = {};
+
+//https://ranchimallflo.duckdns.org/api/v1.0/getFloAddressTransactions?token=rupee&floAddress=FKAEdnPfjXLHSYwrXQu377ugN4tXU7VGdf
+//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
+ }
+ }))
+ /*
+ promises.push(floCloudAPI.requestObjectData("UPI", { //Is this needed?
+ callback: UI_RENDER_FN
+ }));
+ */
+ Promise.all(promises)
+ .then(result => resolve(result))
+ .catch(error => reject(error))
+ })
+}
+
+Object.defineProperty(Cashier, 'cashierRequests', {
+ get: function() {
+ let fk = floCloudAPI.util.filterKey(TYPE_CASHIER_REQUEST, {
+ senderID: myFloID,
+ receiverID: cashierID,
+ group: "Cashiers",
+ });
+ return floGlobals.generalData[fk];
+ }
+});
+
+Object.defineProperty(Cashier, '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])
+ online.push(c);
+ if (!online.length)
+ return null;
+ else
+ return online[floCrypto.randInt(0, online.length)];
+}
+
+User.cashToToken = function(cashier, amount, upiTxID) {
+ return new Promise((resolve, reject) => {
+ if (!floCloudAPI.subAdmins.includes(cashier))
+ return reject("Invalid cashier");
+ floCloudAPI.sendGeneralData({
+ mode: "cash-to-token",
+ amount: amount,
+ upi_txid: upiTxID
+ }, 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 (!floCloudAPI.subAdmins.includes(cashier))
+ return reject("Invalid cashier");
+ floCloudAPI.sendGeneralData({
+ mode: "token-to-cash",
+ amount: amount,
+ token_txid: blkTxID,
+ upi_id: upiID
+ }, TYPE_CASHIER_REQUEST, {
+ receiverID: cashier
+ }).then(result => resolve(result))
+ .catch(error => reject(error))
+ })
+}
+
+User.sendToken = function(receiverID, amount, remark = '') {
+ return new Promise((resolve, reject) => {
+ tokenAPI.sendToken(myPrivKey, amount, receiverID, remark)
+ .then(result => resolve(result))
+ .catch(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 = {};
+
+Cashier.init = function() {
+ 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
+ }));
+ */
+ Promise.all(promises)
+ .then(result => resolve(result))
+ .catch(error => reject(error))
+ })
+}
+
+Object.defineProperty(Cashier, 'Requests', {
+ get: function() {
+ let fk = floCloudAPI.util.filterKey(TYPE_CASHIER_REQUEST, {
+ receiver: myFloID
+ });
+ 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")
+ if (requests[r].note === 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")
+ if (requests[r].note === tokenTxID)
+ return reject([true, "Token transaction is already used for another request"]);
+ tokenAPI.getTx(tokenTxID).then(tx => {
+ let parsedTxData = tokenAPI.util.parseTxData(tx);
+ console.debug(parsedTxData);
+ if (parsedTxData.type !== "transfer" || parsedTxData.transferType !== "token")
+ reject([true, "Invalid token transfer type"]);
+ else if (parsedTxData.tokenAmount !== amount)
+ reject([true, "Incorrect token amount: " + parsedTxData.tokenAmount]);
+ else if (parsedTxData.tokenIdentification !== floGlobals.currency)
+ reject([true, "Incorrect token: " + parsedTxData.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]))
+ })
+}
\ No newline at end of file
diff --git a/fn_ui.js b/fn_ui.js
new file mode 100644
index 0000000..90238fb
--- /dev/null
+++ b/fn_ui.js
@@ -0,0 +1,218 @@
+const userUI = {};
+
+userUI.requestTokenFromCashier = function() {
+ let cashier = User.findCashier();
+ if (!cashier)
+ return alert("No cashier online");
+ let amount = parseFloat(document.forms['request-cashier'][amount]);
+ //get UPI txid from user
+ let upiTxID = prompt(`Send Rs. ${amount} to ${cashierUPI[cashier]} and enter UPI txid`);
+ if (!upiTxID)
+ return alert("Cancelled");
+ User.cashToToken(cashier, amount, upiTxID).then(result => {
+ console.log(result);
+ alert("Requested cashier. please wait!");
+ }).catch(error => console.error(error))
+}
+
+userUI.withdrawCashFromCashier = function() {
+ let cashier = User.findCashier();
+ if (!cashier)
+ return alert("No cashier online");
+ let amount = parseFloat(document.forms['request-cashier'][amount]);
+ //get confirmation from user
+ let upiID = prompt(`${amount} ${floGlobals.currency}# will be sent to ${cashier}. Enter UPI ID`);
+ if (!upiID)
+ return alert("Cancelled");
+ User.sendToken(cashier, amount, 'for token-to-cash').then(txid => {
+ console.log("txid", txid);
+ User.tokenToCash(cashier, amount, txid, upiID).then(result => {
+ console.log(result);
+ alert("Requested cashier. please wait!");
+ }).catch(error => console.error(error))
+ }).catch(error => console.error(error))
+}
+
+userUI.sendMoneyToUser = function() {
+ let form = document.forms['user-money'];
+ let floID = form['flo-id'],
+ amount = parseFloat(form['amount']),
+ remark = form['remark'];
+ let confirmation = prompt(`Do you want to SEND ${amount} to ${floID}?`);
+ if (!confirmation)
+ return alert("Cancelled");
+ User.sendToken(floID, amount, remark).then(txid => {
+ console.info(`Sent ${amount} to ${floID}`, txid);
+ alert(`Sent ${amount} to ${floID}. It may take a few mins to reflect in their wallet`);
+ }).catch(error => console.error(error));
+}
+
+userUI.requestMoneyFromUser = function() {
+ let form = document.forms['user-money'];
+ let floID = form['flo-id'],
+ amount = parseFloat(form['amount']),
+ remark = form['remark'];
+ let confirmation = prompt(`Do you want to REQUEST ${amount} from ${floID}?`);
+ if (!confirmation)
+ return alert("Cancelled");
+ User.requestToken(floID, amount, remark).then(result => {
+ console.log(`Requested ${amount} from ${floID}`, result);
+ alert(`Requested ${amount} from ${floID}`);
+ }).catch(error => console.error(error));
+}
+
+userUI.renderCashierRequests = function(requests, error = null) {
+ if (error)
+ return console.error(error);
+ else if (typeof requests !== "object" || request === null)
+ return;
+ let table = document.getElementById('user-cashier-requests').getElementsByTagName('tbody')[0];
+ for (let r in requests) {
+ let oldCard = document.getElementById(r);
+ if (oldCard) oldCard.remove();
+ let row = table.insertRow();
+ renderUser_cashierRequestCard(request[r], row);
+ }
+}
+
+function renderUser_cashierRequestCard(request, row) {
+ row.id = request.vectorClock;
+ row.insertCell().textContent = request.time;
+ row.insertCell().textContent = request.receiverID;
+ row.insertCell().textContent = request.message.mode;
+ let status = request.tag ? (status = request.tag + ":" + request.note) : (request.note || "PENDING");
+ row.insertCell().textContent = status; //Status
+}
+
+userUI.renderMoneyRequests = function(requests, error = null) {
+ if (error)
+ return console.error(error);
+ else if (typeof requests !== "object" || request === null)
+ return;
+ let table = document.getElementById('user-money-requests').getElementsByTagName('tbody')[0];
+ for (let r in requests) {
+ let oldCard = document.getElementById(r);
+ if (oldCard) oldCard.remove();
+ let row = table.insertRow();
+ renderUser_moneyRequestCard(request[r], row);
+ }
+}
+
+function renderUser_moneyRequestCard(request, row) {
+ row.id = request.vectorClock;
+ row.insertCell().textContent = request.time;
+ row.insertCell().textContent = request.senderID;
+ row.insertCell().textContent = request.message.amount;
+ row.insertCell().textContent = request.message.remark;
+ let status = request.note;
+ if (status)
+ row.insertCell().textContent = request.note;
+ else
+ row.insertCell().innerHTML =
+ `` +
+ ``;
+}
+
+userUI.payRequest = function(reqID) {
+ let request = User.moneyRequests[reqID];
+ let confirmation = prompt(`Do you want to SEND ${request.message. amount} to ${request.senderID}?`);
+ if (!confirmation)
+ return alert("Cancelled");
+ User.sendToken(request.senderID, request.message.amount, request.message.remark).then(txid => {
+ console.info(`Sent ${request.message.amount} to ${request.senderID}`, txid);
+ alert(`Sent ${request.message.amount} to ${request.senderID}. It may take a few mins to reflect in their wallet`);
+ User.decideRequest(request, 'PAID: ' + txid)
+ .then(result => console.log(result))
+ .catch(error => console.error(error))
+ }).catch(error => console.error(error));
+}
+
+userUI.declineRequest = function(reqID) {
+ let request = User.moneyRequests[reqID];
+ User.decideRequest(request, "DECLINED").then(result => {
+ console.log(result);
+ alert("Declined request");
+ }).catch(error => console.error(error))
+}
+
+//Cashier
+const cashierUI = {};
+
+cashierUI.renderRequests = function(requests, error = null) {
+ if (error)
+ return console.error(error);
+ else if (typeof requests !== "object" || request === null)
+ return;
+ let table = document.getElementById('cashier-request-list').getElementsByTagName('tbody')[0];
+ for (let r in requests) {
+ let oldCard = document.getElementById(r);
+ if (oldCard) oldCard.remove();
+ let row = table.insertRow();
+ renderRequestCard(request[r], row);
+ }
+}
+
+function renderCashier_requestCard(request, row) {
+ row.id = request.vectorClock;
+ row.insertCell().textContent = request.senderID;
+ row.insertCell().textContent = request.time;
+ row.insertCell().textContent = request.message.mode;
+ let status = request.tag || request.note; //status tag for completed, note for rejected
+ if (status)
+ row.insertCell().textContent = status;
+ else
+ row.insertCell().innerHTML = ``
+}
+
+cashierUI.completeRequest = function(reqID) {
+ let request = Cashier.Requests[reqID];
+ if (request.message.mode === "cash-to-token")
+ completeCashToTokenRequest(request);
+ else if (request.message.mode === "token-to-cash")
+ completeTokenToCashRequest(request);
+}
+
+function completeCashToTokenRequest(request) {
+ Cashier.checkIfUpiTxIsValid(request.message.upiTxID).then(_ => {
+ let confirmation = prompt(`Check if you have received UPI transfer\ntxid:${request.message.upi_txid}\namount:${request.message.amount}`);
+ if (!confirmation)
+ return alert("Cancelled");
+ User.sendToken(request.senderID, request.message.amount, 'for cash-to-token').then(txid => {
+ console.log("txid", txid);
+ Cashier.finishRequest(request, txid).then(result => {
+ console.log(result);
+ console.info('Completed cash-to-token request:', request.vectorClock);
+ alert("Completed request");
+ }).catch(error => console.error(error))
+ }).catch(error => console.error(error))
+ }).catch(error => {
+ console.error(error);
+ alert(error);
+ if (Array.isArray(error) && error[0] === true && typeof error[1] === 'string')
+ Cashier.rejectRequest(request, error[1]).then(result => {
+ console.log(result);
+ console.info('Rejected cash-to-token request:', request.vectorClock);
+ }).catch(error => console.error(error))
+ })
+}
+
+function completeTokenToCashRequest(request) {
+ Cashier.checkIfTokenTxIsValid(request.message.token_txid, request.senderID, request.message.amount).then(result => {
+ let upiTxID = prompt(`Token transfer is verified!\n Send ${request.message.amount} to ${request.message.upi_id} and Enter UPI txid`);
+ if (!upiTxID)
+ return alert("Cancelled");
+ Cashier.finishRequest(request, upiTxID).then(result => {
+ console.log(result);
+ console.info('Completed token-to-cash request:', request.vectorClock);
+ alert("Completed request");
+ }).catch(error => console.error(error))
+ }).catch(error => {
+ console.error(error);
+ alert(error);
+ if (Array.isArray(error) && error[0] === true && typeof error[1] === 'string')
+ Cashier.rejectRequest(request, error[1]).then(result => {
+ console.log(result);
+ console.info('Rejected token-to-cash request:', request.vectorClock);
+ }).catch(error => console.error(error))
+ })
+}
\ No newline at end of file
diff --git a/new.html b/new.html
new file mode 100644
index 0000000..62bc740
--- /dev/null
+++ b/new.html
@@ -0,0 +1,133 @@
+
+
+
+
+ RanchiMall Pay
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Date-Time |
+ Cashier |
+ Mode |
+ Status |
+
+
+
+
+
+
+
+
+ | Date-Time |
+ Requestor |
+ Amount |
+ Remark |
+ Status |
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Requestor |
+ Date-Time |
+ Mode |
+ Status |
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file