Skip to content
This repository was archived by the owner on Feb 7, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f4fd793
Start and deploy IsoCard payment server using function
notsniped Dec 8, 2024
8716329
Improve message wording for server error code 500
notsniped Dec 8, 2024
ee29eca
Add transaction ids for all new ongoing payments
notsniped Dec 8, 2024
ca8ccd1
Add transaction history database to initial setup autogenerate
notsniped Dec 8, 2024
c7869f9
Fix some command docs markdown
notsniped Dec 8, 2024
6bc2044
Implement a transaction history log framework module
notsniped Dec 8, 2024
780f43c
Add a system to log all IsoCard transaction updates to a log file
notsniped Dec 20, 2024
a61d4ff
Polish up a few things related to log file creation
notsniped Dec 20, 2024
9d35927
Add some new todos to branch
notsniped Dec 21, 2024
7e66aec
Add command to read existing IsoCard transactions
notsniped Dec 23, 2024
046a9e1
Optimize `txn_id` generation function
notsniped Dec 23, 2024
fde7462
Make `transactions_db` variable more accessible
notsniped Dec 23, 2024
625e78b
Add `txn_id` argument to `write_to_log()`
notsniped Dec 23, 2024
83821eb
Enable all transaction update events to be logged to a separate log file
notsniped Dec 23, 2024
18b4f97
Remove completed todo comment
notsniped Dec 23, 2024
f203667
Update all the docstrings to contain more command documentation
notsniped Dec 23, 2024
e6f3cd2
Fix an import name for `isocardtxn` module
notsniped Dec 23, 2024
0b5673b
Move all `isocardtxn` commands to a class `IsoCardTxn`
notsniped Dec 23, 2024
6209028
Initialize class for `IsoCardTxn`
notsniped Dec 23, 2024
53d38a4
Fix the database file stream mode in `save()`
notsniped Dec 23, 2024
05b899f
Fix a small typo in `write_to_log()`'s argument
notsniped Dec 23, 2024
270d77b
Add a `-` in between transaction ids
notsniped Dec 23, 2024
922adcf
Add missing `save()` for when transaction is terminated
notsniped Dec 23, 2024
8730f5f
Fix an issue where all transaction logs would appear with the wrong date
notsniped Dec 23, 2024
7fa9bc7
Add command to fetch all the raw data from the transactions database
notsniped Dec 23, 2024
bb0f45e
Fix an issue where database would not refresh after a transaction his…
notsniped Dec 24, 2024
c6b17e2
Add `/transaction_history` to let users view their IsoCard transactio…
notsniped Dec 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions cogs/isocard.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -126,8 +129,86 @@ 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.")

@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}** | <t:{txn_data['timestamp']}:f>\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}** | <t:{txn_data['timestamp']}:f>\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))
67 changes: 59 additions & 8 deletions framework/isobot/isocard.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import random
import logging
from framework.isobot import isocardtxn as isocardtxn_
from api import auth
from flask import Flask
from flask import request
Expand All @@ -13,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:
Expand All @@ -36,6 +38,16 @@ 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 _ 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

# API Commands
@app.route('/', methods=["GET"])
def main():
Expand All @@ -53,8 +65,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,
Expand All @@ -63,9 +77,17 @@ def requestpayment():
"status": "in_progress"
}
save(transactions_db)
isocardtxn.write_to_log(
txn_id=txn_id,
payer_id=user_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")
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
Expand All @@ -81,38 +103,65 @@ 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"]
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)]
save(transactions_db)
isocardtxn.update_transaction_status(txn_id, "Terminated (insufficient balance)")
return {
"code": 403,
"txn_id": txn_id,
"message": "Transaction terminated: Insufficient payer balance.",
"exception": "InsufficientFunds"
}, 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")
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 {
"code": 404,
"message": "Verification code does not point to an active transaction.",
"exception": "TransactionNotFound"
}, 404
except Exception as e: return {
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,
"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

Expand Down Expand Up @@ -151,11 +200,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
Loading