diff --git a/bank.py b/bank.py
new file mode 100644
index 0000000..b000ea9
--- /dev/null
+++ b/bank.py
@@ -0,0 +1,3754 @@
+from flask import Flask, request, redirect, session, Response, jsonify
+import json, os, random, time, uuid
+from datetime import datetime
+from functools import wraps
+
+app = Flask(__name__)
+app.secret_key = "bank_secret_key_2025"
+app.config['SESSION_PERMANENT'] = True
+app.config['PERMANENT_SESSION_LIFETIME'] = 3600
+
+USERS_FILE = "users.json"
+HISTORY_FILE = "history.json"
+CARDS_FILE = "cards.json"
+SHOP_FILE = "shop.json"
+PAYMENTS_FILE = "payments.json" # Новый файл для платежей
+
+# ---------- ИНИЦИАЛИЗАЦИЯ ФАЙЛОВ ----------
+def init_files():
+ """Инициализация файлов (без удаления существующих)"""
+ # Создаем файлы если они не существуют
+ files_to_init = [
+ (USERS_FILE, {
+ "admin": {
+ "password": "admin123",
+ "balance": 100000,
+ "deposit": 0,
+ "credit": 0,
+ "role": "admin",
+ "full_name": "Администратор Системы",
+ "email": "admin@bank.com",
+ "api_key": None
+ }
+ }),
+ (HISTORY_FILE, []),
+ (CARDS_FILE, {}),
+ (SHOP_FILE, {"items": [], "ads": []}),
+ (PAYMENTS_FILE, []) # Инициализация файла платежей
+ ]
+
+ for filename, default_data in files_to_init:
+ if not os.path.exists(filename):
+ with open(filename, "w", encoding='utf-8') as f:
+ json.dump(default_data, f, indent=4, ensure_ascii=False)
+
+# Инициализируем файлы при запуске
+init_files()
+
+# ---------- ФУНКЦИИ ДЛЯ РАБОТЫ С ДАННЫМИ ----------
+def load_users():
+ try:
+ with open(USERS_FILE, 'r', encoding='utf-8') as f:
+ data = json.load(f)
+ # Убеждаемся, что данные в правильном формате
+ if not isinstance(data, dict):
+ print("Warning: users.json is not a dictionary, resetting to default")
+ data = {
+ "admin": {
+ "password": "admin123",
+ "balance": 100000,
+ "deposit": 0,
+ "credit": 0,
+ "role": "admin",
+ "full_name": "Администратор Системы",
+ "email": "admin@bank.com",
+ "api_key": None
+ }
+ }
+ save_users(data)
+ return data
+ # Проверяем, что все значения являются словарями
+ cleaned_data = {}
+ for username, user_data in data.items():
+ if isinstance(user_data, dict):
+ cleaned_data[username] = user_data
+ else:
+ print(f"Warning: User {username} data is not a dict, resetting")
+ cleaned_data[username] = {
+ "password": "reset123",
+ "balance": 1000,
+ "deposit": 0,
+ "credit": 0,
+ "role": "client",
+ "full_name": "Пользователь",
+ "email": "user@example.com",
+ "api_key": None
+ }
+ if cleaned_data != data:
+ save_users(cleaned_data)
+ return cleaned_data
+ except Exception as e:
+ print(f"Error loading users: {e}")
+ data = {
+ "admin": {
+ "password": "admin123",
+ "balance": 100000,
+ "deposit": 0,
+ "credit": 0,
+ "role": "admin",
+ "full_name": "Администратор Системы",
+ "email": "admin@bank.com",
+ "api_key": None
+ }
+ }
+ save_users(data)
+ return data
+
+def save_users(data):
+ with open(USERS_FILE, "w", encoding='utf-8') as f:
+ json.dump(data, f, indent=4, ensure_ascii=False)
+
+def load_history():
+ try:
+ with open(HISTORY_FILE, 'r', encoding='utf-8') as f:
+ return json.load(f)
+ except:
+ return []
+
+def save_history(data):
+ with open(HISTORY_FILE, "w", encoding='utf-8') as f:
+ json.dump(data, f, indent=4, ensure_ascii=False)
+
+def load_cards():
+ try:
+ with open(CARDS_FILE, 'r', encoding='utf-8') as f:
+ return json.load(f)
+ except:
+ return {}
+
+def save_cards(data):
+ with open(CARDS_FILE, "w", encoding='utf-8') as f:
+ json.dump(data, f, indent=4, ensure_ascii=False)
+
+# ---------- ФУНКЦИИ ДЛЯ ПЛАТЕЖЕЙ ----------
+def load_payments():
+ """Загрузка платежей"""
+ try:
+ with open(PAYMENTS_FILE, 'r', encoding='utf-8') as f:
+ return json.load(f)
+ except:
+ return []
+
+def save_payments(data):
+ """Сохранение платежей"""
+ with open(PAYMENTS_FILE, "w", encoding='utf-8') as f:
+ json.dump(data, f, indent=4, ensure_ascii=False)
+
+def create_payment(payer, receiver, amount, description="", external_id=""):
+ """Создание платежа"""
+ payments = load_payments()
+
+ payment_id = str(uuid.uuid4())[:12]
+ payment_data = {
+ "id": payment_id,
+ "payer": payer,
+ "receiver": receiver,
+ "amount": int(amount),
+ "description": description,
+ "external_id": external_id,
+ "created": int(time.time()),
+ "status": "pending", # pending, completed, failed, cancelled
+ "completed_at": None
+ }
+
+ payments.append(payment_data)
+ save_payments(payments)
+
+ return payment_id, payment_data
+
+def process_payment(payment_id, payer_username=None):
+ """Обработка платежа"""
+ payments = load_payments()
+ users = load_users()
+
+ payment = None
+ for p in payments:
+ if p["id"] == payment_id:
+ payment = p
+ break
+
+ if not payment:
+ return False, "Платеж не найден"
+
+ if payment["status"] != "pending":
+ return False, f"Платеж уже обработан (статус: {payment['status']})"
+
+ # Если передан payer_username, проверяем что это тот же плательщик
+ if payer_username and payment["payer"] != payer_username:
+ return False, "Неверный плательщик"
+
+ payer = payment["payer"]
+ receiver = payment["receiver"]
+ amount = payment["amount"]
+
+ # Проверяем существование пользователей
+ if payer not in users:
+ return False, "Плательщик не найден"
+
+ if receiver not in users:
+ return False, "Получатель не найден"
+
+ # Проверяем баланс плательщика
+ if users[payer]["balance"] < amount:
+ return False, "Недостаточно средств"
+
+ # Выполняем перевод
+ users[payer]["balance"] -= amount
+ users[receiver]["balance"] += amount
+ save_users(users)
+
+ # Обновляем статус платежа
+ for p in payments:
+ if p["id"] == payment_id:
+ p["status"] = "completed"
+ p["completed_at"] = int(time.time())
+ break
+
+ save_payments(payments)
+
+ # Добавляем в историю
+ add_history(payer, "payment_sent", amount, f"Платеж #{payment_id} для {receiver}")
+ add_history(receiver, "payment_received", amount, f"Платеж #{payment_id} от {payer}")
+
+ return True, "Платеж успешно выполнен"
+
+def cancel_payment(payment_id):
+ """Отмена платежа"""
+ payments = load_payments()
+
+ for payment in payments:
+ if payment["id"] == payment_id and payment["status"] == "pending":
+ payment["status"] = "cancelled"
+ save_payments(payments)
+ return True, "Платеж отменен"
+
+ return False, "Платеж не найден или уже обработан"
+
+# ---------- ФУНКЦИИ ДЛЯ МАГАЗИНА ----------
+def load_shop():
+ """Загрузка данных магазина"""
+ try:
+ with open(SHOP_FILE, 'r', encoding='utf-8') as f:
+ data = json.load(f)
+ # Обеспечиваем наличие всех ключей
+ if "items" not in data:
+ data["items"] = []
+ if "ads" not in data:
+ data["ads"] = []
+ return data
+ except:
+ return {"items": [], "ads": []}
+
+def save_shop(data):
+ """Сохранение данных магазина"""
+ with open(SHOP_FILE, "w", encoding='utf-8') as f:
+ json.dump(data, f, indent=4, ensure_ascii=False)
+
+def create_shop_item(seller_username, title, description, price, category="other"):
+ """Создание товара в магазине"""
+ shop_data = load_shop()
+
+ item_id = str(uuid.uuid4())[:8]
+ new_item = {
+ "id": item_id,
+ "seller": seller_username,
+ "title": title,
+ "description": description,
+ "price": int(price),
+ "category": category,
+ "created": int(time.time()),
+ "status": "available",
+ "buyer": None
+ }
+
+ shop_data["items"].append(new_item)
+ save_shop(shop_data)
+
+ # Добавляем в историю
+ add_history(seller_username, "create_shop_item", 0, f"Товар: {title}")
+
+ return item_id
+
+def create_advertisement(author, title, content, contact_info):
+ """Создание объявления"""
+ shop_data = load_shop()
+
+ ad_id = str(uuid.uuid4())[:8]
+ new_ad = {
+ "id": ad_id,
+ "author": author,
+ "title": title,
+ "content": content,
+ "contact_info": contact_info,
+ "created": int(time.time()),
+ "status": "active"
+ }
+
+ shop_data["ads"].append(new_ad)
+ save_shop(shop_data)
+
+ # Добавляем в историю
+ add_history(author, "create_advertisement", 0, f"Объявление: {title}")
+
+ return ad_id
+
+def buy_item(item_id, buyer_username):
+ """Покупка товара"""
+ shop_data = load_shop()
+ users = load_users()
+
+ # Находим товар
+ item_to_buy = None
+ for item in shop_data["items"]:
+ if item["id"] == item_id and item["status"] == "available":
+ item_to_buy = item
+ break
+
+ if not item_to_buy:
+ return False, "Товар не найден или уже продан"
+
+ seller = item_to_buy["seller"]
+ price = item_to_buy["price"]
+
+ # Проверяем, есть ли у покупателя достаточно средств
+ if buyer_username not in users:
+ return False, "Покупатель не найден"
+
+ if users[buyer_username]["balance"] < price:
+ return False, "Недостаточно средств"
+
+ # Проверяем, что покупатель не покупает у себя
+ if buyer_username == seller:
+ return False, "Нельзя купить собственный товар"
+
+ # Проверяем, существует ли продавец
+ if seller not in users:
+ return False, "Продавец не найден"
+
+ # Выполняем транзакцию
+ users[buyer_username]["balance"] -= price
+ users[seller]["balance"] += price
+ save_users(users)
+
+ # Обновляем статус товара
+ for item in shop_data["items"]:
+ if item["id"] == item_id:
+ item["status"] = "sold"
+ item["buyer"] = buyer_username
+ item["sold_date"] = int(time.time())
+ break
+
+ save_shop(shop_data)
+
+ # Добавляем в историю
+ add_history(buyer_username, "buy_item", price, f"Товар: {item_to_buy['title']} от {seller}")
+ add_history(seller, "sell_item", price, f"Товар: {item_to_buy['title']} покупателю {buyer_username}")
+
+ return True, "Покупка успешно завершена"
+
+# ---------- API КЛЮЧИ ----------
+def generate_api_key():
+ """Генерация API ключа"""
+ return f"bank_api_{uuid.uuid4().hex[:16]}"
+
+def get_user_by_api_key(api_key):
+ """Получение пользователя по API ключу - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ if not api_key:
+ return None, None
+
+ users = load_users()
+ for username, user_data in users.items():
+ # Проверяем, что user_data является словарем
+ if isinstance(user_data, dict) and user_data.get('api_key') == api_key:
+ return username, user_data
+ return None, None
+
+# ---------- БАНКОВСКИЕ КАРТЫ ----------
+def generate_card_number(user_id, card_type="standard"):
+ """Генерация номера банковской карты"""
+ prefixes = {
+ "standard": ["5469", "4276", "5375", "4856"],
+ "gold": ["5469", "5278", "5375"],
+ "platinum": ["4475", "4876", "4999"]
+ }
+
+ prefix = random.choice(prefixes.get(card_type, prefixes["standard"]))
+ middle = ''.join([str(random.randint(0, 9)) for _ in range(8)])
+ last_digits = ''.join([str(random.randint(0, 9)) for _ in range(4)])
+
+ card_number = f"{prefix}{middle}{last_digits}"
+ formatted = ' '.join([card_number[i:i+4] for i in range(0, len(card_number), 4)])
+
+ return formatted
+
+def generate_cvv():
+ """Генерация CVV кода"""
+ return str(random.randint(100, 999))
+
+def generate_expiry_date():
+ """Генерация срока действия карты"""
+ from datetime import datetime, timedelta
+ expiry = datetime.now() + timedelta(days=365*3)
+ return expiry.strftime("%m/%y")
+
+def get_user_cards(user_id):
+ """Получение всех карт пользователя"""
+ cards = load_cards()
+ return cards.get(user_id, [])
+
+def create_user_card(user_id, user_data, card_type="standard"):
+ """Создание новой карты для пользователя"""
+ cards = load_cards()
+
+ if user_id not in cards:
+ cards[user_id] = []
+
+ # Проверяем, не слишком ли много карт у пользователя
+ if len(cards[user_id]) >= 5:
+ return None
+
+ card_id = str(uuid.uuid4())[:8]
+
+ new_card = {
+ "id": card_id,
+ "number": generate_card_number(user_id, card_type),
+ "holder": user_data.get('full_name', user_id.upper()),
+ "type": card_type,
+ "cvv": generate_cvv(),
+ "expiry": generate_expiry_date(),
+ "balance": user_data.get('balance', 0),
+ "currency": "USD",
+ "created": int(time.time()),
+ "active": True,
+ "limit": 10000 if card_type == "standard" else 50000 if card_type == "gold" else 100000,
+ "color": {
+ "standard": "#4361ee",
+ "gold": "#ffd166",
+ "platinum": "#e5e5e5"
+ }.get(card_type, "#4361ee")
+ }
+
+ cards[user_id].append(new_card)
+ save_cards(cards)
+
+ # Добавляем в историю
+ add_history(user_id, f"create_{card_type}_card", 0, f"Card {new_card['number'][-4:]}")
+
+ return new_card
+
+# ---------- БАНКОВСКИЕ ОПЕРАЦИИ ----------
+def daily_update():
+ """Ежедневное обновление депозитов и кредитов"""
+ users = load_users()
+ updated = False
+ for user_id, u in users.items():
+ if isinstance(u, dict): # Проверяем, что это словарь
+ if u.get('deposit', 0) > 0:
+ u['deposit'] = int(u['deposit'] * 1.01)
+ updated = True
+ if u.get('credit', 0) > 0:
+ u['credit'] = int(u['credit'] * 1.05)
+ updated = True
+ if updated:
+ save_users(users)
+
+def add_history(user, action, amount, target=None):
+ """Добавление записи в историю"""
+ hist = load_history()
+ hist.append({
+ "time": int(time.time()),
+ "user": user,
+ "action": action,
+ "amount": amount,
+ "target": target,
+ "id": str(uuid.uuid4())[:8]
+ })
+ save_history(hist)
+
+# ---------- ДЕКОРАТОРЫ ----------
+def login_required(f):
+ """Декоратор для проверки авторизации"""
+ @wraps(f)
+ def decorated_function(*args, **kwargs):
+ if 'user' not in session:
+ return redirect('/')
+ return f(*args, **kwargs)
+ return decorated_function
+
+def admin_required(f):
+ """Декоратор для проверки прав администратора"""
+ @wraps(f)
+ def decorated_function(*args, **kwargs):
+ if 'user' not in session:
+ return redirect('/')
+
+ users = load_users()
+ user_data = users.get(session['user'])
+
+ if not user_data or not isinstance(user_data, dict) or user_data.get('role') != 'admin':
+ return redirect('/dashboard')
+
+ return f(*args, **kwargs)
+ return decorated_function
+
+# ИСПРАВЛЕННЫЙ ДЕКОРАТОР API
+def api_key_required(f):
+ """Декоратор для проверки API ключа - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ @wraps(f)
+ def decorated_function(*args, **kwargs):
+ api_key = None
+
+ # 1. Проверяем заголовок X-API-Key
+ if 'X-API-Key' in request.headers:
+ api_key = request.headers.get('X-API-Key')
+
+ # 2. Проверяем параметр запроса api_key
+ if not api_key and request.args.get('api_key'):
+ api_key = request.args.get('api_key')
+
+ # 3. Проверяем JSON тело
+ if not api_key and request.is_json:
+ try:
+ data = request.get_json()
+ if data and 'api_key' in data:
+ api_key = data.get('api_key')
+ except:
+ pass
+
+ # 4. Проверяем форму
+ if not api_key and request.form.get('api_key'):
+ api_key = request.form.get('api_key')
+
+ if not api_key:
+ return jsonify({'success': False, 'error': 'API key required'}), 401
+
+ # Получаем пользователя по API ключу
+ username, user_data = get_user_by_api_key(api_key)
+ if not username:
+ return jsonify({'success': False, 'error': 'Invalid API key'}), 401
+
+ # Сохраняем данные пользователя в объекте request
+ request.username = username
+ request.user_data = user_data
+
+ return f(*args, **kwargs)
+ return decorated_function
+
+# ---------- HTML ШАБЛОНЫ ----------
+def render_login():
+ return '''
+
+
+
+
+
+ Банк | Вход в систему
+
+
+
+
+
+
+
+
+
+'''
+
+def render_register():
+ return '''
+
+
+
+
+
+ Банк | Регистрация
+
+
+
+
+
+
+
+
+
+'''
+
+def render_card_html(card, width="400px"):
+ """Генерация HTML для банковской карты"""
+ colors = {
+ "standard": "#4361ee",
+ "gold": "#ffd166",
+ "platinum": "#e5e5e5"
+ }
+
+ bg_color = card.get('color', colors.get(card['type'], "#4361ee"))
+ text_color = "#ffffff" if card['type'] == 'standard' else "#000000"
+
+ return f'''
+
+
+
+ {card['number']}
+
+
+ {card['holder']}
+
+
+'''
+
+def render_dashboard(user_data, history_data, user_cards, shop_data=None):
+ """Генерация HTML личного кабинета"""
+ # Форматирование времени истории
+ for h in history_data:
+ h['formatted_time'] = datetime.fromtimestamp(h['time']).strftime('%d.%m.%Y %H:%M')
+
+ # HTML для истории
+ history_html = ""
+ for h in history_data[-10:][::-1]:
+ icon = "fa-history"
+ color = "text-primary"
+ if "deposit" in h['action']:
+ icon = "fa-piggy-bank"
+ color = "text-info"
+ elif "credit" in h['action']:
+ icon = "fa-credit-card"
+ color = "text-warning"
+ elif "transfer" in h['action']:
+ icon = "fa-exchange-alt"
+ color = "text-primary"
+ elif "card" in h['action']:
+ icon = "fa-credit-card"
+ color = "text-success"
+ elif "api" in h['action']:
+ icon = "fa-key"
+ color = "text-secondary"
+ elif "shop" in h['action'] or "item" in h['action'] or "advertisement" in h['action']:
+ icon = "fa-shopping-cart"
+ color = "text-success"
+ elif "payment" in h['action']:
+ icon = "fa-credit-card"
+ color = "text-success"
+
+ history_html += f'''
+
+ {h['formatted_time']}
+ {h['user']}
+ {h['action']}
+ {h['amount']:,} $
+ {h.get('target', '-')}
+
+ '''
+
+ # HTML для банковских карт
+ cards_html = ""
+ if user_cards:
+ for card in user_cards:
+ width = "350px" if len(card['number']) < 24 else "400px" if len(card['number']) < 28 else "450px"
+ cards_html += f'''
+
+
+
+
+
+ Карта {card['type'].upper()}
+
+ {render_card_html(card, width)}
+
+
CVV: {card['cvv']}
+
Срок: {card['expiry']}
+
Баланс: ${card['balance']:,}
+
Лимит: ${card['limit']:,}
+
+
+
+
+
+ '''
+ else:
+ cards_html = '''
+
+
+
+ У вас еще нет банковских карт. Создайте свою первую карту!
+
+
+ '''
+
+ # HTML для API ключа
+ api_key_html = ""
+ if user_data.get('api_key'):
+ api_key_html = f'''
+
+
Ваш API ключ
+
+
+
+
+
+
+
Используйте этот ключ для доступа к API
+
+ '''
+ else:
+ api_key_html = '''
+
+
API ключ не создан
+
+
+ '''
+
+ # HTML для магазина
+ shop_tab_content = ""
+ if shop_data:
+ # Товары
+ shop_items_html = ""
+ available_items = [item for item in shop_data["items"] if item["status"] == "available"]
+
+ if available_items:
+ for item in available_items[-10:][::-1]: # Последние 10 товаров
+ created_date = datetime.fromtimestamp(item["created"]).strftime('%d.%m.%Y')
+ shop_items_html += f'''
+
+
+
+
{item["title"]}
+
{item["description"]}
+
+ Продавец: {item["seller"]}
+ Категория: {item["category"]}
+ Дата: {created_date}
+
+
+ ${item["price"]:,}
+
+
+
+
+
+ '''
+ else:
+ shop_items_html = '''
+
+
+
+ В магазине пока нет товаров. Будьте первым, кто добавит товар!
+
+
+ '''
+
+ # Объявления
+ ads_html = ""
+ active_ads = [ad for ad in shop_data["ads"] if ad["status"] == "active"]
+
+ if active_ads:
+ for ad in active_ads[-10:][::-1]: # Последние 10 объявлений
+ created_date = datetime.fromtimestamp(ad["created"]).strftime('%d.%m.%Y')
+ ads_html += f'''
+
+
+
+
{ad["title"]}
+
{ad["content"]}
+
+ Автор: {ad["author"]}
+ Контакты: {ad["contact_info"]}
+ Дата: {created_date}
+
+
+
+
+ '''
+
+ shop_tab_content = f'''
+
+
+
+
+
Магазин
+
+
+ Добавить товар
+
+
+ Добавить объявление
+
+
+
+
+
+
+
Товары в магазине
+
+ {shop_items_html}
+
+
+
Объявления
+
+ {ads_html}
+
+
+ '''
+
+ # HTML для админ-панели
+ admin_tab = ""
+ if user_data.get('role') == 'admin':
+ admin_tab = '''
+
+
+ Админ-панель
+
+
+ '''
+
+ return f'''
+
+
+
+
+
+ Банк | Личный кабинет
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Вклады
+
Откройте вклад под 1% ежедневно и получайте пассивный доход.
+
+
+
Кредиты
+
Получите кредит на любые цели. Ставка 5% ежедневно.
+
+
+
Переводы
+
Быстрые переводы между клиентами банка без комиссий.
+
+
+
Банковские карты
+
Выпустите банковскую карту для удобных платежей.
+
+
+
Магазин
+
Покупайте и продавайте товары, размещайте объявления.
+
+
+
Платежи
+
Принимайте платежи от клиентов через API или ссылки.
+
+
+
API Доступ
+
Интегрируйте банковские услуги в ваши приложения через API.
+
+
+
+
+
+
+ {shop_tab_content}
+
+
+
+
+
+
+ Время
+ Пользователь
+ Операция
+ Сумма
+ Получатель
+
+
+
+ {history_html}
+
+
+
+
+
+
+
+
Документация API
+
Используйте ваш API ключ для доступа к банковским операциям через REST API.
+
+
Базовый URL
+
+ http://ваш-домен/api/v1/
+
+
+
Аутентификация
+
Добавьте заголовок с вашим API ключом:
+
+ X-API-Key: ваш_api_ключ
+
+
Или передайте как параметр:
+
+ ?api_key=ваш_api_ключ
+
+
+
Доступные методы
+
+
+
+
+ Метод
+ Эндпоинт
+ Описание
+ Параметры
+
+
+
+
+ GET
+ /api/v1/balance
+ Получить информацию о балансе
+ -
+
+
+ GET
+ /api/v1/history
+ Получить историю операций
+ limit (опционально)
+
+
+ GET
+ /api/v1/cards
+ Получить список карт
+ -
+
+
+ POST
+ /api/v1/deposit
+ Открыть вклад
+ amount (int)
+
+
+ POST
+ /api/v1/credit
+ Взять кредит
+ amount (int)
+
+
+ POST
+ /api/v1/transfer
+ Сделать перевод
+ target (str), amount (int)
+
+
+ POST
+ /api/v1/cards/create
+ Создать карту
+ card_type (standard|gold|platinum)
+
+
+ POST
+ /api/v1/cards/delete
+ Удалить карту
+ card_id (str)
+
+
+ GET
+ /api/v1/shop/items
+ Получить список товаров
+ -
+
+
+ GET
+ /api/v1/shop/ads
+ Получить список объявлений
+ -
+
+
+ POST
+ /api/v1/payment/create
+ Создать платеж
+ receiver, amount, description
+
+
+ POST
+ /api/v1/payment/process
+ Выполнить платеж
+ payment_id
+
+
+
+
+
+
Примеры использования
+
Python с requests:
+
+import requests
+
+api_key = "ваш_api_ключ"
+base_url = "http://ваш-домен/api/v1"
+
+# Получить баланс
+headers = {{"X-API-Key": api_key}}
+response = requests.get(f"{{base_url}}/balance", headers=headers)
+print(response.json())
+
+# Сделать перевод
+data = {{"target": "получатель", "amount": 100}}
+response = requests.post(f"{{base_url}}/transfer", headers=headers, json=data)
+print(response.json())
+
+
+
cURL:
+
+# Получить баланс
+curl -H "X-API-Key: ваш_api_ключ" http://ваш-домен/api/v1/balance
+
+# Сделать перевод
+curl -X POST -H "X-API-Key: ваш_api_ключ" \\
+ -H "Content-Type: application/json" \\
+ -d '{{"target": "получатель", "amount": 100}}' \\
+ http://ваш-домен/api/v1/transfer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Заголовок
+
+
+
+ Содержание
+
+
+
+ Контактная информация
+
+
+
+
+
+
+
+
+
+
+
+
+
+'''
+
+def render_admin_panel(users):
+ """Генерация HTML админ-панели - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ users_html = ""
+ current_user = session.get('user')
+
+ valid_users_count = 0
+ total_balance = 0
+ total_deposit = 0
+ total_credit = 0
+
+ for username, user in users.items():
+ # Пропускаем текущего пользователя
+ if username == current_user:
+ continue
+
+ # Проверяем, что пользователь - словарь (корректные данные)
+ if not isinstance(user, dict):
+ continue
+
+ # Безопасно получаем данные пользователя
+ role = user.get('role', 'client')
+ balance = user.get('balance', 0)
+ deposit = user.get('deposit', 0)
+ credit = user.get('credit', 0)
+ email = user.get('email', 'нет')
+
+ # Обновляем статистику
+ valid_users_count += 1
+ total_balance += balance
+ total_deposit += deposit
+ total_credit += credit
+
+ # Определяем цвет бейджа роли
+ badge_color = "success" if role == 'admin' else "primary" if role == 'manager' else "secondary"
+
+ users_html += f'''
+
+
+
+
{username}
+
+ Роль: {role}
+ Баланс: ${balance:,}
+ Вклад: ${deposit:,}
+ Кредит: ${credit:,}
+ Email: {email}
+
+
+
+
+ Клиент
+ Админ
+ Менеджер
+
+ Изменить роль
+
+
+
+
+
+ +
+
+
+
+
+
+ '''
+
+ return f'''
+
+
+
+
+
+ Админ-панель
+
+
+
+
+
+
+
+
+
+ {users_html if users_html else '''
+
+
+
+ Нет других пользователей для управления.
+
+
+ '''}
+
+
+
+
+
+
+
Статистика системы
+
+
+
+
Всего пользователей
+ {valid_users_count + 1}
+
+
+
+
+
Общий баланс
+ ${total_balance:,}
+
+
+
+
+
Общие вклады
+ ${total_deposit:,}
+
+
+
+
+
Общие кредиты
+ ${total_credit:,}
+
+
+
+
+
+
+
+
+
+
+
+
+'''
+
+# ---------- СТРАНИЦА ОПЛАТЫ ----------
+def render_payment_page():
+ """Страница оплаты"""
+ return '''
+
+
+
+
+
+ Оплата | Банковская система
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Создать ссылку для оплаты
+
+
+ Получатель (логин)
+
+
+
+ Сумма ($)
+
+
+
+ Описание платежа
+
+
+
+ Создать ссылку
+
+
+
+
+
QR-код для оплаты
+
+ QR-код появится после создания ссылки
+
+
+
+
+
+
API для оплаты
+
Используйте API для интеграции платежей в ваши приложения:
+
+
1. Создать платеж:
+
+ POST /api/v1/payment/create
+ Headers: X-API-Key: ваш_ключ
+ Body: {
+ "receiver": "username",
+ "amount": 100,
+ "description": "Оплата за услугу"
+ }
+
+
+
2. Получить статус платежа:
+
+ GET /api/v1/payment/status/{payment_id}
+ Headers: X-API-Key: ваш_ключ
+
+
+
3. Пример ссылки для оплаты:
+
+ http://ваш-домен/pay/{payment_id}
+
+
+
+
Как это работает:
+
+ Создаете платеж через форму или API
+ Получаете уникальную ссылку или payment_id
+ Отправляете ссылку клиенту
+ Клиент переходит по ссылке и оплачивает
+ Вы получаете деньги на счет
+
+
+
+
+
+
+
Последние платежи
+
+
+
+
+ ID
+ Сумма
+ Получатель
+ Статус
+ Дата
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+'''
+
+def render_pay_page(payment_id):
+ """Страница оплаты для клиента"""
+ payments = load_payments()
+
+ payment = None
+ for p in payments:
+ if p["id"] == payment_id:
+ payment = p
+ break
+
+ if not payment:
+ return '''
+
+
+
+ Платеж не найден
+
+
+
+
+
+
Платеж не найден
+
Проверьте правильность ссылки или обратитесь к отправителю.
+
На главную
+
+
+
+
+ '''
+
+ if payment["status"] != "pending":
+ status_message = {
+ "completed": "Оплачен",
+ "failed": "Не удался",
+ "cancelled": "Отменен"
+ }.get(payment["status"], "Неизвестен")
+
+ return f'''
+
+
+
+ Статус платежа
+
+
+
+
+
+
+
+
+
+
Платеж {status_message}
+
+
+
+
ID платежа: {payment_id}
+
Сумма: ${payment['amount']}
+
Получатель: {payment['receiver']}
+
Описание: {payment['description']}
+
На главную
+
+
+
+
+
+
+
+ '''
+
+ return f'''
+
+
+
+
+
+ Оплата #{payment_id}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${payment['amount']}
+
+
+
+
+ Получатель:
+ {payment['receiver']}
+
+
+ Описание:
+ {payment['description'] or 'Нет описания'}
+
+
+ Статус:
+ Ожидает оплаты
+
+
+
+
+
+ Ваш логин
+
+
+
+ Пароль
+
+
+
+
+ Оплатить ${payment['amount']}
+
+
+
+ После оплаты средства будут переведены получателю
+
+
+
+
+
Безопасная оплата
+
Все платежи защищены банковской системой. Для оплаты требуется авторизация.
+
+
+
+
+
+
+
+
+
+'''
+
+# ---------- ИСПРАВЛЕННЫЕ API МАРШРУТЫ ----------
+
+@app.route('/api/v1/balance', methods=['GET'])
+@api_key_required
+def api_balance():
+ """API: Получить информацию о балансе - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ try:
+ user_data = request.user_data
+ return jsonify({
+ 'success': True,
+ 'username': request.username,
+ 'balance': user_data.get('balance', 0),
+ 'deposit': user_data.get('deposit', 0),
+ 'credit': user_data.get('credit', 0),
+ 'total': user_data.get('balance', 0) + user_data.get('deposit', 0) - user_data.get('credit', 0)
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/history', methods=['GET'])
+@api_key_required
+def api_history():
+ """API: Получить историю операций - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ try:
+ history = load_history()
+ user_history = [h for h in history if h['user'] == request.username]
+
+ limit = request.args.get('limit', type=int, default=50)
+ if limit > 0 and limit < len(user_history):
+ user_history = user_history[-limit:]
+
+ for h in user_history:
+ h['time_formatted'] = datetime.fromtimestamp(h['time']).strftime('%Y-%m-%d %H:%M:%S')
+
+ return jsonify({
+ 'success': True,
+ 'count': len(user_history),
+ 'history': user_history[::-1]
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/cards', methods=['GET'])
+@api_key_required
+def api_cards():
+ """API: Получить список карт - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ try:
+ cards = get_user_cards(request.username)
+
+ safe_cards = []
+ for card in cards:
+ safe_card = card.copy()
+ if 'number' in safe_card and len(safe_card['number']) > 4:
+ safe_card['number_masked'] = '**** **** **** ' + safe_card['number'][-4:]
+ safe_card['cvv'] = '***'
+ safe_cards.append(safe_card)
+
+ return jsonify({
+ 'success': True,
+ 'count': len(safe_cards),
+ 'cards': safe_cards
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/deposit', methods=['POST'])
+@api_key_required
+def api_deposit():
+ """API: Открыть вклад - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ try:
+ if not request.is_json:
+ return jsonify({'success': False, 'error': 'JSON data required'}), 400
+
+ data = request.get_json()
+ amount = data.get('amount')
+
+ if not amount:
+ return jsonify({'success': False, 'error': 'Amount required'}), 400
+
+ amount = int(amount)
+ if amount <= 0:
+ return jsonify({'success': False, 'error': 'Amount must be positive'}), 400
+
+ users = load_users()
+ user = users.get(request.username)
+
+ if not user or user['balance'] < amount:
+ return jsonify({'success': False, 'error': 'Insufficient funds'}), 400
+
+ user['balance'] -= amount
+ user['deposit'] += amount
+ save_users(users)
+
+ add_history(request.username, 'api_deposit', amount)
+
+ return jsonify({
+ 'success': True,
+ 'message': f'Deposit of ${amount} created successfully',
+ 'new_balance': user['balance'],
+ 'new_deposit': user['deposit']
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/credit', methods=['POST'])
+@api_key_required
+def api_credit():
+ """API: Взять кредит - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ try:
+ if not request.is_json:
+ return jsonify({'success': False, 'error': 'JSON data required'}), 400
+
+ data = request.get_json()
+ amount = data.get('amount')
+
+ if not amount:
+ return jsonify({'success': False, 'error': 'Amount required'}), 400
+
+ amount = int(amount)
+ if amount <= 0:
+ return jsonify({'success': False, 'error': 'Amount must be positive'}), 400
+
+ users = load_users()
+ user = users.get(request.username)
+
+ if not user:
+ return jsonify({'success': False, 'error': 'User not found'}), 404
+
+ user['balance'] += amount
+ user['credit'] += amount
+ save_users(users)
+
+ add_history(request.username, 'api_credit', amount)
+
+ return jsonify({
+ 'success': True,
+ 'message': f'Credit of ${amount} taken successfully',
+ 'new_balance': user['balance'],
+ 'new_credit': user['credit']
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/transfer', methods=['POST'])
+@api_key_required
+def api_transfer():
+ """API: Сделать перевод - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ try:
+ if not request.is_json:
+ return jsonify({'success': False, 'error': 'JSON data required'}), 400
+
+ data = request.get_json()
+ target = data.get('target', '').strip()
+ amount = data.get('amount')
+
+ if not target:
+ return jsonify({'success': False, 'error': 'Target username required'}), 400
+
+ if not amount:
+ return jsonify({'success': False, 'error': 'Amount required'}), 400
+
+ amount = int(amount)
+ if amount <= 0:
+ return jsonify({'success': False, 'error': 'Amount must be positive'}), 400
+
+ users = load_users()
+
+ if target not in users:
+ return jsonify({'success': False, 'error': 'Target user not found'}), 404
+
+ if target == request.username:
+ return jsonify({'success': False, 'error': 'Cannot transfer to yourself'}), 400
+
+ sender = users.get(request.username)
+ if not sender:
+ return jsonify({'success': False, 'error': 'Sender not found'}), 404
+
+ if sender['balance'] < amount:
+ return jsonify({'success': False, 'error': 'Insufficient funds'}), 400
+
+ sender['balance'] -= amount
+ users[target]['balance'] += amount
+ save_users(users)
+
+ add_history(request.username, 'api_transfer', amount, target)
+
+ return jsonify({
+ 'success': True,
+ 'message': f'Transfer of ${amount} to {target} successful',
+ 'new_balance': sender['balance']
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/cards/create', methods=['POST'])
+@api_key_required
+def api_create_card():
+ """API: Создать карту - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ try:
+ if not request.is_json:
+ return jsonify({'success': False, 'error': 'JSON data required'}), 400
+
+ data = request.get_json()
+ card_type = data.get('card_type', 'standard')
+
+ if card_type not in ['standard', 'gold', 'platinum']:
+ return jsonify({'success': False, 'error': 'Invalid card type'}), 400
+
+ users = load_users()
+ user = users.get(request.username)
+
+ if not user:
+ return jsonify({'success': False, 'error': 'User not found'}), 404
+
+ costs = {'standard': 0, 'gold': 50, 'platinum': 100}
+ cost = costs.get(card_type, 0)
+
+ if user['balance'] < cost:
+ return jsonify({'success': False, 'error': 'Insufficient funds'}), 400
+
+ if cost > 0:
+ user['balance'] -= cost
+ save_users(users)
+
+ card = create_user_card(request.username, user, card_type)
+
+ if not card:
+ if cost > 0:
+ user['balance'] += cost
+ save_users(users)
+ return jsonify({'success': False, 'error': 'Card creation failed. Maximum cards limit reached?'}), 400
+
+ safe_card = card.copy()
+ if 'number' in safe_card and len(safe_card['number']) > 4:
+ safe_card['number_masked'] = '**** **** **** ' + safe_card['number'][-4:]
+ safe_card['cvv'] = '***'
+
+ return jsonify({
+ 'success': True,
+ 'message': f'{card_type.capitalize()} card created successfully',
+ 'card': safe_card,
+ 'cost': cost,
+ 'new_balance': user['balance']
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/cards/delete', methods=['POST'])
+@api_key_required
+def api_delete_card():
+ """API: Удалить карту - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ try:
+ if not request.is_json:
+ return jsonify({'success': False, 'error': 'JSON data required'}), 400
+
+ data = request.get_json()
+ card_id = data.get('card_id')
+
+ if not card_id:
+ return jsonify({'success': False, 'error': 'Card ID required'}), 400
+
+ cards = load_cards()
+ user_cards = cards.get(request.username, [])
+
+ card_to_delete = None
+ for card in user_cards:
+ if card['id'] == card_id:
+ card_to_delete = card
+ break
+
+ if not card_to_delete:
+ return jsonify({'success': False, 'error': 'Card not found'}), 404
+
+ cards[request.username] = [c for c in user_cards if c['id'] != card_id]
+ save_cards(cards)
+
+ add_history(request.username, 'api_delete_card', 0, f"Card {card_to_delete['number'][-4:]}")
+
+ return jsonify({
+ 'success': True,
+ 'message': f"Card ending with {card_to_delete['number'][-4:]} deleted successfully"
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+# ---------- API ДЛЯ ПЛАТЕЖЕЙ ----------
+
+@app.route('/api/v1/payment/create', methods=['POST'])
+@api_key_required
+def api_payment_create():
+ """API: Создать платеж"""
+ try:
+ if not request.is_json:
+ return jsonify({'success': False, 'error': 'JSON data required'}), 400
+
+ data = request.get_json()
+ receiver = data.get('receiver')
+ amount = data.get('amount')
+ description = data.get('description', '')
+ external_id = data.get('external_id', '')
+
+ if not receiver:
+ return jsonify({'success': False, 'error': 'Receiver required'}), 400
+
+ if not amount:
+ return jsonify({'success': False, 'error': 'Amount required'}), 400
+
+ try:
+ amount_int = int(amount)
+ if amount_int <= 0:
+ return jsonify({'success': False, 'error': 'Amount must be positive'}), 400
+ except:
+ return jsonify({'success': False, 'error': 'Invalid amount'}), 400
+
+ # Проверяем существование получателя
+ users = load_users()
+ if receiver not in users:
+ return jsonify({'success': False, 'error': 'Receiver not found'}), 404
+
+ # Создаем платеж
+ payment_id, payment_data = create_payment(
+ payer=request.username,
+ receiver=receiver,
+ amount=amount_int,
+ description=description,
+ external_id=external_id
+ )
+
+ return jsonify({
+ 'success': True,
+ 'message': 'Payment created successfully',
+ 'payment_id': payment_id,
+ 'payment': payment_data,
+ 'payment_url': f'/pay/{payment_id}'
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/payment/process', methods=['POST'])
+@api_key_required
+def api_payment_process():
+ """API: Выполнить платеж"""
+ try:
+ if not request.is_json:
+ return jsonify({'success': False, 'error': 'JSON data required'}), 400
+
+ data = request.get_json()
+ payment_id = data.get('payment_id')
+
+ if not payment_id:
+ return jsonify({'success': False, 'error': 'Payment ID required'}), 400
+
+ success, message = process_payment(payment_id, request.username)
+
+ return jsonify({
+ 'success': success,
+ 'message': message
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/payment/status/', methods=['GET'])
+@api_key_required
+def api_payment_status(payment_id):
+ """API: Получить статус платежа"""
+ try:
+ payments = load_payments()
+
+ payment = None
+ for p in payments:
+ if p["id"] == payment_id:
+ payment = p
+ break
+
+ if not payment:
+ return jsonify({'success': False, 'error': 'Payment not found'}), 404
+
+ # Проверяем, имеет ли пользователь доступ к этому платежу
+ if payment["payer"] != request.username and payment["receiver"] != request.username:
+ users = load_users()
+ if users.get(request.username, {}).get('role') != 'admin':
+ return jsonify({'success': False, 'error': 'Access denied'}), 403
+
+ return jsonify({
+ 'success': True,
+ 'payment': payment
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/payment/list', methods=['GET'])
+@api_key_required
+def api_payment_list():
+ """API: Получить список платежей пользователя"""
+ try:
+ payments = load_payments()
+
+ # Фильтруем платежи по пользователю
+ user_payments = []
+ for payment in payments:
+ if payment["payer"] == request.username or payment["receiver"] == request.username:
+ user_payments.append(payment)
+
+ # Сортируем по дате (новые сверху)
+ user_payments.sort(key=lambda x: x['created'], reverse=True)
+
+ # Ограничиваем количество
+ limit = min(20, len(user_payments))
+ user_payments = user_payments[:limit]
+
+ return jsonify({
+ 'success': True,
+ 'count': len(user_payments),
+ 'payments': user_payments
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/shop/items', methods=['GET'])
+@api_key_required
+def api_shop_items():
+ """API: Получить список товаров - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ try:
+ shop_data = load_shop()
+ available_items = [item for item in shop_data["items"] if item["status"] == "available"]
+
+ for item in available_items:
+ item['created_formatted'] = datetime.fromtimestamp(item['created']).strftime('%Y-%m-%d %H:%M:%S')
+
+ return jsonify({
+ 'success': True,
+ 'count': len(available_items),
+ 'items': available_items[::-1]
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/shop/ads', methods=['GET'])
+@api_key_required
+def api_shop_ads():
+ """API: Получить список объявлений - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ try:
+ shop_data = load_shop()
+ active_ads = [ad for ad in shop_data["ads"] if ad["status"] == "active"]
+
+ for ad in active_ads:
+ ad['created_formatted'] = datetime.fromtimestamp(ad['created']).strftime('%Y-%m-%d %H:%M:%S')
+
+ return jsonify({
+ 'success': True,
+ 'count': len(active_ads),
+ 'ads': active_ads[::-1]
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/system/status', methods=['GET'])
+@api_key_required
+def api_system_status():
+ """API: Получить статус системы - ИСПРАВЛЕННАЯ ВЕРСИЯ"""
+ try:
+ users = load_users()
+ valid_users = [u for u in users.values() if isinstance(u, dict)]
+ total_users = len(valid_users)
+ total_balance = sum(u.get('balance', 0) for u in valid_users)
+ total_deposit = sum(u.get('deposit', 0) for u in valid_users)
+ total_credit = sum(u.get('credit', 0) for u in valid_users)
+
+ return jsonify({
+ 'success': True,
+ 'system_status': 'operational',
+ 'statistics': {
+ 'total_users': total_users,
+ 'total_balance': total_balance,
+ 'total_deposit': total_deposit,
+ 'total_credit': total_credit,
+ 'net_worth': total_balance + total_deposit - total_credit
+ },
+ 'timestamp': int(time.time())
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+# ДОБАВЛЕННЫЕ API ЭНДПОИНТЫ
+
+@app.route('/api/v1/users', methods=['GET'])
+@api_key_required
+def api_users():
+ """API: Получить список пользователей (только для админов)"""
+ try:
+ users = load_users()
+
+ if not isinstance(request.user_data, dict) or request.user_data.get('role') != 'admin':
+ return jsonify({'success': False, 'error': 'Admin access required'}), 403
+
+ safe_users = {}
+ for username, user in users.items():
+ if isinstance(user, dict):
+ safe_user = user.copy()
+ safe_user.pop('password', None)
+ safe_users[username] = safe_user
+
+ return jsonify({
+ 'success': True,
+ 'count': len(safe_users),
+ 'users': safe_users
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/health', methods=['GET'])
+def api_health():
+ """API: Проверка здоровья системы (не требует API ключа)"""
+ try:
+ files = [USERS_FILE, HISTORY_FILE, CARDS_FILE, SHOP_FILE, PAYMENTS_FILE]
+ file_status = {}
+
+ for file in files:
+ file_status[file] = os.path.exists(file)
+
+ return jsonify({
+ 'status': 'healthy',
+ 'timestamp': int(time.time()),
+ 'files': file_status,
+ 'message': 'System is operational'
+ })
+ except Exception as e:
+ return jsonify({'status': 'unhealthy', 'error': str(e)}), 500
+
+@app.route('/api/v1/shop/create_item', methods=['POST'])
+@api_key_required
+def api_create_shop_item():
+ """API: Создать товар в магазине"""
+ try:
+ if not request.is_json:
+ return jsonify({'success': False, 'error': 'JSON data required'}), 400
+
+ data = request.get_json()
+ title = data.get('title')
+ description = data.get('description')
+ price = data.get('price')
+ category = data.get('category', 'other')
+
+ if not title or not description or not price:
+ return jsonify({'success': False, 'error': 'Title, description and price required'}), 400
+
+ try:
+ price_int = int(price)
+ if price_int <= 0:
+ return jsonify({'success': False, 'error': 'Price must be positive'}), 400
+ except:
+ return jsonify({'success': False, 'error': 'Invalid price'}), 400
+
+ item_id = create_shop_item(request.username, title, description, price_int, category)
+
+ return jsonify({
+ 'success': True,
+ 'message': f'Item "{title}" created successfully',
+ 'item_id': item_id
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/v1/shop/buy_item', methods=['POST'])
+@api_key_required
+def api_buy_item():
+ """API: Купить товар"""
+ try:
+ if not request.is_json:
+ return jsonify({'success': False, 'error': 'JSON data required'}), 400
+
+ data = request.get_json()
+ item_id = data.get('item_id')
+
+ if not item_id:
+ return jsonify({'success': False, 'error': 'Item ID required'}), 400
+
+ success, message = buy_item(item_id, request.username)
+
+ return jsonify({
+ 'success': success,
+ 'message': message
+ })
+ except Exception as e:
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+# ---------- МАГАЗИН МАРШРУТЫ ----------
+@app.route('/create_shop_item', methods=['POST'])
+@login_required
+def create_shop_item_route():
+ """Создание товара в магазине"""
+ title = request.form.get('title')
+ description = request.form.get('description')
+ price = request.form.get('price')
+ category = request.form.get('category', 'other')
+
+ if not title or not description or not price:
+ return redirect('/dashboard')
+
+ try:
+ price_int = int(price)
+ if price_int <= 0:
+ return redirect('/dashboard')
+ except:
+ return redirect('/dashboard')
+
+ item_id = create_shop_item(session['user'], title, description, price_int, category)
+
+ return redirect('/dashboard')
+
+@app.route('/create_advertisement', methods=['POST'])
+@login_required
+def create_advertisement_route():
+ """Создание объявления"""
+ title = request.form.get('title')
+ content = request.form.get('content')
+ contact_info = request.form.get('contact_info')
+
+ if not title or not content or not contact_info:
+ return redirect('/dashboard')
+
+ ad_id = create_advertisement(session['user'], title, content, contact_info)
+
+ return redirect('/dashboard')
+
+@app.route('/buy_item/', methods=['POST'])
+@login_required
+def buy_item_route(item_id):
+ """Покупка товара"""
+ success, message = buy_item(item_id, session['user'])
+
+ return redirect('/dashboard')
+
+# ---------- ПЛАТЕЖНЫЕ МАРШРУТЫ ----------
+@app.route('/payment_page')
+@login_required
+def payment_page():
+ """Страница управления платежами"""
+ return render_payment_page()
+
+@app.route('/create_payment_link', methods=['POST'])
+@login_required
+def create_payment_link():
+ """Создание ссылки для оплаты"""
+ receiver = request.form.get('receiver')
+ amount = request.form.get('amount')
+ description = request.form.get('description', '')
+
+ if not receiver or not amount:
+ return redirect('/payment_page')
+
+ try:
+ amount_int = int(amount)
+ if amount_int <= 0:
+ return redirect('/payment_page')
+ except:
+ return redirect('/payment_page')
+
+ payment_id, payment_data = create_payment(
+ payer=session['user'],
+ receiver=receiver,
+ amount=amount_int,
+ description=description
+ )
+
+ return redirect(f'/payment_page')
+
+@app.route('/pay/', methods=['GET'])
+def pay_page(payment_id):
+ """Страница оплаты для клиента"""
+ return render_pay_page(payment_id)
+
+@app.route('/process_payment/', methods=['POST'])
+def process_payment_route(payment_id):
+ """Обработка платежа (для неавторизованных пользователей)"""
+ username = request.form.get('username')
+ password = request.form.get('password')
+
+ if not username or not password:
+ return "Неверные данные", 400
+
+ # Проверяем логин и пароль
+ users = load_users()
+ user_data = users.get(username)
+
+ if not user_data or user_data['password'] != password:
+ return "Неверный логин или пароль", 401
+
+ # Проверяем, что пользователь является плательщиком
+ payments = load_payments()
+ payment = None
+ for p in payments:
+ if p["id"] == payment_id:
+ payment = p
+ break
+
+ if not payment:
+ return "Платеж не найден", 404
+
+ if payment["payer"] != username:
+ return "Вы не являетесь плательщиком по этому платежу", 403
+
+ # Выполняем платеж
+ success, message = process_payment(payment_id, username)
+
+ if success:
+ return redirect(f'/pay/{payment_id}')
+ else:
+ return f"Ошибка: {message}", 400
+
+# ---------- ОСНОВНЫЕ МАРШРУТЫ ----------
+@app.route('/', methods=['GET', 'POST'])
+def login():
+ if request.method == 'POST':
+ users = load_users()
+ username = request.form.get('username')
+ password = request.form.get('password')
+
+ user_data = users.get(username)
+ if user_data and isinstance(user_data, dict) and user_data['password'] == password:
+ session['user'] = username
+ return redirect('/dashboard')
+ else:
+ return render_login().replace('',
+ 'Неверный логин или пароль
')
+
+ return render_login()
+
+@app.route('/register', methods=['GET', 'POST'])
+def register():
+ if request.method == 'POST':
+ users = load_users()
+ username = request.form.get('username')
+
+ if username in users:
+ return render_register().replace('',
+ 'Пользователь уже существует
')
+
+ user_data = {
+ 'password': request.form.get('password'),
+ 'balance': 1000,
+ 'deposit': 0,
+ 'credit': 0,
+ 'role': 'client',
+ 'full_name': f"{request.form.get('first_name')} {request.form.get('last_name')}",
+ 'email': request.form.get('email'),
+ 'api_key': None
+ }
+
+ users[username] = user_data
+ save_users(users)
+
+ session['user'] = username
+ return redirect('/dashboard')
+
+ return render_register()
+
+@app.route('/dashboard')
+@login_required
+def dashboard():
+ users = load_users()
+ history = load_history()
+
+ if session['user'] not in users:
+ session.clear()
+ return redirect('/')
+
+ user_data = users[session['user']]
+ user_history = [h for h in history if h['user'] == session['user']]
+ user_cards = get_user_cards(session['user'])
+ shop_data = load_shop()
+
+ return render_dashboard(user_data, user_history[-20:], user_cards, shop_data)
+
+@app.route('/generate_api_key', methods=['POST'])
+@login_required
+def generate_api_key_route():
+ users = load_users()
+ user = users.get(session['user'])
+ if user and isinstance(user, dict):
+ user['api_key'] = generate_api_key()
+ save_users(users)
+ add_history(session['user'], 'generate_api_key', 0)
+ return redirect('/dashboard')
+
+@app.route('/deposit', methods=['POST'])
+@login_required
+def deposit():
+ users = load_users()
+ user = users.get(session['user'])
+
+ try:
+ amount = int(request.form.get('amount', 0))
+ if amount > 0 and user and user['balance'] >= amount:
+ user['balance'] -= amount
+ user['deposit'] += amount
+ save_users(users)
+ add_history(session['user'], 'deposit', amount)
+ except:
+ pass
+
+ return redirect('/dashboard')
+
+@app.route('/credit', methods=['POST'])
+@login_required
+def credit():
+ users = load_users()
+ user = users.get(session['user'])
+
+ try:
+ amount = int(request.form.get('amount', 0))
+ if amount > 0 and user:
+ user['balance'] += amount
+ user['credit'] += amount
+ save_users(users)
+ add_history(session['user'], 'credit', amount)
+ except:
+ pass
+
+ return redirect('/dashboard')
+
+@app.route('/transfer', methods=['POST'])
+@login_required
+def transfer():
+ users = load_users()
+ sender = session['user']
+
+ try:
+ target = request.form.get('target')
+ amount = int(request.form.get('amount', 0))
+
+ sender_data = users.get(sender)
+ target_data = users.get(target)
+
+ if (amount > 0 and target_data and target != sender and
+ sender_data and sender_data['balance'] >= amount):
+ sender_data['balance'] -= amount
+ target_data['balance'] += amount
+ save_users(users)
+ add_history(sender, 'transfer', amount, target)
+ except Exception as e:
+ print(f"Transfer error: {e}")
+ pass
+
+ return redirect('/dashboard')
+
+@app.route('/create_card', methods=['POST'])
+@login_required
+def create_card():
+ users = load_users()
+ user = users.get(session['user'])
+ card_type = request.form.get('card_type', 'standard')
+
+ costs = {
+ 'standard': 0,
+ 'gold': 50,
+ 'platinum': 100
+ }
+
+ cost = costs.get(card_type, 0)
+
+ if user and user['balance'] >= cost:
+ user['balance'] -= cost
+ save_users(users)
+
+ card = create_user_card(session['user'], user, card_type)
+ if card:
+ add_history(session['user'], 'create_card', cost, f"{card_type}_{card['number'][-4:]}")
+
+ return redirect('/dashboard')
+
+@app.route('/delete_card/', methods=['POST'])
+@login_required
+def delete_card(card_id):
+ cards = load_cards()
+ user_cards = cards.get(session['user'], [])
+ cards[session['user']] = [c for c in user_cards if c['id'] != card_id]
+ save_cards(cards)
+ return redirect('/dashboard')
+
+# ---------- АДМИН МАРШРУТЫ (ИСПРАВЛЕННЫЕ) ----------
+@app.route('/admin')
+@admin_required
+def admin():
+ users = load_users()
+ return render_admin_panel(users)
+
+@app.route('/admin/change_role', methods=['POST'])
+@admin_required
+def admin_change_role():
+ users = load_users()
+ username = request.form.get('username')
+ new_role = request.form.get('role')
+
+ if username in users and username != session['user'] and isinstance(users[username], dict):
+ users[username]['role'] = new_role
+ save_users(users)
+ add_history(session['user'], 'change_role', 0, f"{username}->{new_role}")
+
+ return redirect('/admin')
+
+@app.route('/admin/add_money', methods=['POST'])
+@admin_required
+def admin_add_money():
+ users = load_users()
+ username = request.form.get('username')
+ amount = int(request.form.get('amount', 0))
+
+ if username in users and amount > 0 and isinstance(users[username], dict):
+ users[username]['balance'] += amount
+ save_users(users)
+ add_history('admin', 'add_money', amount, username)
+
+ return redirect('/admin')
+
+@app.route('/logout')
+def logout():
+ session.clear()
+ return redirect('/')
+
+# ---------- API ДОКУМЕНТАЦИЯ ----------
+@app.route('/api/docs')
+def api_docs():
+ """Страница с документацией API"""
+ return '''
+
+
+
+
+
+ API Документация
+
+
+
+
+
+
+
+
+
+
+
Документация API
+
Используйте API для интеграции банковских услуг в ваши приложения.
+
+
Базовый URL
+
+ http://localhost:5000/api/v1/
+
+
+
Аутентификация
+
Для использования API необходим API ключ. Получите его в личном кабинете.
+
Передавайте ключ одним из способов:
+
+
Заголовок HTTP:
+
+ X-API-Key: ваш_api_ключ
+
+
+
Параметр запроса:
+
+ ?api_key=ваш_api_ключ
+
+
+
JSON тело запроса:
+
+ {
+ "api_key": "ваш_api_ключ",
+ ...
+ }
+
+
+
Доступные эндпоинты
+
+
+
GET /api/v1/health
+
Проверка здоровья системы (не требует API ключа)
+
+curl http://localhost:5000/api/v1/health
+
+
+
+
+
GET /api/v1/balance
+
Получить информацию о балансе пользователя
+
+curl -H "X-API-Key: ваш_ключ" http://localhost:5000/api/v1/balance
+
+
+
+
+
GET /api/v1/history?limit=50
+
Получить историю операций (опциональный параметр limit)
+
+
+
+
POST /api/v1/deposit
+
Открыть вклад. Требуется JSON: {"amount": 100}
+
+curl -X POST -H "X-API-Key: ваш_ключ" -H "Content-Type: application/json"
+ -d "{\\"amount\\": 100}" http://localhost:5000/api/v1/deposit
+
+
+
+
+
POST /api/v1/credit
+
Взять кредит. Требуется JSON: {"amount": 100}
+
+
+
+
POST /api/v1/transfer
+
Сделать перевод. Требуется JSON: {"target": "username", "amount": 100}
+
+
+
+
GET /api/v1/cards
+
Получить список карт пользователя
+
+
+
+
POST /api/v1/cards/create
+
Создать карту. Требуется JSON: {"card_type": "standard"}
+
+
+
+
GET /api/v1/shop/items
+
Получить список товаров в магазине
+
+
+
+
POST /api/v1/payment/create
+
Создать платеж. Требуется JSON: {"receiver": "...", "amount": 100, "description": "..."}
+
+
+
+
POST /api/v1/payment/process
+
Выполнить платеж. Требуется JSON: {"payment_id": "..."}
+
+
+
+
GET /api/v1/payment/status/{payment_id}
+
Получить статус платежа
+
+
+
+
GET /api/v1/system/status
+
Получить статус системы
+
+
+
+
+
+
+
+
+'''
+# ================= ДЕКОРАТОР УЧИТЕЛЯ =================
+
+from functools import wraps
+
+def teacher_required(f):
+ @wraps(f)
+ def decorated(*args, **kwargs):
+ if 'user' not in session:
+ return redirect("/")
+
+ users = load_users()
+ user = users.get(session['user'])
+
+ if not user:
+ return redirect("/")
+
+ if user.get("role") not in ["teacher", "admin"]:
+ return redirect("/dashboard")
+
+ return f(*args, **kwargs)
+ return decorated
+
+# ============================================================
+# ========= ГРАФИК ДИНАМИКИ СРЕДНЕГО БАЛЛА ==================
+# ============================================================
+
+@app.route("/student_avg_graph/")
+@login_required
+@teacher_required
+def student_avg_graph(student):
+ journal = load_journal()
+
+ if student not in journal:
+ return "Нет данных"
+
+ subjects = journal[student]
+
+ # собираем оценки по порядку добавления
+ all_grades = []
+ for subject, grades in subjects.items():
+ all_grades += grades
+
+ averages = []
+ current = []
+
+ for g in all_grades:
+ current.append(g)
+ averages.append(sum(current) / len(current))
+
+ plt.figure()
+ plt.plot(range(1, len(averages)+1), averages)
+
+ plt.xlabel("Количество оценок")
+ plt.ylabel("Средний балл")
+ plt.title(f"Динамика среднего балла {student}")
+
+ img = BytesIO()
+ plt.savefig(img, format="png")
+ plt.close()
+ img.seek(0)
+
+ graph_url = base64.b64encode(img.getvalue()).decode()
+
+ return f"""
+ 📈 Динамика среднего балла: {student}
+ Назад
+
+
+ """
+
+
+# ============================================================
+# ========= ФИНАНСОВЫЙ ГРАФИК УЧЕНИКА ========================
+# ============================================================
+
+@app.route("/student_finance_graph/")
+@login_required
+@teacher_required
+def student_finance_graph(student):
+ journal = load_journal()
+ users = load_users()
+
+ if student not in journal:
+ return "Нет данных"
+
+ subjects = journal[student]
+
+ # считаем баланс на основе оценок
+ balance_history = []
+ balance = 0
+
+ for subject, grades in subjects.items():
+ for g in grades:
+ balance += calculate_money_for_grade(g)
+ balance_history.append(balance)
+
+ if not balance_history:
+ return "Нет оценок"
+
+ plt.figure()
+ plt.plot(range(1, len(balance_history)+1), balance_history)
+
+ plt.xlabel("Количество оценок")
+ plt.ylabel("Баланс ($)")
+ plt.title(f"Финансовая динамика {student}")
+
+ img = BytesIO()
+ plt.savefig(img, format="png")
+ plt.close()
+ img.seek(0)
+
+ graph_url = base64.b64encode(img.getvalue()).decode()
+
+ return f"""
+ 💰 Финансовый график: {student}
+ Назад
+
+
+ """
+
+
+# ============================================================
+# ОБНОВЛЕНИЕ ТАБЛИЦЫ В ЖУРНАЛЕ (добавление ссылок)
+# ============================================================
+
+@app.route("/journal")
+@login_required
+@teacher_required
+def journal_page():
+ journal = load_journal()
+
+ table = ""
+
+ for student, subjects in journal.items():
+ total = []
+ for grades in subjects.values():
+ total += grades
+
+ avg = round(sum(total)/len(total),2) if total else 0
+
+ table += f"""
+
+ {student}
+ {avg}
+
+ 📊 Успеваемость
+ 📈 Средний
+ 💰 Финансы
+
+
+ """
+
+ return f"""
+ Электронный журнал PRO MAX
+ Назад
+
+
+ Создать класс
+
+
+ Создать
+
+
+ Добавить ученика в класс
+
+
+
+ Добавить
+
+
+ Поставить оценку
+
+
+
+
+ Поставить
+
+
+
+ Ученики
+
+
+ Ученик
+ Средний балл
+ Графики
+
+ {table}
+
+
+
+ 🏆 Смотреть рейтинг
+ """
+
+
+if __name__ == '__main__':
+ import threading
+
+ def daily_update_thread():
+ while True:
+ time.sleep(86400)
+ daily_update()
+
+ thread = threading.Thread(target=daily_update_thread, daemon=True)
+ thread.start()
+ app.run(host='0.0.0.0', port=5000, debug=True)
\ No newline at end of file