213 lines
9.3 KiB
Python
213 lines
9.3 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Body, Header, Request
|
||
from sqlalchemy.orm import Session
|
||
from core.db import get_db, Sale, User, Poster
|
||
from core.pay import YooKassaPayment
|
||
from pydantic import BaseModel, Field
|
||
from typing import Optional, List
|
||
from datetime import datetime
|
||
import json
|
||
|
||
class PaymentRequest(BaseModel):
|
||
amount: float
|
||
currency: str
|
||
description: str
|
||
return_url: str
|
||
user_id: Optional[int] = Field(None) # Делаем необязательным
|
||
poster_id: Optional[int] = None
|
||
|
||
router = APIRouter()
|
||
|
||
# Конфигурация YooKassa
|
||
SHOP_ID = "1017909"
|
||
API_KEY = "test_udCBy7nXqGavVoj3RrzIRXN9UL02_UnF0FBBykxWH60"
|
||
payment_service = YooKassaPayment(SHOP_ID, API_KEY)
|
||
|
||
@router.post("/create-payment/")
|
||
async def create_payment(
|
||
payment_data: dict = Body(...), # Принимаем любой JSON
|
||
db: Session = Depends(get_db)
|
||
):
|
||
try:
|
||
# Логирование для отладки
|
||
print(f"Received payment data: {json.dumps(payment_data, ensure_ascii=False)}")
|
||
|
||
# Создание платежа через YooKassa
|
||
payment_response = payment_service.create_payment(
|
||
amount=float(payment_data.get("amount", 0)),
|
||
currency=payment_data.get("currency", "RUB"),
|
||
description=payment_data.get("description", ""),
|
||
return_url=payment_data.get("return_url", "http://localhost:3000/payments")
|
||
)
|
||
|
||
# Логирование ответа YooKassa
|
||
print(f"YooKassa response: {json.dumps(payment_response, ensure_ascii=False)}")
|
||
|
||
# Сохранение данных о платеже в БД
|
||
new_sale = Sale(
|
||
amount=float(payment_data.get("amount", 0)),
|
||
status="pending",
|
||
description=payment_data.get("description", ""),
|
||
poster_id=payment_data.get("poster_id"),
|
||
user_id=payment_data.get("user_id", 1), # Используем 1 как значение по умолчанию
|
||
user_email=payment_data.get("user_email", "example@example.com"),
|
||
payment_id=payment_response["id"],
|
||
confirmation_url=payment_response["confirmation"]["confirmation_url"]
|
||
)
|
||
db.add(new_sale)
|
||
db.commit()
|
||
db.refresh(new_sale)
|
||
|
||
return {"confirmation_url": new_sale.confirmation_url, "payment_id": payment_response["id"]}
|
||
except Exception as e:
|
||
print(f"ERROR creating payment: {str(e)}")
|
||
raise HTTPException(status_code=400, detail=str(e))
|
||
|
||
@router.get("/payment-status/{payment_id}")
|
||
async def get_payment_status(payment_id: str, db: Session = Depends(get_db)):
|
||
try:
|
||
print(f"Checking payment status for ID: {payment_id}")
|
||
|
||
# Получение информации о платеже из БД
|
||
sale = db.query(Sale).filter(Sale.payment_id == payment_id).first()
|
||
|
||
if not sale:
|
||
raise HTTPException(status_code=404, detail=f"Платеж с ID {payment_id} не найден")
|
||
|
||
# Обновление статуса из YooKassa
|
||
yookassa_status = payment_service.get_payment_status(payment_id)
|
||
if yookassa_status and "status" in yookassa_status:
|
||
# Маппинг статусов YooKassa на наши статусы
|
||
if yookassa_status["status"] == "succeeded" or yookassa_status["paid"] == True:
|
||
sale.status = "paid"
|
||
elif yookassa_status["status"] == "canceled":
|
||
sale.status = "canceled"
|
||
elif yookassa_status["status"] == "waiting_for_capture":
|
||
sale.status = "waiting_for_capture"
|
||
# Сохраняем обновленный статус
|
||
db.commit()
|
||
print(f"Updated payment status from YooKassa: {yookassa_status['status']} -> {sale.status}")
|
||
|
||
return {"payment_id": sale.payment_id, "status": sale.status}
|
||
except Exception as e:
|
||
print(f"ERROR checking payment status: {str(e)}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
@router.post("/notifications/")
|
||
async def process_notification(request: Request, db: Session = Depends(get_db)):
|
||
try:
|
||
# Получаем тело запроса
|
||
notification_data = await request.json()
|
||
print(f"Received notification: {json.dumps(notification_data, ensure_ascii=False)}")
|
||
|
||
# Проверяем тип события
|
||
event = notification_data.get("event")
|
||
payment_id = notification_data.get("object", {}).get("id")
|
||
|
||
if not payment_id:
|
||
raise HTTPException(status_code=400, detail="Missing payment ID")
|
||
|
||
# Находим платеж в БД
|
||
sale = db.query(Sale).filter(Sale.payment_id == payment_id).first()
|
||
if not sale:
|
||
raise HTTPException(status_code=404, detail=f"Payment {payment_id} not found")
|
||
|
||
# Обновляем статус платежа в зависимости от события
|
||
if event == "payment.waiting_for_capture":
|
||
sale.status = "waiting_for_capture"
|
||
elif event == "payment.succeeded":
|
||
sale.status = "paid"
|
||
elif event == "payment.canceled":
|
||
sale.status = "canceled"
|
||
else:
|
||
print(f"Unknown event: {event}")
|
||
|
||
db.commit()
|
||
return {"success": True}
|
||
except Exception as e:
|
||
print(f"ERROR processing notification: {str(e)}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
@router.post("/update-payment-status/")
|
||
async def manual_update_payment_status(payment_id: str, new_status: str, db: Session = Depends(get_db)):
|
||
try:
|
||
sale = db.query(Sale).filter(Sale.payment_id == payment_id).first()
|
||
if not sale:
|
||
raise HTTPException(status_code=404, detail="Платеж не найден")
|
||
|
||
# Обновляем статус
|
||
sale.status = new_status
|
||
db.commit()
|
||
|
||
return {"message": "Статус платежа обновлен", "payment_id": sale.payment_id, "status": sale.status}
|
||
except Exception as e:
|
||
print(f"ERROR updating payment status: {str(e)}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
@router.get("/user-tickets/{user_id}")
|
||
async def get_user_tickets(user_id: int, db: Session = Depends(get_db)):
|
||
"""Получение всех оплаченных билетов пользователя"""
|
||
try:
|
||
# Проверяем существование пользователя
|
||
user = db.query(User).filter(User.id == user_id).first()
|
||
if not user:
|
||
raise HTTPException(status_code=404, detail="Пользователь не найден")
|
||
|
||
# Получаем все успешно оплаченные билеты пользователя
|
||
sales = db.query(Sale).filter(Sale.user_id == user_id, Sale.status == "paid").all()
|
||
|
||
result = []
|
||
for sale in sales:
|
||
# Получаем информацию о мероприятии
|
||
poster = None
|
||
if sale.poster_id:
|
||
poster = db.query(Poster).filter(Poster.id == sale.poster_id).first()
|
||
|
||
# Формирование даты покупки
|
||
purchase_date = sale.date if hasattr(sale, 'date') else datetime.now().isoformat()
|
||
|
||
ticket_data = {
|
||
"id": sale.id,
|
||
"amount": sale.amount,
|
||
"description": sale.description,
|
||
"payment_id": sale.payment_id,
|
||
"purchase_date": purchase_date,
|
||
"poster": None
|
||
}
|
||
|
||
if poster:
|
||
# Используем только те поля, которые точно есть в модели Poster
|
||
ticket_data["poster"] = {
|
||
"id": poster.id,
|
||
"title": poster.title,
|
||
"date": poster.date
|
||
# Убрали поле img, которого нет в модели
|
||
}
|
||
|
||
result.append(ticket_data)
|
||
|
||
return result
|
||
except HTTPException as he:
|
||
# Пробрасываем дальше HTTPException
|
||
raise he
|
||
except Exception as e:
|
||
print(f"ERROR getting user tickets: {str(e)}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
# Для тестирования, также добавим временный эндпоинт для обновления статуса билета
|
||
@router.post("/update-ticket-status/{sale_id}")
|
||
async def update_ticket_status(sale_id: int, status: str, db: Session = Depends(get_db)):
|
||
"""Временный эндпоинт для обновления статуса билета (для тестирования)"""
|
||
try:
|
||
sale = db.query(Sale).filter(Sale.id == sale_id).first()
|
||
if not sale:
|
||
raise HTTPException(status_code=404, detail="Билет не найден")
|
||
|
||
sale.status = status
|
||
db.commit()
|
||
|
||
return {"success": True, "message": f"Статус билета с ID {sale_id} обновлен на {status}"}
|
||
except Exception as e:
|
||
print(f"ERROR updating ticket status: {str(e)}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|