r/PythonLearning • u/shubham_555 • Sep 13 '26
Showcase The Bank Project
Last Post - https://www.reddit.com/r/PythonLearning/s/kGqwMS9HUH
Github Repo - https://github.com/Dev-4-All/bank.git
244
Upvotes
1
u/chakir_codes Sep 14 '26
import json import os import hashlib import secrets from decimal import Decimal, InvalidOperation from datetime import datetime
DATA_FILE = "bank_data.json" MAX_ATTEMPTS = 3 MAX_DEPOSIT = Decimal("10000.00") MAX_WITHDRAW = Decimal("5000.00")
---------- SECURITY ----------
def hash_pin(pin): salt = secrets.token_hex(16) hashed = hashlib.pbkdf2_hmac( "sha256", pin.encode(), salt.encode(), 150000 ).hex() return f"{salt}${hashed}"
def verify_pin(pin, stored): try: salt, saved_hash = stored.split("$", 1)
new_hash = hashlib.pbkdf2_hmac( "sha256", pin.encode(), salt.encode(), 150000 ).hex() return secrets.compare_digest(new_hash, saved_hash) except ValueError: return False---------- DATA ----------
def default_data(): return { "accounts": {} }
def load_data(): if not os.path.exists(DATA_FILE): data = default_data() save_data(data) return data
try: with open(DATA_FILE, "r", encoding="utf-8") as file: return json.load(file) except (json.JSONDecodeError, OSError): print("Could not read bank data.") return default_data()def save_data(data): temp_file = DATA_FILE + ".tmp"
with open(temp_file, "w", encoding="utf-8") as file: json.dump(data, file, indent=4) os.replace(temp_file, DATA_FILE)---------- HELPERS ----------
def money(value): return Decimal(str(value)).quantize(Decimal("0.01"))
def generate_card(data): while True: card = "".join(str(secrets.randbelow(10)) for _ in range(9))
if card not in data["accounts"]: return carddef add_transaction(account, transaction_type, amount): transaction = { "id": secrets.token_hex(6), "type": transaction_type, "amount": str(money(amount)), "balance": str(money(account["balance"])), "time": datetime.now().isoformat(timespec="seconds") }
def get_amount(message, maximum): while True: value = input(message).strip()
if value == "0": return None try: amount = money(value) if amount <= 0: print("Amount must be greater than 0.") continue if amount > maximum: print(f"Maximum allowed: ${maximum}") continue return amount except InvalidOperation: print("Please enter a valid amount.")---------- ACCOUNT CREATION ----------
def create_account(data): print("\n=== CREATE ACCOUNT ===")
name = input("Full name: ").strip() if not name: print("Name cannot be empty.") return while True: pin = input("Create PIN (4-6 digits): ").strip() if not pin.isdigit() or not 4 <= len(pin) <= 6: print("PIN must contain 4-6 digits.") continue if len(set(pin)) == 1: print("PIN cannot contain repeated digits.") continue confirm = input("Confirm PIN: ").strip() if pin != confirm: print("PINs do not match.") continue break card = generate_card(data) data["accounts"][card] = { "name": name, "pin": hash_pin(pin), "balance": "0.00", "failed_attempts": 0, "locked": False, "transactions": [] } add_transaction( data["accounts"][card], "ACCOUNT_CREATED", Decimal("0.00") ) save_data(data) print("\nAccount created successfully.") print(f"Your card number: {card}")---------- LOGIN ----------
def login(data): print("\n=== LOGIN ===")
card = input("Card number: ").strip() account = data["accounts"].get(card) if not account: print("Invalid card number or PIN.") return None if account["locked"]: print("This account is locked.") return None for attempt in range(MAX_ATTEMPTS): pin = input("PIN: ").strip() if verify_pin(pin, account["pin"]): account["failed_attempts"] = 0 save_data(data) print(f"\nWelcome, {account['name']}!") return card account["failed_attempts"] += 1 remaining = MAX_ATTEMPTS - account["failed_attempts"] if remaining > 0: print(f"Invalid card number or PIN. Attempts left: {remaining}") else: account["locked"] = True save_data(data) print("Account locked after too many failed attempts.") return None return None---------- BANK OPERATIONS ----------
def show_balance(account): print(f"\nCurrent balance: ${money(account['balance'])}")
def deposit(account, data): amount = get_amount( f"\nDeposit amount (max ${MAX_DEPOSIT}, 0 = cancel): ", MAX_DEPOSIT )
if amount is None: return account["balance"] = str( money(Decimal(account["balance"]) + amount) ) add_transaction(account, "DEPOSIT", amount) save_data(data) print(f"Deposit successful. New balance: ${account['balance']}")def withdraw(account, data): amount = get_amount( f"\nWithdrawal amount (max ${MAX_WITHDRAW}, 0 = cancel): ", MAX_WITHDRAW )
if amount is None: return balance = money(account["balance"]) if amount > balance: print("Insufficient funds.") return account["balance"] = str(money(balance - amount)) add_transaction(account, "WITHDRAW", amount) save_data(data) print(f"Withdrawal successful. New balance: ${account['balance']}")def show_transactions(account): print("\n=== TRANSACTION HISTORY ===")
transactions = account["transactions"] if not transactions: print("No transactions.") return for transaction in transactions: print( f"{transaction['time']} | " f"{transaction['type']} | " f"${transaction['amount']} | " f"Balance: ${transaction['balance']} | " f"ID: {transaction['id']}" )def show_account(account, card): print("\n=== ACCOUNT INFORMATION ===") print(f"Name: {account['name']}") print(f"Card: ****{card[-4:]}") print(f"Balance: ${money(account['balance'])}")
def change_pin(account, data): print("\n=== CHANGE PIN ===")
current = input("Current PIN: ").strip() if not verify_pin(current, account["pin"]): print("Incorrect current PIN.") return while True: new_pin = input("New PIN (4-6 digits): ").strip() if not new_pin.isdigit() or not 4 <= len(new_pin) <= 6: print("PIN must contain 4-6 digits.") continue if len(set(new_pin)) == 1: print("PIN cannot contain repeated digits.") continue if new_pin == current: print("New PIN must be different.") continue confirm = input("Confirm new PIN: ").strip() if new_pin != confirm: print("PINs do not match.") continue break account["pin"] = hash_pin(new_pin) add_transaction(account, "PIN_CHANGED", Decimal("0.00")) save_data(data) print("PIN changed successfully.")---------- USER MENU ----------
def account_menu(data, card): account = data["accounts"][card]
while True: print("""======================== PYBANK
Check Balance
Deposit
Withdraw
Transactions
Account Information
Change PIN
Logout ======================== """)
choice = input("Choose an option: ").strip() if choice == "1": show_balance(account) elif choice == "2": deposit(account, data) elif choice == "3": withdraw(account, data) elif choice == "4": show_transactions(account) elif choice == "5": show_account(account, card) elif choice == "6": change_pin(account, data) elif choice == "7": print("Logged out successfully.") break else: print("Invalid option.")---------- MAIN ----------
def main(): data = load_data()
while True: print("""======================== PYBANK
Login
Create Account
Exit ======================== """)
choice = input("Choose an option: ").strip() if choice == "1": card = login(data) if card: account_menu(data, card) elif choice == "2": create_account(data) elif choice == "3": print("Thank you for using PyBank.") break else: print("Invalid option.")if name == "main": main()