From ae4db73835e815eb3fa750c2b63e1d1630e78619 Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Tue, 25 Jan 2022 17:53:56 +0000 Subject: [PATCH] Implement detecting Revolut callback updates --- handler/transactions.py | 102 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/handler/transactions.py b/handler/transactions.py index a34ae68..1a7f97c 100644 --- a/handler/transactions.py +++ b/handler/transactions.py @@ -30,6 +30,9 @@ class Transactions(object): def set_irc(self, irc): self.irc = irc + def set_revolut(self, revolut): + self.revolut = revolut + def transaction(self, data): """ Store details of transaction and post notifications to IRC. @@ -43,7 +46,33 @@ class Transactions(object): inside = data["data"] txid = inside["id"] if "type" not in inside: - self.log.error("Type not in inside: {inside}", inside=inside) + stored_trade = r.hgetall(f"tx.{txid}") + print("stored trade", stored_trade) + if not stored_trade: + self.log.error("Could not find entry in DB for typeless transaction: {id}", id=txid) + return + stored_trade = convert(stored_trade) + if "old_state" in inside: + if "new_state" in inside: + # We don't care unless we're being told a transaction is now completed + if not inside["new_state"] == "completed": + return + # We don't care unless the existing trade is pending + if not stored_trade["state"] == "pending": + return + # Check the old state is what we also think it is + if inside["old_state"] == stored_trade["state"]: + # Set the state to the new state + stored_trade["state"] = inside["new_state"] + # Store the updated state + r.hmset(f"tx.{txid}", stored_trade) + # Check it's all been previously validated + if "valid" not in stored_trade: + print("valid not in stored_trade", stored_trade) + if stored_trade["valid"] == 1: + print("WOULD RELEASE ESCROW FROM SECONDARY NOTINSIDE UPDATE", stored_trade["trade_id"], stored_trade["txid"]) + return + # If type not in inside and we haven't hit any more returns return txtype = inside["type"] if txtype == "card_payment": @@ -71,6 +100,7 @@ class Transactions(object): to_store = { "event": event, + "trade_id": "", "ts": ts, "txid": txid, "txtype": txtype, @@ -80,10 +110,10 @@ class Transactions(object): "amount": amount, "currency": currency, "description": description, + "valid": 0, # All checks passed and we can release escrow? } self.log.info("Transaction processed: {formatted}", formatted=dumps(to_store, indent=2)) - r.hmset(f"tx.{txid}", to_store) self.irc.sendmsg(f"AUTO Incoming transaction: {amount}{currency} ({reference}) - {state} - {description}") # Partial reference implementation @@ -177,14 +207,32 @@ class Transactions(object): self.irc.sendmsg(f"Amount mismatch - not in margins: {stored_trade['amount']} (min: {min_amount} / max: {max_amount}") return # Make sure the account type was Revolut, as these are completed instantly - if not account_type == "revolut": - self.log.info("Account type is not Revolut: {account_type}", account_type=account_type) - self.irc.sendmsg(f"Account type is not Revolut: {account_type}") + # if not account_type == "revolut": + # self.log.info("Account type is not Revolut: {account_type}", account_type=account_type) + # self.irc.sendmsg(f"Account type is not Revolut: {account_type}") + # return + + # We have made it this far without hitting any of the returns, so let's set valid = True + # This will let us instantly release if the type is pending, and it is subsequently updated to completed with a callback. + to_store["valid"] = 1 + # Store the trade ID so we can release it easily + to_store["trade_id"] = stored_trade["id"] + if not state == "completed": + self.log.info("Storing incomplete trade: {id}", id=txid) + r.hmset(f"tx.{txid}", to_store) + # Don't procees further if state is not "completed" return - self.log.info("All checks passed, releasing funds for {trade_id} {reference}", trade_id=stored_trade["id"], reference=reference) - self.irc.sendmsg(f"All checks passed, releasing funds for {stored_trade['id']} / {reference}") - rtrn = self.agora.release_funds(stored_trade["id"]) - self.agora.agora.contact_message_post(stored_trade["id"], "Thanks! Releasing now :)") + + r.hmset(f"tx.{txid}", to_store) + self.release_funds(stored_trade["id"], stored_trade["reference"]) + + def release_funds(self, trade_id, reference): + self.log.info("All checks passed, releasing funds for {trade_id} {reference}", trade_id=trade_id, reference=reference) + self.irc.sendmsg(f"All checks passed, releasing funds for {trade_id} / {reference}") + rtrn = self.agora.release_funds(trade_id) + self.agora.agora.contact_message_post(trade_id, "Thanks! Releasing now :)") + + # Parse the escrow release response message = rtrn["message"] message_long = rtrn["response"]["data"]["message"] self.irc.sendmsg(f"{message} - {message_long}") @@ -331,3 +379,39 @@ class Transactions(object): if not ref_data: return False return ref_data["id"] + + def get_total(self): + total_usd_revolut = self.revolut.get_total_usd() + if total_usd_revolut is False: + return False + agora_wallet_xmr = self.agora.agora.wallet_balance_xmr() + if not agora_wallet_xmr["success"]: + return False + agora_wallet_btc = self.agora.agora.wallet_balance() + if not agora_wallet_btc["success"]: + return False + total_xmr_agora = agora_wallet_xmr["response"]["data"]["total"]["balance"] + total_btc_agora = agora_wallet_btc["response"]["data"]["total"]["balance"] + # Get the XMR -> USD exchange rate + xmr_usd = self.agora.cg.get_price(ids="monero", vs_currencies=["USD"]) + + # Get the BTC -> USD exchange rate + btc_usd = self.agora.cg.get_price(ids="bitcoin", vs_currencies=["USD"]) + + # Convert the Agora XMR total to USD + total_usd_agora_xmr = float(total_xmr_agora) * xmr_usd["monero"]["usd"] + + # Convert the Agora BTC total to USD + total_usd_agora_btc = float(total_btc_agora) * btc_usd["bitcoin"]["usd"] + + # Add it all up + total_usd_agora = total_usd_agora_xmr + total_usd_agora_btc + total_usd = total_usd_agora + total_usd_revolut + + # Convert the total USD price to GBP and SEK + rates = self.agora.get_rates_all() + price_sek = rates["SEK"] * total_usd + price_usd = total_usd + price_gbp = rates["GBP"] * total_usd + + return ((price_sek, price_usd, price_gbp), (total_usd_agora_xmr, total_usd_agora_btc))