Spaces:
Runtime error
Runtime error
| from flask import request, jsonify | |
| from models import db, User, Transaction, Alert, PurityMemory | |
| from flask_jwt_extended import JWTManager, jwt_required, create_access_token, get_jwt_identity | |
| from werkzeug.security import generate_password_hash, check_password_hash | |
| from ai_review import ai_review_transaction | |
| from datetime import datetime | |
| import random, string, uuid | |
| jwt = JWTManager() | |
| TIER_LIMITS = { | |
| 0: {"daily": 0, "max_balance": 0, "label": "Unverified"}, | |
| 1: {"daily": 50000, "max_balance": 300000, "label": "Tier 1 - NIN"}, | |
| 2: {"daily": 200000, "max_balance": 500000, "label": "Tier 2 - NIN+BVN"}, | |
| 3: {"daily": 5000000, "max_balance": float('inf'), "label": "Tier 3 - Full KYC"}, | |
| } | |
| def generate_account_number(): | |
| while True: | |
| number = "ACC" + "".join(random.choices(string.digits, k=10)) | |
| if not User.query.filter_by(account_number=number).first(): | |
| return number | |
| def create_alert(account_number, message): | |
| db.session.add(Alert(account_number=account_number, message=message)) | |
| def register(): | |
| data = request.get_json() | |
| if User.query.filter_by(email=data["email"]).first(): | |
| return jsonify({"message": "Email already exists"}), 400 | |
| user = User( | |
| name=data["name"], | |
| email=data["email"], | |
| password=generate_password_hash(data["password"]), | |
| account_number=generate_account_number(), | |
| phone=data.get("phone", ""), | |
| ) | |
| db.session.add(user) | |
| db.session.commit() | |
| create_alert(user.account_number, f"Welcome to dolor3v, {user.name}! Complete KYC to unlock transfers.") | |
| db.session.commit() | |
| return jsonify({"message": "User registered successfully", "account_number": user.account_number}) | |
| def login(): | |
| data = request.get_json() | |
| user = User.query.filter_by(email=data["email"]).first() | |
| if not user or not check_password_hash(user.password, data["password"]): | |
| return jsonify({"message": "Invalid credentials"}), 401 | |
| access_token = create_access_token(identity=user.account_number) | |
| return jsonify({ | |
| "access_token": access_token, | |
| "account_number": user.account_number, | |
| "name": user.name, | |
| "tier": user.tier, | |
| }) | |
| def set_pin(): | |
| data = request.get_json() | |
| acc = get_jwt_identity() | |
| user = User.query.filter_by(account_number=acc).first() | |
| pin = data.get("pin", "") | |
| if len(pin) != 4 or not pin.isdigit(): | |
| return jsonify({"message": "PIN must be 4 digits"}), 400 | |
| user.pin = generate_password_hash(pin) | |
| db.session.commit() | |
| return jsonify({"message": "PIN set successfully"}) | |
| def verify_pin(): | |
| data = request.get_json() | |
| acc = get_jwt_identity() | |
| user = User.query.filter_by(account_number=acc).first() | |
| if not user.pin: | |
| return jsonify({"valid": False, "message": "No PIN set"}), 400 | |
| valid = check_password_hash(user.pin, data.get("pin", "")) | |
| return jsonify({"valid": valid}) | |
| def kyc_upgrade(): | |
| acc = get_jwt_identity() | |
| user = User.query.filter_by(account_number=acc).first() | |
| data = request.get_json() | |
| tier = data.get("tier", 1) | |
| if tier == 1: | |
| if not data.get("nin"): | |
| return jsonify({"message": "NIN required for Tier 1"}), 400 | |
| user.nin = data["nin"] | |
| user.tier = 1 | |
| user.daily_limit = 50000 | |
| user.max_balance = 300000 | |
| create_alert(acc, "KYC Tier 1 approved! Daily limit: ₦50,000") | |
| elif tier == 2: | |
| if not data.get("bvn"): | |
| return jsonify({"message": "BVN required for Tier 2"}), 400 | |
| user.bvn = data["bvn"] | |
| user.tier = 2 | |
| user.daily_limit = 200000 | |
| user.max_balance = 500000 | |
| create_alert(acc, "KYC Tier 2 approved! Daily limit: ₦200,000") | |
| elif tier == 3: | |
| user.tier = 3 | |
| user.daily_limit = 5000000 | |
| user.max_balance = float('inf') | |
| create_alert(acc, "KYC Tier 3 approved! Daily limit: ₦5,000,000. Unlimited balance.") | |
| db.session.commit() | |
| return jsonify({"message": f"Tier {tier} upgrade successful", "tier": user.tier}) | |
| def profile(): | |
| acc = get_jwt_identity() | |
| user = User.query.filter_by(account_number=acc).first() | |
| limits = TIER_LIMITS.get(user.tier, TIER_LIMITS[0]) | |
| return jsonify({ | |
| "name": user.name, | |
| "email": user.email, | |
| "phone": user.phone, | |
| "account_number": user.account_number, | |
| "tier": user.tier, | |
| "tier_label": limits["label"], | |
| "daily_limit": limits["daily"], | |
| "max_balance": limits["max_balance"] if limits["max_balance"] != float('inf') else -1, | |
| "balance": user.balance, | |
| "pin_set": user.pin is not None, | |
| }) | |
| def transfer(): | |
| data = request.get_json() | |
| sender_acc = get_jwt_identity() | |
| sender = User.query.filter_by(account_number=sender_acc).first() | |
| if sender.tier == 0: | |
| return jsonify({"message": "Complete KYC to make transfers"}), 403 | |
| if not sender.pin: | |
| return jsonify({"message": "Set a PIN before making transfers"}), 403 | |
| if not check_password_hash(sender.pin, data.get("pin", "")): | |
| return jsonify({"message": "Invalid PIN"}), 401 | |
| recipient = User.query.filter_by(account_number=data["recipient_account_number"]).first() | |
| if not sender or not recipient: | |
| return jsonify({"message": "Invalid account numbers"}), 404 | |
| if sender.account_number == recipient.account_number: | |
| return jsonify({"message": "Cannot transfer to same account"}), 400 | |
| amount = float(data["amount"]) | |
| limit = TIER_LIMITS[sender.tier]["daily"] | |
| if amount > limit: | |
| return jsonify({"message": f"Amount exceeds your daily limit of ₦{limit:,.0f}"}), 400 | |
| if sender.balance < amount: | |
| return jsonify({"message": "Insufficient funds"}), 400 | |
| decision, reason = ai_review_transaction(sender, recipient, amount, False) | |
| status = "pending" if decision == "hold" else "completed" | |
| if status == "completed": | |
| sender.balance -= amount | |
| recipient.balance += amount | |
| t = Transaction( | |
| sender_account_number=sender.account_number, | |
| recipient_account_number=recipient.account_number, | |
| amount=amount, status=status, is_external=False, | |
| reference=str(uuid.uuid4())[:8].upper(), ai_review=reason, | |
| ) | |
| db.session.add(t) | |
| create_alert(sender.account_number, f"Debit: ₦{amount:,.0f} to {recipient.name}. Ref: {t.reference}") | |
| create_alert(recipient.account_number, f"Credit: ₦{amount:,.0f} from {sender.name}. Ref: {t.reference}") | |
| db.session.commit() | |
| return jsonify({"message": f"Transfer {status}", "reference": t.reference, "ai_review": reason}) | |
| def external_transfer(): | |
| data = request.get_json() | |
| sender_acc = get_jwt_identity() | |
| sender = User.query.filter_by(account_number=sender_acc).first() | |
| if sender.tier == 0: | |
| return jsonify({"message": "Complete KYC to make transfers"}), 403 | |
| if not sender.pin: | |
| return jsonify({"message": "Set a PIN before making transfers"}), 403 | |
| if not check_password_hash(sender.pin, data.get("pin", "")): | |
| return jsonify({"message": "Invalid PIN"}), 401 | |
| amount = float(data["amount"]) | |
| limit = TIER_LIMITS[sender.tier]["daily"] | |
| if amount > limit: | |
| return jsonify({"message": f"Exceeds daily limit of ₦{limit:,.0f}"}), 400 | |
| if sender.balance < amount: | |
| return jsonify({"message": "Insufficient funds"}), 400 | |
| bank_name = data.get("bank_name", "External Bank") | |
| recipient_name = data.get("recipient_name", "Unknown") | |
| decision, reason = ai_review_transaction(sender, recipient_name, amount, True) | |
| status = "pending" if decision == "hold" else "processing" | |
| if status == "processing": | |
| sender.balance -= amount | |
| ref = str(uuid.uuid4())[:8].upper() | |
| t = Transaction( | |
| sender_account_number=sender.account_number, | |
| recipient_account_number=f"EXT:{bank_name}:{data.get('recipient_account')}", | |
| amount=amount, status=status, is_external=True, | |
| reference=ref, ai_review=reason, | |
| ) | |
| db.session.add(t) | |
| create_alert(sender.account_number, f"External transfer: ₦{amount:,.0f} to {recipient_name} @ {bank_name}. Ref: {ref}") | |
| db.session.commit() | |
| return jsonify({"message": f"Transfer {status}", "reference": ref, "ai_review": reason}) | |
| def buy_airtime(): | |
| data = request.get_json() | |
| acc = get_jwt_identity() | |
| user = User.query.filter_by(account_number=acc).first() | |
| if not user.pin or not check_password_hash(user.pin, data.get("pin", "")): | |
| return jsonify({"message": "Invalid PIN"}), 401 | |
| amount = float(data["amount"]) | |
| phone = data.get("phone") | |
| network = data.get("network", "MTN") | |
| if user.balance < amount: | |
| return jsonify({"message": "Insufficient funds"}), 400 | |
| user.balance -= amount | |
| ref = "AIR" + str(uuid.uuid4())[:6].upper() | |
| t = Transaction( | |
| sender_account_number=user.account_number, | |
| recipient_account_number=f"AIRTIME:{network}:{phone}", | |
| amount=amount, status="completed", is_external=True, | |
| reference=ref, ai_review="Airtime purchase", | |
| transaction_type="airtime", | |
| ) | |
| db.session.add(t) | |
| create_alert(user.account_number, f"Airtime: ₦{amount:,.0f} {network} sent to {phone}. Ref: {ref}") | |
| db.session.commit() | |
| return jsonify({"message": f"₦{amount:,.0f} {network} airtime sent to {phone}", "reference": ref}) | |
| def buy_data(): | |
| data = request.get_json() | |
| acc = get_jwt_identity() | |
| user = User.query.filter_by(account_number=acc).first() | |
| if not user.pin or not check_password_hash(user.pin, data.get("pin", "")): | |
| return jsonify({"message": "Invalid PIN"}), 401 | |
| amount = float(data["amount"]) | |
| phone = data.get("phone") | |
| network = data.get("network", "MTN") | |
| plan = data.get("plan", "1GB") | |
| if user.balance < amount: | |
| return jsonify({"message": "Insufficient funds"}), 400 | |
| user.balance -= amount | |
| ref = "DATA" + str(uuid.uuid4())[:6].upper() | |
| t = Transaction( | |
| sender_account_number=user.account_number, | |
| recipient_account_number=f"DATA:{network}:{phone}", | |
| amount=amount, status="completed", is_external=True, | |
| reference=ref, ai_review="Data purchase", | |
| transaction_type="data", | |
| ) | |
| db.session.add(t) | |
| create_alert(user.account_number, f"Data: {plan} {network} sent to {phone}. Ref: {ref}") | |
| db.session.commit() | |
| return jsonify({"message": f"{plan} {network} data sent to {phone}", "reference": ref}) | |
| def balance(): | |
| acc = get_jwt_identity() | |
| user = User.query.filter_by(account_number=acc).first() | |
| if not user: | |
| return jsonify({"message": "Invalid account number"}), 404 | |
| return jsonify({"balance": user.balance, "account_number": user.account_number, "name": user.name}) | |
| def history(): | |
| acc = get_jwt_identity() | |
| txns = Transaction.query.filter( | |
| (Transaction.sender_account_number == acc) | | |
| (Transaction.recipient_account_number == acc) | |
| ).order_by(Transaction.timestamp.desc()).all() | |
| result = [] | |
| for t in txns: | |
| result.append({ | |
| "id": t.id, | |
| "type": "sent" if t.sender_account_number == acc else "received", | |
| "amount": t.amount, | |
| "sender": t.sender_account_number, | |
| "recipient": t.recipient_account_number, | |
| "status": t.status, | |
| "reference": t.reference, | |
| "is_external": t.is_external, | |
| "transaction_type": t.transaction_type, | |
| "ai_review": t.ai_review, | |
| "timestamp": t.timestamp.isoformat(), | |
| }) | |
| return jsonify({"transactions": result}) | |
| def alerts(): | |
| acc = get_jwt_identity() | |
| unread = Alert.query.filter_by(account_number=acc, read=False).order_by(Alert.timestamp.desc()).all() | |
| result = [{"id": a.id, "message": a.message, "timestamp": a.timestamp.isoformat()} for a in unread] | |
| for a in unread: | |
| a.read = True | |
| db.session.commit() | |
| return jsonify({"alerts": result}) | |
| def lookup(): | |
| acc_num = request.args.get("account_number") | |
| user = User.query.filter_by(account_number=acc_num).first() | |
| if not user: | |
| return jsonify({"message": "Account not found"}), 404 | |
| return jsonify({"name": user.name, "account_number": user.account_number, "bank": "dolor3v"}) | |
| def purity_memory(): | |
| acc = get_jwt_identity() | |
| data = request.get_json() | |
| mem = PurityMemory(account_number=acc, memory=data.get("memory", "")) | |
| db.session.add(mem) | |
| db.session.commit() | |
| return jsonify({"message": "Memory saved"}) | |
| def get_purity_memory(): | |
| acc = get_jwt_identity() | |
| mems = PurityMemory.query.filter_by(account_number=acc).order_by(PurityMemory.timestamp.desc()).limit(20).all() | |
| return jsonify({"memories": [m.memory for m in mems]}) | |