Files
video-gen/video-gen-api/app/api/v1/payments.py
T

123 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import logging
from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
logger = logging.getLogger("videogen")
from app.dependencies import get_db, get_current_user
from app.models.user import User
from app.models.payment_order import PaymentOrder
from app.models.recharge_package import RechargePackage
from app.schemas.payment import RechargeRequest, PaymentOrderOut
from app.services.payment import (
create_recharge_order,
verify_wechat_callback,
verify_alipay_callback,
process_payment_success_by_order_no,
)
router = APIRouter(prefix="/payments", tags=["payments"])
@router.get("/methods")
async def get_payment_methods(db: AsyncSession = Depends(get_db)):
"""Return which payment methods are enabled (from admin config)."""
from app.services.payment import _get_payment_configs
configs = await _get_payment_configs(db)
return {
"alipay": configs.get("payment_alipay_enabled", "").lower() == "true",
"wechat": configs.get("payment_wechat_enabled", "").lower() == "true",
}
@router.post("/recharge", response_model=PaymentOrderOut)
async def recharge(
req: RechargeRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
if req.method not in ("wechat", "alipay"):
raise HTTPException(status_code=400, detail="不支持的支付方式")
# Check if the selected payment method is enabled in admin config
from app.services.payment import _get_payment_configs, _is_mock_mode
configs = await _get_payment_configs(db)
if not _is_mock_mode(configs):
enabled_key = f"payment_{req.method}_enabled"
if configs.get(enabled_key, "").lower() != "true":
raise HTTPException(status_code=400, detail="该支付方式未启用")
result = await db.execute(
select(RechargePackage).where(
RechargePackage.id == req.plan,
RechargePackage.is_active == True,
)
.limit(1)
)
pkg = result.scalar_one_or_none()
if not pkg:
raise HTTPException(status_code=400, detail="无效的套餐")
try:
order = await create_recharge_order(
db,
current_user.id,
credits=pkg.credits,
price=pkg.price,
label=pkg.name,
bonus_credits=pkg.bonus_credits,
method=req.method,
)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
return order
@router.post("/wechat/callback")
async def wechat_callback(request: Request, db: AsyncSession = Depends(get_db)):
data = await request.json()
if not await verify_wechat_callback(data, db):
raise HTTPException(status_code=400, detail="签名验证失败")
order_no = data.get("out_trade_no")
if order_no:
await process_payment_success_by_order_no(db, order_no)
return {"code": "SUCCESS", "message": "OK"}
@router.post("/alipay/callback")
async def alipay_callback(request: Request, db: AsyncSession = Depends(get_db)):
form_data = await request.form()
data = dict(form_data)
logger.info(f"Alipay callback received: {list(data.keys())}")
# Verify signature first
if not await verify_alipay_callback(data, db):
raise HTTPException(status_code=400, detail="签名验证失败")
# Check trade_status only "TRADE_SUCCESS" and "TRADE_FINISHED" mean paid
trade_status = data.get("trade_status", "")
if trade_status not in ("TRADE_SUCCESS", "TRADE_FINISHED"):
logger.info(f"Alipay callback trade_status={trade_status}, ignoring")
return "success"
order_no = data.get("out_trade_no")
trade_no = data.get("trade_no", "")
if order_no:
await process_payment_success_by_order_no(db, order_no, trade_no)
return "success"
@router.get("/orders", response_model=list[PaymentOrderOut])
async def list_orders(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(
select(PaymentOrder)
.where(PaymentOrder.user_id == current_user.id)
.order_by(PaymentOrder.created_at.desc())
)
return result.scalars().all()