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
24 commits
Select commit Hold shift + click to select a range
ceadb9e
Add command for verifying ongoing transactions with IsoCard
notsniped Jul 14, 2023
1a67165
Add database to store information about ongoing transactions
notsniped Jul 14, 2023
ddf5cee
Add missing `ctx.respond()` trigger after embed creation
notsniped Jul 15, 2023
c830a23
Add basic functionality of the web payment server
notsniped Jul 15, 2023
b0dff50
Change port to prevent web server overlapping
notsniped Jul 15, 2023
b57e332
Add `/account` endpoint to fetch IsoCard account info
notsniped Jul 15, 2023
3d652f4
Merge branch 'main' into isocard-system-phase-3
notsniped Nov 8, 2024
efc4af3
Set `isocard_transactions` database to be automatically generated upo…
notsniped Nov 8, 2024
e1b125a
Merge branch 'main' into isocard-system-phase-3
notsniped Nov 24, 2024
ba13285
Set a specific IsoCard server network address rather than using a bla…
notsniped Nov 29, 2024
542d4d1
Add `json` import to module
notsniped Nov 30, 2024
b3e0e80
Fix an issue where user's cards list would not show up
notsniped Dec 1, 2024
e5e7501
Fix calling information from the IsoCard database
notsniped Dec 1, 2024
b50d374
Add a runtime option to configure whether to start IsoCard payments s…
notsniped Dec 2, 2024
256a95a
Use a `call function` type system for the IsoCards database
notsniped Dec 2, 2024
9651936
Add IsoCard payments server to bot client startup
notsniped Dec 2, 2024
3894804
Add output to confirm that IsoCard server is starting
notsniped Dec 2, 2024
c26f388
Revert server IP address to binding on all interfaces
notsniped Dec 2, 2024
0fd2389
Fix a small typo in IsoCard database path
notsniped Dec 2, 2024
f722a95
Add missing `self` keyword for `/verify_transaction` function
notsniped Dec 2, 2024
e25e25a
Add missing log file path for CurrencyAPI
notsniped Dec 2, 2024
2552830
Convert `ssc` API argument to `str` for checking
notsniped Dec 2, 2024
fa0f537
Fix an issue with bank account payment targets
notsniped Dec 2, 2024
4cb8f72
Remove resolved todo comment
notsniped Dec 6, 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
3 changes: 2 additions & 1 deletion api/runtimeconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"guild_log_whitelist": {},
"ping_server_override": false,
"debug_mode": false,
"show_ping_on_startup": true
"show_ping_on_startup": true,
"isocard_server_enabled": true
},
"replit": false,
"other_keys": {
Expand Down
29 changes: 25 additions & 4 deletions cogs/isocard.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Imports
import discord
import random
import json
from framework.isobot.db.isocard import IsoCard
from discord import option, ApplicationContext, SlashCommandGroup
from discord.ext import commands
Expand Down Expand Up @@ -74,15 +75,16 @@ async def info(self, ctx: ApplicationContext, card_number: int):
description="View a list of all your cards."
)
async def my_card(self, ctx: ApplicationContext):
all_card_numbers = isocard_db.keys()
all_card_numbers = isocard_db.fetch_all_cards()
isocard_database = isocard_db.raw()
your_cards = list()
for card in all_card_numbers:
if isocard_db[str(card)]["cardholder_user_id"] == ctx.author.id: your_cards.append(str(card))
if isocard_database[str(card)]["cardholder_user_id"] == ctx.author.id: your_cards.append(str(card))
embed_desc = str()
sr = 1
for card in your_cards:
if isocard_db[str(card)]["config"]["card_label"] != None:
embed_desc += f"{sr}. **{card}**: {isocard_db[str(card)]['config']['card_label']}\n"
if isocard_database[str(card)]["config"]["card_label"] != None:
embed_desc += f"{sr}. **{card}**: {isocard_database[str(card)]['config']['card_label']}\n"
else: embed_desc += f"{sr}. **{card}**\n"
sr += 1
embed_desc += "\n*Nothing more here*"
Expand All @@ -108,5 +110,24 @@ async def options_label(self, ctx: ApplicationContext, card_number: int, new_lab
if new_label == None: return await ctx.respond(embed=discord.Embed(description=":white_check_mark: Your card label has been reset.", color=discord.Color.green()), ephemeral=True)
else: return await ctx.respond(embed=discord.Embed(description=":white_check_mark: Your card label has been edited.", color=discord.Color.green()), ephemeral=True)

@isocard.command(
name="verify_transaction",
description="Verify an ongoing transaction."
)
@option(name="verification_code", description="The 6-digit verification code for your transaction", type=int)
async def verify_transaction(self, ctx: ApplicationContext, verification_code: int):
try:
with open("database/isocard_transactions.json", 'r') as f: transactions_db = json.load(f)
if transactions_db[str(verification_code)]["payer_id"] == ctx.author.id:
transactions_db[str(verification_code)]["status"] = "complete"
with open("database/isocard_transactions.json", 'w+') as f: json.dump(transactions_db, f, indent=4)
localembed = discord.Embed(
title="Transaction successfully verified.",
description="Please wait patiently until the merchant has verified the transaction.",
color=discord.Color.green()
)
await ctx.respond(embed=localembed, ephemeral=True)
except KeyError: return await ctx.respond("This transaction verification code is invalid.")

# Initialization
def setup(bot): bot.add_cog(IsoCard(bot))
10 changes: 10 additions & 0 deletions framework/isobot/db/isocard.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ def save(self, data: dict) -> int:
with open("database/isocard.json", 'w+', encoding="utf-8") as f: json.dump(data, f, indent=4)
return 0

def raw(self) -> dict:
"""Returns all of the raw data from the IsoCard database.\n\n***WARNING:*** This function must **ONLY** be used for validation of IsoCard ownership, and nothing else."""
isocard_db = self.load()
return isocard_db

def fetch_all_cards(self) -> list:
"""Fetches a `list` of all registered IsoCard numbers in the IsoCard database.\n\n***WARNING:*** This function must **ONLY** be used for validation of IsoCard ownership, and nothing else."""
isocard_db = self.load()
return list(isocard_db.keys())

def fetch_card_data(self, card_id: int) -> dict:
"""Fetches the raw `dict` data related to the given IsoCard id.\n\nReturns data as `dict` if successful, returns `KeyError` if card id does not exist."""
isocard_db = self.load()
Expand Down
161 changes: 161 additions & 0 deletions framework/isobot/isocard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
"""The IsoCard payments web server."""
# Imports
import json
import random
import logging
from api import auth
from flask import Flask
from flask import request
from framework.isobot import currency
from threading import Thread

# Configuration
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
app = Flask('')
currency = currency.CurrencyAPI("database/currency.json", "logs/currency.log")

def call_isocards_database() -> dict:
"""Calls all of the latest information from the IsoCards database."""
with open("database/isocard.json", 'r') as f: isocards = json.load(f)
return isocards

def save(data):
"""Dumps all cached databases to the local machine."""
with open("database/isocard_transactions.json", 'w+') as f: json.dump(data, f, indent=4)

# Functions
def generate_verification_code() -> int:
"""Generates a random 6 digit verification code."""
int_1 = str(random.randint(1, 9))
int_2 = str(random.randint(0, 9))
int_3 = str(random.randint(0, 9))
int_4 = str(random.randint(0, 9))
int_5 = str(random.randint(0, 9))
int_6 = str(random.randint(0, 9))
code: str = int_1 + int_2 + int_3 + int_4 + int_5 + int_6
return int(code)

# API Commands
@app.route('/', methods=["GET"])
def main():
return "Server is online."

@app.route('/requestpayment', methods=["GET"])
def requestpayment():
try:
isocards = call_isocards_database()
with open("database/isocard_transactions.json", 'r') as f: transactions_db = json.load(f)
args = request.args
card_number = args.get("cardnumber")
ssc = args.get("ssc")
amount = args.get("amount")
merchant_id = args.get("merchantid")
if str(isocards[str(card_number)]["ssc"]) == ssc:
verification_code = generate_verification_code()
user_id = isocards[str(card_number)]["cardholder_user_id"]
transactions_db[str(verification_code)] = {
"payer_id": user_id,
"merchant_id": merchant_id,
"card_number": card_number,
"user_id": user_id,
"amount": int(amount),
"status": "in_progress"
}
save(transactions_db)
request_data = {
"code": 200,
"message": f"Payment requested to IsoCard number: {card_number}. Payment will be complete once user accepts this.",
"verification_code": verification_code
}
return request_data, 200
else: return {
"code": 401,
"message": "Unable to authorize transaction."
}, 401
except Exception as e: return {
"code": 500,
"message": f"Failed to process payment: {e}",
"exception": type(e).__name__
}, 500

@app.route('/checkpayment', methods=["GET"])
def checkpayment():
try:
with open("database/isocard_transactions.json", 'r') as f: transactions_db = json.load(f)
args = request.args
verification_code = args.get("verificationcode")
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,
"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"])
del transactions_db[str(verification_code)]
save(transactions_db)
return {
"code": 200,
"message": "Transaction complete."
}, 200
else: return {
"code": 202,
"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 {
"code": 500,
"message": f"Failed to process payment: {e}",
"exception": type(e).__name__
}, 500

