from fastapi import APIRouter, Depends, HTTPException, Query, Request from fastapi.responses import PlainTextResponse from sqlalchemy.ext.asyncio import AsyncSession from auth.dependencies import get_current_user, require_admin from auth.system_settings import load_settings from models.database import get_db from models.models import User from . import service from .schemas import ( AdminUpdateOrderStatusRequest, CreatePaymentOrderRequest, MessageResponse, PaymentConfigResponse, PaymentOrderListItem, PaymentOrderListResponse, PaymentOrderResponse, ) router = APIRouter(prefix="/api/payments", tags=["payments"]) @router.get("/config", response_model=PaymentConfigResponse) async def get_payment_config( db: AsyncSession = Depends(get_db), _: User = Depends(get_current_user), ): settings = await load_settings(db) return PaymentConfigResponse(**service.payment_config_payload(settings)) @router.post("/orders", response_model=PaymentOrderResponse) async def create_payment_order( body: CreatePaymentOrderRequest, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ): order, demo_mode = await service.create_order(db, user, body.slots, body.channel) return PaymentOrderResponse(**service.order_to_dict(order, demo_mode=demo_mode)) @router.get("/orders", response_model=PaymentOrderListResponse) async def list_payment_orders( status: str | None = Query(default=None, description="pending/paid/expired/cancelled"), channel: str | None = Query(default=None, description="wechat/alipay"), page: int = Query(default=1, ge=1), page_size: int = Query(default=20, ge=1, le=100), db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ): if status and status not in service.ORDER_STATUSES: raise HTTPException(status_code=400, detail="无效的订单状态") data = await service.list_orders( db, user, status=status, channel=channel, page=page, page_size=page_size, ) return PaymentOrderListResponse(**data) @router.get("/orders/{order_no}", response_model=PaymentOrderResponse) async def get_payment_order( order_no: str, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ): settings = await load_settings(db) order = await service.get_user_order(db, user, order_no) demo_mode = settings.payment_demo_mode and not settings.payment_channel_available(order.channel) return PaymentOrderResponse(**service.order_to_dict(order, demo_mode=demo_mode)) @router.post("/orders/{order_no}/simulate", response_model=PaymentOrderResponse) async def simulate_payment_order( order_no: str, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ): settings = await load_settings(db) order = await service.simulate_pay(db, user, order_no) return PaymentOrderResponse(**service.order_to_dict(order, demo_mode=settings.payment_demo_mode)) @router.put("/orders/{order_no}/status", response_model=PaymentOrderListItem) async def admin_update_payment_order_status( order_no: str, body: AdminUpdateOrderStatusRequest, db: AsyncSession = Depends(get_db), _: User = Depends(require_admin), ): order = await service.admin_update_order_status(db, order_no, body.status) return PaymentOrderListItem(**order) @router.delete("/orders/{order_no}", response_model=MessageResponse) async def admin_delete_payment_order( order_no: str, db: AsyncSession = Depends(get_db), _: User = Depends(require_admin), ): await service.admin_delete_order(db, order_no) return MessageResponse(message="订单已删除") @router.post("/notify/wechat") async def wechat_payment_notify(request: Request, db: AsyncSession = Depends(get_db)): body = await request.body() headers = {k.lower(): v for k, v in request.headers.items()} ok = await service.handle_wechat_notify(db, body, headers) if not ok: return PlainTextResponse("FAIL", status_code=400) return PlainTextResponse('{"code": "SUCCESS", "message": "成功"}', media_type="application/json") @router.post("/notify/alipay") async def alipay_payment_notify(request: Request, db: AsyncSession = Depends(get_db)): form = await request.form() params = {k: v for k, v in form.items()} ok = await service.handle_alipay_notify(db, params) if not ok: return PlainTextResponse("fail") return PlainTextResponse("success")