From f4fd793971b32beaff3ac221fcbe755c6908c99f Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Sun, 8 Dec 2024 12:24:21 +0530 Subject: [PATCH 01/27] Start and deploy IsoCard payment server using function --- framework/isobot/isocard.py | 12 +++++++----- main.py | 4 +++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 60bc2b4..7b67183 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -151,11 +151,13 @@ def account(): # Initialization def run(): app.run(host="0.0.0.0", port=4800) -if auth.get_runtime_options()["isocard_server_enabled"]: # Run server ONLY if its runtime option is enabled - print("[isocard/server] Starting IsoCard payments server...") - t = Thread(target=run) - t.daemon = True - t.start() +def deploy_server(): + """Deploys the IsoCard Payments Server. (if the option is enabled in the runtimeconfig file)\n\nRuntimeconfig Option: `isocard_server_enabled`""" + if auth.get_runtime_options()["isocard_server_enabled"]: # Run server ONLY if its runtime option is enabled + print("[isocard/server] Starting IsoCard payments server...") + t = Thread(target=run) + t.daemon = True + t.start() #btw i use arch diff --git a/main.py b/main.py index a62f574..f521c67 100644 --- a/main.py +++ b/main.py @@ -161,7 +161,9 @@ async def on_ready(): s.log(f'[main/Client] Logged in as {client.user.name}. Start time: {start_time.strftime("%H:%M:%S")}\n[main/Client] Ready to accept commands. Click Ctrl+C to shut down the bot.') await client.change_presence(activity=discord.Activity(type=discord.ActivityType.playing, name="I-I-I be poppin bottles 🗣🗣🔥"), status=discord.Status.idle) s.log(f'[main/Log] {colors.green}Status set to IDLE. Rich presence set.{colors.end}') - + # Deploy IsoCard Payments Server + isocard.deploy_server() + time.sleep(0.5) # Start and Deploy Ping Server if api.auth.get_mode() or api.auth.get_runtime_options()["ping_server_override"]: # If ping_server_override is set to true, it will start the pinging server no matter what. If it's set to false, it will only start if client mode is set to replit. From 8716329ffaa882b1dda546945127e50c28cc0d9e Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Sun, 8 Dec 2024 12:38:56 +0530 Subject: [PATCH 02/27] Improve message wording for server error code 500 --- framework/isobot/isocard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 7b67183..ce89b70 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -112,7 +112,7 @@ def checkpayment(): }, 404 except Exception as e: return { "code": 500, - "message": f"Failed to process payment: {e}", + "message": f"Failed to process payment due to an unhandled server error: {e}", "exception": type(e).__name__ }, 500 From ee29ecac748c0a43e6fa82b6cdc7f356783ca4c3 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Sun, 8 Dec 2024 12:43:32 +0530 Subject: [PATCH 03/27] Add transaction ids for all new ongoing payments This will be a 9 character id, consisting of 3 *CAPITAL* letters, followed by 6 numbers. --- cogs/isocard.py | 1 + framework/isobot/isocard.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/cogs/isocard.py b/cogs/isocard.py index 4088c78..a37b2a2 100644 --- a/cogs/isocard.py +++ b/cogs/isocard.py @@ -126,6 +126,7 @@ async def verify_transaction(self, ctx: ApplicationContext, verification_code: i description="Please wait patiently until the merchant has verified the transaction.", color=discord.Color.green() ) + localembed.set_footer(text=f"Transaction ID: {transactions_db[str(verification_code)]['txn_id']}") await ctx.respond(embed=localembed, ephemeral=True) except KeyError: return await ctx.respond("This transaction verification code is invalid.") diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index ce89b70..66c1d2b 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -36,6 +36,15 @@ def generate_verification_code() -> int: code: str = int_1 + int_2 + int_3 + int_4 + int_5 + int_6 return int(code) +def generate_txn_id() -> str: + """Generates a randomized transaction id, which is three **CAPITAL** letters followed by 6 numbers.""" + txn_id = str() + for c in range(0, 3): + txn_id += str(random.choice(('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ))).upper() + for n in range(0, 6): + txn_id += str(random.randint(0, 9)) + return txn_id + # API Commands @app.route('/', methods=["GET"]) def main(): @@ -53,8 +62,10 @@ def requestpayment(): merchant_id = args.get("merchantid") if str(isocards[str(card_number)]["ssc"]) == ssc: verification_code = generate_verification_code() + txn_id = generate_txn_id() user_id = isocards[str(card_number)]["cardholder_user_id"] transactions_db[str(verification_code)] = { + "txn_id": txn_id, "payer_id": user_id, "merchant_id": merchant_id, "card_number": card_number, @@ -66,6 +77,7 @@ def requestpayment(): request_data = { "code": 200, "message": f"Payment requested to IsoCard number: {card_number}. Payment will be complete once user accepts this.", + "txn_id": txn_id, "verification_code": verification_code } return request_data, 200 @@ -85,11 +97,13 @@ def checkpayment(): with open("database/isocard_transactions.json", 'r') as f: transactions_db = json.load(f) args = request.args verification_code = args.get("verificationcode") + txn_id: str = transactions_db[str(verification_code)]["txn_id"] if transactions_db[str(verification_code)]["status"] == "complete": if currency.get_bank(transactions_db[str(verification_code)]["payer_id"]) < transactions_db[str(verification_code)]["amount"]: del transactions_db[str(verification_code)] return { "code": 403, + "txn_id": txn_id, "message": "Transaction terminated: Insufficient payer balance.", "exception": "InsufficientFunds" }, 403 @@ -99,10 +113,12 @@ def checkpayment(): save(transactions_db) return { "code": 200, + "txn_id": txn_id, "message": "Transaction complete." }, 200 else: return { "code": 202, + "txn_id": txn_id, "message": "Transaction still not approved." }, 202 except KeyError: return { From ca8ccd101a7fbea2de72797294ac4ee206809610 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Sun, 8 Dec 2024 12:44:12 +0530 Subject: [PATCH 04/27] Add transaction history database to initial setup autogenerate --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index f521c67..1d179a5 100644 --- a/main.py +++ b/main.py @@ -55,6 +55,7 @@ def initial_setup(): "weather", "embeds", "isocard_transactions", + "isocard_transaction_history", "isobank/accounts", "isobank/auth" ) From c7869f97edc23c5723f236fbe464dda6874d0e17 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Sun, 8 Dec 2024 12:44:43 +0530 Subject: [PATCH 05/27] Fix some command docs markdown --- framework/isobot/isocard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 66c1d2b..0e9f94e 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -37,7 +37,7 @@ def generate_verification_code() -> int: return int(code) def generate_txn_id() -> str: - """Generates a randomized transaction id, which is three **CAPITAL** letters followed by 6 numbers.""" + """Generates a randomized transaction id, which is three *CAPITAL* letters followed by 6 numbers.""" txn_id = str() for c in range(0, 3): txn_id += str(random.choice(('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ))).upper() From 6bc2044cf06fa130dd71c43008b56d2c53aea0bc Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Sun, 8 Dec 2024 14:04:47 +0530 Subject: [PATCH 06/27] Implement a transaction history log framework module This will assist with logging new transactions in the transaction history log. --- framework/isobot/isocard.py | 8 +++++++- framework/isobot/isocardtxn.py | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 framework/isobot/isocardtxn.py diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 0e9f94e..3b6ddcc 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -3,6 +3,7 @@ import json import random import logging +import isocardtxn from api import auth from flask import Flask from flask import request @@ -74,6 +75,7 @@ def requestpayment(): "status": "in_progress" } save(transactions_db) + isocardtxn.write_transaction(txn_id, user_id, merchant_id, card_number, user_id, int(amount), "In Progress") request_data = { "code": 200, "message": f"Payment requested to IsoCard number: {card_number}. Payment will be complete once user accepts this.", @@ -101,6 +103,7 @@ def checkpayment(): if transactions_db[str(verification_code)]["status"] == "complete": if currency.get_bank(transactions_db[str(verification_code)]["payer_id"]) < transactions_db[str(verification_code)]["amount"]: del transactions_db[str(verification_code)] + isocardtxn.update_transaction_status(txn_id, "Terminated (insufficient balance)") return { "code": 403, "txn_id": txn_id, @@ -111,6 +114,7 @@ def checkpayment(): currency.bank_add(transactions_db[str(verification_code)]["merchant_id"], transactions_db[str(verification_code)]["amount"]) del transactions_db[str(verification_code)] save(transactions_db) + isocardtxn.update_transaction_status(txn_id, "Successful") return { "code": 200, "txn_id": txn_id, @@ -126,7 +130,9 @@ def checkpayment(): "message": "Verification code does not point to an active transaction.", "exception": "TransactionNotFound" }, 404 - except Exception as e: return { + except Exception as e: + isocardtxn.update_transaction_status(txn_id, "Failed (unable to process payment)") + return { "code": 500, "message": f"Failed to process payment due to an unhandled server error: {e}", "exception": type(e).__name__ diff --git a/framework/isobot/isocardtxn.py b/framework/isobot/isocardtxn.py new file mode 100644 index 0000000..fbd4736 --- /dev/null +++ b/framework/isobot/isocardtxn.py @@ -0,0 +1,36 @@ +# Imports +import json +import time + +# Initialization +with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: + txn_db = json.load(f) + +def save() -> int: + """Dumps the latest transaction data to local machine storage.""" + with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: + json.dump(txn_db, f, indent=4) + return 0 + +# Functions +def write_transaction(txn_id: str, payer_id: str, merchant_id: str, card_number: str, user_id: str, amount: int, status: str): + """Writes a new transaction to the transaction history log.""" + txn_db[str(txn_id)] = { + "payer_id": payer_id, + "merchant_id": merchant_id, + "card_number": card_number, + "user_id": user_id, + "amount": amount, + "status": status, + "timestamp": round(time.time()), + } + save() + +def update_transaction_status(txn_id: str, new_status: str) -> int: + """Updates the status field of a transaction in transaction history.\n\nReturns `0` if successful, returns `1` if transactions does not exist.""" + try: + txn_db[str(txn_id)]["status"] = new_status + save() + return 0 + except KeyError: + return 1 From 780f43c11c8a7c775ec6c0f6883601fffb790f02 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Fri, 20 Dec 2024 21:06:52 +0530 Subject: [PATCH 07/27] Add a system to log all IsoCard transaction updates to a log file --- framework/isobot/isocardtxn.py | 13 +++++++++++++ main.py | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/framework/isobot/isocardtxn.py b/framework/isobot/isocardtxn.py index fbd4736..42f4457 100644 --- a/framework/isobot/isocardtxn.py +++ b/framework/isobot/isocardtxn.py @@ -1,17 +1,30 @@ # Imports import json import time +import datetime +from typing_extensions import Union + +# Variables +log_file_path = "logs/isocard_transactions.log" # Initialization with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: txn_db = json.load(f) +# Pre-defined Methods def save() -> int: """Dumps the latest transaction data to local machine storage.""" with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: json.dump(txn_db, f, indent=4) return 0 +def write_to_log(payer_id: Union[str, int], reciever_id: Union[str, int], data: str) -> int: + """Writes a new transaction update to the specified log path.""" + current_time = datetime.time().strftime("%d-%m-%Y %H:%M:%S") + with open(log_file_path, 'a') as f: + f.write(f"[{current_time}] ({str(payer_id)} -> {str(reciever_id)}) {data}\n") + return 0 + # Functions def write_transaction(txn_id: str, payer_id: str, merchant_id: str, card_number: str, user_id: str, amount: int, status: str): """Writes a new transaction to the transaction history log.""" diff --git a/main.py b/main.py index 1d179a5..b2c5fcb 100644 --- a/main.py +++ b/main.py @@ -107,6 +107,10 @@ def initial_setup(): with open("logs/startup-log.txt", 'x', encoding="utf-8") as this: this.close() time.sleep(0.5) + if not os.path.isfile("logs/isocard_transactions.log"): + with open("logs/isocard_transactions.log", 'x', encoding="utf-8") as this: + this.close() + time.sleep(0.5) except IOError as e: logger.error(f"Failed to make log file: {e}", module="main/Setup", nolog=True) From a61d4ff1c51b512becaa4123d731b813c795518b Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Fri, 20 Dec 2024 21:10:40 +0530 Subject: [PATCH 08/27] Polish up a few things related to log file creation --- main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index b2c5fcb..86b07b9 100644 --- a/main.py +++ b/main.py @@ -88,13 +88,13 @@ def initial_setup(): try: if not os.path.isfile("logs/info-log.txt"): with open('logs/info-log.txt', 'x', encoding="utf-8") as this: - this.write("#All information and warnings will be logged here!\n") + this.write("# All information and warnings will be logged here!\n") this.close() logger.info("Created info log", module="main/Setup", nolog=True) time.sleep(0.5) if not os.path.isfile("logs/error-log.txt"): with open('logs/error-log.txt', 'x', encoding="utf-8") as this: - this.write("#All exceptions will be logged here!\n") + this.write("# All exceptions will be logged here!\n") this.close() logger.info("Created error log", module="main/Setup", nolog=True) time.sleep(0.5) @@ -109,6 +109,7 @@ def initial_setup(): time.sleep(0.5) if not os.path.isfile("logs/isocard_transactions.log"): with open("logs/isocard_transactions.log", 'x', encoding="utf-8") as this: + this.write("# All IsoCard transaction updates will be logged here.\n") this.close() time.sleep(0.5) except IOError as e: From 9d35927d8860345dc274bba99c960f85c008c062 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Sat, 21 Dec 2024 11:55:23 +0530 Subject: [PATCH 09/27] Add some new todos to branch --- cogs/isocard.py | 2 ++ framework/isobot/isocard.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/cogs/isocard.py b/cogs/isocard.py index a37b2a2..eb75690 100644 --- a/cogs/isocard.py +++ b/cogs/isocard.py @@ -130,5 +130,7 @@ async def verify_transaction(self, ctx: ApplicationContext, verification_code: i await ctx.respond(embed=localembed, ephemeral=True) except KeyError: return await ctx.respond("This transaction verification code is invalid.") + # TODO: Make a command to view all transactions related to a user's IsoCard, (or) all transactions related to a user. + # Initialization def setup(bot): bot.add_cog(IsoCard(bot)) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 3b6ddcc..3868db8 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -10,6 +10,8 @@ from framework.isobot import currency from threading import Thread +# TODO: Add log file write commands to ALL EXISTING FUNCTIONS. + # Configuration log = logging.getLogger('werkzeug') log.setLevel(logging.ERROR) From 7e66aec4add41ec7eb46fa95e7c42212e95bb0ea Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 19:23:58 +0530 Subject: [PATCH 10/27] Add command to read existing IsoCard transactions --- framework/isobot/isocardtxn.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/framework/isobot/isocardtxn.py b/framework/isobot/isocardtxn.py index 42f4457..f03cd1a 100644 --- a/framework/isobot/isocardtxn.py +++ b/framework/isobot/isocardtxn.py @@ -26,6 +26,36 @@ def write_to_log(payer_id: Union[str, int], reciever_id: Union[str, int], data: return 0 # Functions +def read_transaction(txn_id: str) -> dict: + """ + # `read_transactions()` Command + ## Command Information + Reads and returns the data of a transaction from the history log with the given transaction id. + + ## Exception Handling + ### Transaction does not exist in the transactions database: + - Returns error response code`1` + + ## Response Format + All successful responses will be provided in the following `dict` format: + + ```json + txn_id: { + "payer_id": user id of the payer, + "merchant_id": user id of the merchant (reciever), + "card_number": IsoCard number of the payer, + "user_id": the id of the user paying, + "amount": amount of coins requested by merchant, + "status": current status of the transaction, + "timestamp": time at which the transaction was logged + } + ``` + """ + try: + return txn_db[str(txn_id)] + except KeyError: + return 1 + def write_transaction(txn_id: str, payer_id: str, merchant_id: str, card_number: str, user_id: str, amount: int, status: str): """Writes a new transaction to the transaction history log.""" txn_db[str(txn_id)] = { From 046a9e10f44737f49a87e8113a6bf9e01df4877a Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 19:36:46 +0530 Subject: [PATCH 11/27] Optimize `txn_id` generation function --- framework/isobot/isocard.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 3868db8..a2aa7e0 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -42,9 +42,9 @@ def generate_verification_code() -> int: def generate_txn_id() -> str: """Generates a randomized transaction id, which is three *CAPITAL* letters followed by 6 numbers.""" txn_id = str() - for c in range(0, 3): - txn_id += str(random.choice(('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ))).upper() - for n in range(0, 6): + for _ in range(3): + txn_id += str(random.choice(('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ))) + for _ in range(6): txn_id += str(random.randint(0, 9)) return txn_id From fde74623d95067680406c2017de4244070744722 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 19:37:20 +0530 Subject: [PATCH 12/27] Make `transactions_db` variable more accessible --- framework/isobot/isocard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index a2aa7e0..9bf842b 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -97,8 +97,8 @@ def requestpayment(): @app.route('/checkpayment', methods=["GET"]) def checkpayment(): + with open("database/isocard_transactions.json", 'r') as f: transactions_db = json.load(f) try: - with open("database/isocard_transactions.json", 'r') as f: transactions_db = json.load(f) args = request.args verification_code = args.get("verificationcode") txn_id: str = transactions_db[str(verification_code)]["txn_id"] From 625e78b3ab8999f0d7b449c6ec246776d8ec3c3e Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 19:39:09 +0530 Subject: [PATCH 13/27] Add `txn_id` argument to `write_to_log()` --- framework/isobot/isocardtxn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/isobot/isocardtxn.py b/framework/isobot/isocardtxn.py index f03cd1a..ff09799 100644 --- a/framework/isobot/isocardtxn.py +++ b/framework/isobot/isocardtxn.py @@ -18,11 +18,11 @@ def save() -> int: json.dump(txn_db, f, indent=4) return 0 -def write_to_log(payer_id: Union[str, int], reciever_id: Union[str, int], data: str) -> int: +def write_to_log(txn_id: str, payer_id: Union[str, int], reciever_id: Union[str, int], data: str) -> int: """Writes a new transaction update to the specified log path.""" current_time = datetime.time().strftime("%d-%m-%Y %H:%M:%S") with open(log_file_path, 'a') as f: - f.write(f"[{current_time}] ({str(payer_id)} -> {str(reciever_id)}) {data}\n") + f.write(f"[{current_time}] ({str(payer_id)} -> {str(reciever_id)}) {txn_id}: {data}\n") return 0 # Functions From 83821ebe4f3828621a5ff9699b8d6201531449fe Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:00:51 +0530 Subject: [PATCH 14/27] Enable all transaction update events to be logged to a separate log file Each log contains info like the respective transaction id, payer id, receiver id, and information of the transaction's status update. --- framework/isobot/isocard.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 9bf842b..99f4e0c 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -77,6 +77,12 @@ def requestpayment(): "status": "in_progress" } save(transactions_db) + isocardtxn.write_to_log( + txn_id=txn_id, + payer_id=user_id, + merchant_id=merchant_id, + data=f"New transaction request started (txn_amount: {amount}; verification code: {verification_code})" + ) isocardtxn.write_transaction(txn_id, user_id, merchant_id, card_number, user_id, int(amount), "In Progress") request_data = { "code": 200, @@ -104,6 +110,12 @@ def checkpayment(): txn_id: str = transactions_db[str(verification_code)]["txn_id"] if transactions_db[str(verification_code)]["status"] == "complete": if currency.get_bank(transactions_db[str(verification_code)]["payer_id"]) < transactions_db[str(verification_code)]["amount"]: + isocardtxn.write_to_log( + txn_id=txn_id, + payer_id=transactions_db[str(verification_code)]["payer_id"], + reciever_id=transactions_db[str(verification_code)]["merchant_id"], + data="Transaction has been terminated (reason: insufficient balance of the payer)" + ) del transactions_db[str(verification_code)] isocardtxn.update_transaction_status(txn_id, "Terminated (insufficient balance)") return { @@ -114,6 +126,12 @@ def checkpayment(): }, 403 currency.bank_remove(transactions_db[str(verification_code)]["payer_id"], transactions_db[str(verification_code)]["amount"]) currency.bank_add(transactions_db[str(verification_code)]["merchant_id"], transactions_db[str(verification_code)]["amount"]) + isocardtxn.write_to_log( + txn_id=txn_id, + payer_id=transactions_db[str(verification_code)]["payer_id"], + reciever_id=transactions_db[str(verification_code)]["merchant_id"], + data=f"Payment of {transactions_db[str(verification_code)]['amount']} coins has been successful" + ) del transactions_db[str(verification_code)] save(transactions_db) isocardtxn.update_transaction_status(txn_id, "Successful") @@ -133,6 +151,12 @@ def checkpayment(): "exception": "TransactionNotFound" }, 404 except Exception as e: + isocardtxn.write_to_log( + txn_id=txn_id, + payer_id=transactions_db[str(verification_code)]["payer_id"], + reciever_id=transactions_db[str(verification_code)]["merchant_id"], + data=f"Failed to process payment due to a server error (error: {e})" + ) isocardtxn.update_transaction_status(txn_id, "Failed (unable to process payment)") return { "code": 500, From 18b4f97bc365427a24696e10caffcba76ed68e17 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:01:25 +0530 Subject: [PATCH 15/27] Remove completed todo comment --- framework/isobot/isocard.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 99f4e0c..1be52ad 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -10,8 +10,6 @@ from framework.isobot import currency from threading import Thread -# TODO: Add log file write commands to ALL EXISTING FUNCTIONS. - # Configuration log = logging.getLogger('werkzeug') log.setLevel(logging.ERROR) From f20366725a922912a7cde6ab414025f748e2190a Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:13:50 +0530 Subject: [PATCH 16/27] Update all the docstrings to contain more command documentation Also it looks good so why not lol --- framework/isobot/isocardtxn.py | 77 +++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/framework/isobot/isocardtxn.py b/framework/isobot/isocardtxn.py index ff09799..30874b3 100644 --- a/framework/isobot/isocardtxn.py +++ b/framework/isobot/isocardtxn.py @@ -13,13 +13,42 @@ # Pre-defined Methods def save() -> int: - """Dumps the latest transaction data to local machine storage.""" + """ + # `save()` Command + ## Command Information + Dumps the latest transaction data from memory to local machine storage. + + ## Status Return Codes + ### If successful: + - Returns `0` + ### If not successful: + - Returns the respective exception class + """ with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: json.dump(txn_db, f, indent=4) return 0 def write_to_log(txn_id: str, payer_id: Union[str, int], reciever_id: Union[str, int], data: str) -> int: - """Writes a new transaction update to the specified log path.""" + """ + # `write_to_log()` Command + ## Command Information + Writes a new transaction update to the specified log path. + + ## Status Return Codes + ### If successful: + - Returns `0` + ### If not successful: + - Returns the respective exception class + + ## Log Format + Each log update is written to the log file at `logs/isocard_transactions.log`, whenever the command is fired during runtime. + + The log format is provided as follows: + + ```log + [current time] (payer id -> receiver id) transaction id: status update data + ``` + """ current_time = datetime.time().strftime("%d-%m-%Y %H:%M:%S") with open(log_file_path, 'a') as f: f.write(f"[{current_time}] ({str(payer_id)} -> {str(reciever_id)}) {txn_id}: {data}\n") @@ -56,8 +85,35 @@ def read_transaction(txn_id: str) -> dict: except KeyError: return 1 -def write_transaction(txn_id: str, payer_id: str, merchant_id: str, card_number: str, user_id: str, amount: int, status: str): - """Writes a new transaction to the transaction history log.""" +def write_transaction(txn_id: str, payer_id: str, merchant_id: str, card_number: str, user_id: str, amount: int, status: str) -> int: + """ + # `write_transaction()` Command + ## Command Information + Writes a new transaction to the transaction history log. + + ## Status Return Codes + ### If successful: + - Returns `0` + ### If not successful: + - Returns the respective exception class + + ## Log Database Format + Each transaction in the transaction history database is stored in the following format: + + ```json + txn_id: { + "payer_id": user id of the payer, + "merchant_id": user id of the merchant (reciever), + "card_number": IsoCard number of the payer, + "user_id": the id of the user paying, + "amount": amount of coins requested by merchant, + "status": current status of the transaction, + "timestamp": time at which the transaction was logged + } + ``` + + - Note: This format can be refered to, while working with the output from the `read_transaction()` command. + """ txn_db[str(txn_id)] = { "payer_id": payer_id, "merchant_id": merchant_id, @@ -68,9 +124,20 @@ def write_transaction(txn_id: str, payer_id: str, merchant_id: str, card_number: "timestamp": round(time.time()), } save() + return 0 def update_transaction_status(txn_id: str, new_status: str) -> int: - """Updates the status field of a transaction in transaction history.\n\nReturns `0` if successful, returns `1` if transactions does not exist.""" + """ + # `update_transaction_status()` Command + ## Command Information + Updates the status field of a transaction in transaction history. + + ## Status Return Codes + ### If successful: + - Returns `0` + ### If transaction does not exist: + - Returns `1` + """ try: txn_db[str(txn_id)]["status"] = new_status save() From e6f3cd294cbb126f93ed1bf8c2d74f3ddd821cfc Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:30:23 +0530 Subject: [PATCH 17/27] Fix an import name for `isocardtxn` module --- framework/isobot/isocard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 1be52ad..3ffb1bc 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -3,7 +3,7 @@ import json import random import logging -import isocardtxn +from framework.isobot import isocardtxn from api import auth from flask import Flask from flask import request From 0b5673ba144a8a690a44a07101ce3e8cda3e3f23 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:31:39 +0530 Subject: [PATCH 18/27] Move all `isocardtxn` commands to a class `IsoCardTxn` --- framework/isobot/isocardtxn.py | 270 +++++++++++++++++---------------- 1 file changed, 136 insertions(+), 134 deletions(-) diff --git a/framework/isobot/isocardtxn.py b/framework/isobot/isocardtxn.py index 30874b3..174ce96 100644 --- a/framework/isobot/isocardtxn.py +++ b/framework/isobot/isocardtxn.py @@ -8,139 +8,141 @@ log_file_path = "logs/isocard_transactions.log" # Initialization -with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: - txn_db = json.load(f) - -# Pre-defined Methods -def save() -> int: - """ - # `save()` Command - ## Command Information - Dumps the latest transaction data from memory to local machine storage. - - ## Status Return Codes - ### If successful: - - Returns `0` - ### If not successful: - - Returns the respective exception class - """ - with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: - json.dump(txn_db, f, indent=4) - return 0 - -def write_to_log(txn_id: str, payer_id: Union[str, int], reciever_id: Union[str, int], data: str) -> int: - """ - # `write_to_log()` Command - ## Command Information - Writes a new transaction update to the specified log path. - - ## Status Return Codes - ### If successful: - - Returns `0` - ### If not successful: - - Returns the respective exception class - - ## Log Format - Each log update is written to the log file at `logs/isocard_transactions.log`, whenever the command is fired during runtime. - - The log format is provided as follows: - - ```log - [current time] (payer id -> receiver id) transaction id: status update data - ``` - """ - current_time = datetime.time().strftime("%d-%m-%Y %H:%M:%S") - with open(log_file_path, 'a') as f: - f.write(f"[{current_time}] ({str(payer_id)} -> {str(reciever_id)}) {txn_id}: {data}\n") - return 0 - -# Functions -def read_transaction(txn_id: str) -> dict: - """ - # `read_transactions()` Command - ## Command Information - Reads and returns the data of a transaction from the history log with the given transaction id. - - ## Exception Handling - ### Transaction does not exist in the transactions database: - - Returns error response code`1` - - ## Response Format - All successful responses will be provided in the following `dict` format: - - ```json - txn_id: { - "payer_id": user id of the payer, - "merchant_id": user id of the merchant (reciever), - "card_number": IsoCard number of the payer, - "user_id": the id of the user paying, - "amount": amount of coins requested by merchant, - "status": current status of the transaction, - "timestamp": time at which the transaction was logged - } - ``` - """ - try: - return txn_db[str(txn_id)] - except KeyError: - return 1 - -def write_transaction(txn_id: str, payer_id: str, merchant_id: str, card_number: str, user_id: str, amount: int, status: str) -> int: - """ - # `write_transaction()` Command - ## Command Information - Writes a new transaction to the transaction history log. - - ## Status Return Codes - ### If successful: - - Returns `0` - ### If not successful: - - Returns the respective exception class - - ## Log Database Format - Each transaction in the transaction history database is stored in the following format: - - ```json - txn_id: { - "payer_id": user id of the payer, - "merchant_id": user id of the merchant (reciever), - "card_number": IsoCard number of the payer, - "user_id": the id of the user paying, - "amount": amount of coins requested by merchant, - "status": current status of the transaction, - "timestamp": time at which the transaction was logged - } - ``` - - - Note: This format can be refered to, while working with the output from the `read_transaction()` command. - """ - txn_db[str(txn_id)] = { - "payer_id": payer_id, - "merchant_id": merchant_id, - "card_number": card_number, - "user_id": user_id, - "amount": amount, - "status": status, - "timestamp": round(time.time()), - } - save() - return 0 - -def update_transaction_status(txn_id: str, new_status: str) -> int: - """ - # `update_transaction_status()` Command - ## Command Information - Updates the status field of a transaction in transaction history. +class IsoCardTxn: + def __init__(self): + with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: + self.txn_db = json.load(f) + + # Pre-defined Methods + def save(self) -> int: + """ + # `save()` Command + ## Command Information + Dumps the latest transaction data from memory to local machine storage. + + ## Status Return Codes + ### If successful: + - Returns `0` + ### If not successful: + - Returns the respective exception class + """ + with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: + json.dump(self.txn_db, f, indent=4) + return 0 + + def write_to_log(self, txn_id: str, payer_id: Union[str, int], reciever_id: Union[str, int], data: str) -> int: + """ + # `write_to_log()` Command + ## Command Information + Writes a new transaction update to the specified log path. + + ## Status Return Codes + ### If successful: + - Returns `0` + ### If not successful: + - Returns the respective exception class + + ## Log Format + Each log update is written to the log file at `logs/isocard_transactions.log`, whenever the command is fired during runtime. + + The log format is provided as follows: + + ```log + [current time] (payer id -> receiver id) transaction id: status update data + ``` + """ + current_time = datetime.time().strftime("%d-%m-%Y %H:%M:%S") + with open(log_file_path, 'a') as f: + f.write(f"[{current_time}] ({str(payer_id)} -> {str(reciever_id)}) {txn_id}: {data}\n") + return 0 + + # Functions + def read_transaction(self, txn_id: str) -> dict: + """ + # `read_transactions()` Command + ## Command Information + Reads and returns the data of a transaction from the history log with the given transaction id. - ## Status Return Codes - ### If successful: - - Returns `0` - ### If transaction does not exist: - - Returns `1` - """ - try: - txn_db[str(txn_id)]["status"] = new_status - save() + ## Exception Handling + ### Transaction does not exist in the transactions database: + - Returns error response code`1` + + ## Response Format + All successful responses will be provided in the following `dict` format: + + ```json + txn_id: { + "payer_id": user id of the payer, + "merchant_id": user id of the merchant (reciever), + "card_number": IsoCard number of the payer, + "user_id": the id of the user paying, + "amount": amount of coins requested by merchant, + "status": current status of the transaction, + "timestamp": time at which the transaction was logged + } + ``` + """ + try: + return self.txn_db[str(txn_id)] + except KeyError: + return 1 + + def write_transaction(self, txn_id: str, payer_id: str, merchant_id: str, card_number: str, user_id: str, amount: int, status: str) -> int: + """ + # `write_transaction()` Command + ## Command Information + Writes a new transaction to the transaction history log. + + ## Status Return Codes + ### If successful: + - Returns `0` + ### If not successful: + - Returns the respective exception class + + ## Log Database Format + Each transaction in the transaction history database is stored in the following format: + + ```json + txn_id: { + "payer_id": user id of the payer, + "merchant_id": user id of the merchant (reciever), + "card_number": IsoCard number of the payer, + "user_id": the id of the user paying, + "amount": amount of coins requested by merchant, + "status": current status of the transaction, + "timestamp": time at which the transaction was logged + } + ``` + + - Note: This format can be refered to, while working with the output from the `read_transaction()` command. + """ + self.txn_db[str(txn_id)] = { + "payer_id": payer_id, + "merchant_id": merchant_id, + "card_number": card_number, + "user_id": user_id, + "amount": amount, + "status": status, + "timestamp": round(time.time()), + } + self.save() return 0 - except KeyError: - return 1 + + def update_transaction_status(self, txn_id: str, new_status: str) -> int: + """ + # `update_transaction_status()` Command + ## Command Information + Updates the status field of a transaction in transaction history. + + ## Status Return Codes + ### If successful: + - Returns `0` + ### If transaction does not exist: + - Returns `1` + """ + try: + self.txn_db[str(txn_id)]["status"] = new_status + self.save() + return 0 + except KeyError: + return 1 From 6209028c7c4dcf750390c2b4592b770c044e87b7 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:36:05 +0530 Subject: [PATCH 19/27] Initialize class for `IsoCardTxn` --- framework/isobot/isocard.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 3ffb1bc..dd675e9 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -3,7 +3,7 @@ import json import random import logging -from framework.isobot import isocardtxn +from framework.isobot import isocardtxn as isocardtxn_ from api import auth from flask import Flask from flask import request @@ -14,6 +14,7 @@ log = logging.getLogger('werkzeug') log.setLevel(logging.ERROR) app = Flask('') +isocardtxn = isocardtxn_.IsoCardTxn() currency = currency.CurrencyAPI("database/currency.json", "logs/currency.log") def call_isocards_database() -> dict: From 53d38a4c18cd6c86c26d114c4f17fa4a18b928d0 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:42:34 +0530 Subject: [PATCH 20/27] Fix the database file stream mode in `save()` --- framework/isobot/isocardtxn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/isobot/isocardtxn.py b/framework/isobot/isocardtxn.py index 174ce96..78369c6 100644 --- a/framework/isobot/isocardtxn.py +++ b/framework/isobot/isocardtxn.py @@ -26,7 +26,7 @@ def save(self) -> int: ### If not successful: - Returns the respective exception class """ - with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: + with open("database/isocard_transaction_history.json", 'w+', encoding="utf-8") as f: json.dump(self.txn_db, f, indent=4) return 0 From 05b899f7a0f95af18874ce46aad2096319b84abb Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:43:05 +0530 Subject: [PATCH 21/27] Fix a small typo in `write_to_log()`'s argument --- framework/isobot/isocard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index dd675e9..0c16c69 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -79,7 +79,7 @@ def requestpayment(): isocardtxn.write_to_log( txn_id=txn_id, payer_id=user_id, - merchant_id=merchant_id, + reciever_id=merchant_id, data=f"New transaction request started (txn_amount: {amount}; verification code: {verification_code})" ) isocardtxn.write_transaction(txn_id, user_id, merchant_id, card_number, user_id, int(amount), "In Progress") From 270d77baa9db8d2491da57ffcdbf3bf6615cb0e6 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:51:32 +0530 Subject: [PATCH 22/27] Add a `-` in between transaction ids Now it looks better :) --- framework/isobot/isocard.py | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 0c16c69..353c4ac 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -43,6 +43,7 @@ def generate_txn_id() -> str: txn_id = str() for _ in range(3): txn_id += str(random.choice(('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ))) + txn_id += "-" for _ in range(6): txn_id += str(random.randint(0, 9)) return txn_id From 922adcfdf09493d2352682fb06913485badf72cf Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:54:19 +0530 Subject: [PATCH 23/27] Add missing `save()` for when transaction is terminated (due to insufficient funds.) --- framework/isobot/isocard.py | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/isobot/isocard.py b/framework/isobot/isocard.py index 353c4ac..c8af0e8 100644 --- a/framework/isobot/isocard.py +++ b/framework/isobot/isocard.py @@ -117,6 +117,7 @@ def checkpayment(): data="Transaction has been terminated (reason: insufficient balance of the payer)" ) del transactions_db[str(verification_code)] + save(transactions_db) isocardtxn.update_transaction_status(txn_id, "Terminated (insufficient balance)") return { "code": 403, From 8730f5fc5f06815d23bafc543c5da65f57f91d4e Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 21:00:03 +0530 Subject: [PATCH 24/27] Fix an issue where all transaction logs would appear with the wrong date The date would automatically be set to 1-1-1900 with time 00:00:00. This commit should fix this issue, and the logs' time and date should appear as normal now. --- framework/isobot/isocardtxn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/isobot/isocardtxn.py b/framework/isobot/isocardtxn.py index 78369c6..e69a61a 100644 --- a/framework/isobot/isocardtxn.py +++ b/framework/isobot/isocardtxn.py @@ -51,7 +51,7 @@ def write_to_log(self, txn_id: str, payer_id: Union[str, int], reciever_id: Unio [current time] (payer id -> receiver id) transaction id: status update data ``` """ - current_time = datetime.time().strftime("%d-%m-%Y %H:%M:%S") + current_time = datetime.datetime.now().strftime("%d-%m-%Y %H:%M:%S") with open(log_file_path, 'a') as f: f.write(f"[{current_time}] ({str(payer_id)} -> {str(reciever_id)}) {txn_id}: {data}\n") return 0 From 7fa9bc7fadb8a4c887ecbd40202d1310a605bba1 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Mon, 23 Dec 2024 21:49:40 +0530 Subject: [PATCH 25/27] Add command to fetch all the raw data from the transactions database --- framework/isobot/isocardtxn.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/framework/isobot/isocardtxn.py b/framework/isobot/isocardtxn.py index e69a61a..025eb36 100644 --- a/framework/isobot/isocardtxn.py +++ b/framework/isobot/isocardtxn.py @@ -146,3 +146,26 @@ def update_transaction_status(self, txn_id: str, new_status: str) -> int: return 0 except KeyError: return 1 + + def fetch_raw(self) -> dict: + """ + # `fetch_raw()` Command + ## Command Information + Fetches all of the raw data from the transactions database as `dict.` + + ## Transaction Log Format + Each log in the transactions database is given in the following format: + + ```json + txn_id: { + "payer_id": user id of the payer, + "merchant_id": user id of the merchant (reciever), + "card_number": IsoCard number of the payer, + "user_id": the id of the user paying, + "amount": amount of coins requested by merchant, + "status": current status of the transaction, + "timestamp": time at which the transaction was logged + } + ``` + """ + return self.txn_db From bb0f45ec0de16fa1bd23ec7a0632a312c50fe0ce Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Tue, 24 Dec 2024 19:05:34 +0530 Subject: [PATCH 26/27] Fix an issue where database would not refresh after a transaction history update --- framework/isobot/isocardtxn.py | 39 ++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/framework/isobot/isocardtxn.py b/framework/isobot/isocardtxn.py index 025eb36..0bcb234 100644 --- a/framework/isobot/isocardtxn.py +++ b/framework/isobot/isocardtxn.py @@ -9,12 +9,21 @@ # Initialization class IsoCardTxn: - def __init__(self): + # Pre-defined Methods + def read(self) -> dict: + """ + # `read()` Command + ## Command Information + Reads the latest data from the transaction database, and returns it. + + ### Note: This command should only be used for internal module use. + ### To use this elsewhere, use the alternate command `fetch_raw()`. + """ with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: - self.txn_db = json.load(f) + txn_db = json.load(f) + return txn_db - # Pre-defined Methods - def save(self) -> int: + def save(self, data: dict) -> int: """ # `save()` Command ## Command Information @@ -27,7 +36,7 @@ def save(self) -> int: - Returns the respective exception class """ with open("database/isocard_transaction_history.json", 'w+', encoding="utf-8") as f: - json.dump(self.txn_db, f, indent=4) + json.dump(data, f, indent=4) return 0 def write_to_log(self, txn_id: str, payer_id: Union[str, int], reciever_id: Union[str, int], data: str) -> int: @@ -83,7 +92,9 @@ def read_transaction(self, txn_id: str) -> dict: ``` """ try: - return self.txn_db[str(txn_id)] + with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: + txn_db = json.load(f) + return txn_db[str(txn_id)] except KeyError: return 1 @@ -116,7 +127,9 @@ def write_transaction(self, txn_id: str, payer_id: str, merchant_id: str, card_n - Note: This format can be refered to, while working with the output from the `read_transaction()` command. """ - self.txn_db[str(txn_id)] = { + with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: + txn_db = json.load(f) + txn_db[str(txn_id)] = { "payer_id": payer_id, "merchant_id": merchant_id, "card_number": card_number, @@ -125,7 +138,7 @@ def write_transaction(self, txn_id: str, payer_id: str, merchant_id: str, card_n "status": status, "timestamp": round(time.time()), } - self.save() + self.save(txn_db) return 0 def update_transaction_status(self, txn_id: str, new_status: str) -> int: @@ -141,8 +154,10 @@ def update_transaction_status(self, txn_id: str, new_status: str) -> int: - Returns `1` """ try: - self.txn_db[str(txn_id)]["status"] = new_status - self.save() + with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: + txn_db = json.load(f) + txn_db[str(txn_id)]["status"] = new_status + self.save(txn_db) return 0 except KeyError: return 1 @@ -168,4 +183,6 @@ def fetch_raw(self) -> dict: } ``` """ - return self.txn_db + with open("database/isocard_transaction_history.json", 'r', encoding="utf-8") as f: + txn_db = json.load(f) + return txn_db From c6b17e2e25b43da292c292f4e82088b08b148d13 Mon Sep 17 00:00:00 2001 From: snipe <72265661+notsniped@users.noreply.github.com> Date: Tue, 24 Dec 2024 19:07:28 +0530 Subject: [PATCH 27/27] Add `/transaction_history` to let users view their IsoCard transaction history This command has page support, which means that you can choose which page you want to view your transactions from. This command is also split into two separate sub-commands: one for paid transactions, and another for received transactions. --- cogs/isocard.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/cogs/isocard.py b/cogs/isocard.py index eb75690..8337990 100644 --- a/cogs/isocard.py +++ b/cogs/isocard.py @@ -4,12 +4,15 @@ import discord import random import json +import math +from framework.isobot.isocardtxn import IsoCardTxn from framework.isobot.db.isocard import IsoCard from discord import option, ApplicationContext, SlashCommandGroup from discord.ext import commands # Variables and Functions isocard_db = IsoCard() +isocardtxn = IsoCardTxn() def generate_card_id() -> int: # Generate 16 random digits and append to a str variable @@ -130,7 +133,82 @@ async def verify_transaction(self, ctx: ApplicationContext, verification_code: i await ctx.respond(embed=localembed, ephemeral=True) except KeyError: return await ctx.respond("This transaction verification code is invalid.") - # TODO: Make a command to view all transactions related to a user's IsoCard, (or) all transactions related to a user. + @isocard.command( + name="transaction_history", + description="View all your past transactions (paid and received)" + ) + @option(name="transaction_type", description="Which type of transactions do you want to view?", type=str, choices=["paid", "received"]) + @option(name="page", description="Select the page number that you want to view (1 page = 5 logs)", type=int, default=1) + async def transaction_history(self, ctx: ApplicationContext, transaction_type: str, page: int = 1): + """View all your past transactions (paid and received)""" + transactions_db = isocardtxn.fetch_raw() + + if transaction_type == "paid": + user_transactions_paid = {} + for transaction in transactions_db: + if str(transactions_db[transaction]["payer_id"]) == str(ctx.author.id): + user_transactions_paid[transaction] = transactions_db[transaction] + + # Initial Calculation for Pages + total_pages = math.ceil(len(user_transactions_paid)/5) + if page > total_pages: page = total_pages + + log_entries = 0 + log_entries_offset = -((page-1)*5) + parsed_output = str() + sr = 0 + for transaction in user_transactions_paid: + sr += 1 + log_entries_offset += 1 + if log_entries_offset > 0: + log_entries += 1 + if log_entries <= 5: + txn_data = user_transactions_paid[transaction] + status = "" + if txn_data['status'] == "Successful": status = ":white_check_mark: Successful" + elif txn_data['status'] == "In Progress": status = ":arrows_counterclockwise: In Progress" + elif txn_data['status'] == "Terminated (insufficient balance)": status = ":x: Terminated (insufficient balance)" + elif txn_data['status'] == "Failed (unable to process payment)": status = ":warning: Failed (unable to process payment)" + parsed_output += f"{sr}. **TXN ID:** `{transaction}`\n> <@!{txn_data['payer_id']}> -> <@!{txn_data['merchant_id']}> | Amount: {txn_data['amount']} | Card Used: `{txn_data['card_number']}`\n> Status: **{status}** | \n\n" + localembed = discord.Embed( + title=f"IsoCard Transaction History for **{ctx.author.name}** (paid)", + description=parsed_output + ) + localembed.set_footer(text=f"Page {page} of {total_pages}") + return await ctx.respond(embed=localembed, ephemeral=True) + + elif transaction_type == "received": + user_transactions_received = {} + for transaction in transactions_db: + if str(transactions_db[transaction]["merchant_id"]) == str(ctx.author.id): + user_transactions_received[transaction] = transactions_db[transaction] + + # Initial Calculation for Pages + total_pages = math.ceil(len(user_transactions_received)/5) + if page > total_pages: page = total_pages + + log_entries = 0 + log_entries_offset = -((page-1)*5) + parsed_output = str() + sr = 0 + for transaction in user_transactions_received: + sr += 1 + log_entries_offset += 1 + if log_entries_offset > 0: + log_entries += 1 + if log_entries <= 5: + txn_data = user_transactions_received[transaction] + status = "" + if txn_data['status'] == "Successful": status = ":white_check_mark: Successful" + elif txn_data['status'] == "Terminated (insufficient balance)": status = ":x: Terminated (insufficient balance)" + elif txn_data['status'] == "Failed (unable to process payment)": status = ":warning: Failed (unable to process payment)" + parsed_output += f"{sr}. **TXN ID:** `{transaction}`\n> <@!{txn_data['payer_id']}> -> <@!{txn_data['merchant_id']}> | Amount: {txn_data['amount']} | Card Used: `{txn_data['card_number']}`\n> Status: **{status}** | \n\n" + localembed = discord.Embed( + title=f"IsoCard Transaction History for **{ctx.author.name}** (received)", + description=parsed_output + ) + localembed.set_footer(text=f"Page {page} of {total_pages}") + return await ctx.respond(embed=localembed, ephemeral=True) # Initialization def setup(bot): bot.add_cog(IsoCard(bot))