@app.route('/account', methods=["GET"])
def account():
try:
isocards = call_isocards_database()
args = request.args
isocard_number = args.get("cardnumber")
ssc = args.get("ssc")
if isocards[str(isocard_number)]["ssc"] == ssc:
card_data = isocards[str(isocard_number)]
del card_data["config"]
del card_data["ssc"]
card_data["account_balance"] = currency.get_wallet(card_data["user_id"])
return {
"code": 200,
"card_info": card_data
}, 200
else: return {
"code": 403,
"message": "Incorrect IsoCard SSC.",
"exception": "InvalidSSC"
}
except KeyError: return {
"code": 404,
"message": "Card number is invalid.",
"exception": "InvalidIsoCard"
}, 404
except Exception as e: return {
"code": 500,
"message": f"Failed to fetch account info: {e}",
"exception": type(e).__name__
}

# 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()


#btw i use arch
3 changes: 2 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from utils import logger, ping
from math import floor
from random import randint
from framework.isobot import currency, colors, settings, commands as _commands
from framework.isobot import currency, colors, settings, commands as _commands, isocard
from framework.isobot.shop import ShopData
from framework.isobot.db import levelling, items, userdata, automod, weather, warnings, presence as _presence, serverconfig, embeds
from discord import ApplicationContext, option
Expand Down Expand Up @@ -54,6 +54,7 @@ def initial_setup():
"user_data",
"weather",
"embeds",
"isocard_transactions",
"isobank/accounts",
"isobank/auth"
)
Expand Down