""" 支付代理 API 路由 代理前端请求到 New API 支付后端 """ import logging from typing import Optional, List from fastapi import APIRouter, HTTPException, Header, Cookie from pydantic import BaseModel from utils.new_api_proxy import get_new_api_proxy from agent.db_pool_manager import get_db_pool_manager from utils.fastapi_utils import extract_api_key_from_auth logger = logging.getLogger('app') router = APIRouter() # ============== Pydantic Models ============== class TopupRequest(BaseModel): """充值请求""" amount: int # 充值金额 payment_method: str = "alipay" # 支付方式: alipay, wxpay class StripeTopupRequest(BaseModel): """Stripe 充值请求""" amount: int class CreemTopupRequest(BaseModel): """Creem 充值请求""" product_id: str class SubscribeRequest(BaseModel): """订阅请求""" plan_id: int payment_method: str = "alipay" # 支付方式: alipay, wxpay class PaymentInfoResponse(BaseModel): """支付信息响应""" success: bool message: str = "" data: Optional[dict] = None class OrderListResponse(BaseModel): """订单列表响应""" success: bool message: str = "" data: Optional[dict] = None # ============== 辅助函数 ============== async def verify_user_and_get_session(authorization: Optional[str]) -> tuple[str, Optional[dict], Optional[int]]: """ 验证用户并获取 New API session cookies 和 user_id Args: authorization: Authorization header Returns: tuple[str, Optional[dict], Optional[int]]: (user_id, cookies_dict, new_api_user_id) Raises: HTTPException: 认证失败 """ token = extract_api_key_from_auth(authorization) if not token: raise HTTPException(status_code=401, detail="Authorization required") pool = get_db_pool_manager().pool async with pool.connection() as conn: async with conn.cursor() as cursor: # 验证 token 并获取用户信息 await cursor.execute(""" SELECT u.id, u.new_api_session, u.new_api_user_id FROM agent_user_tokens t JOIN agent_user u ON t.user_id = u.id WHERE t.token = %s AND t.expires_at > NOW() AND u.is_active = TRUE """, (token,)) row = await cursor.fetchone() if not row: raise HTTPException(status_code=401, detail="Invalid or expired token") user_id, new_api_session, new_api_user_id = row # 如果有 session,构建 cookies cookies = None if new_api_session: cookies = {"session": new_api_session} return str(user_id), cookies, new_api_user_id # ============== 套餐/产品相关 API ============== @router.get("/api/v1/payment/packages") async def get_payment_packages(authorization: Optional[str] = Header(None)): """ 获取可购买的套餐列表 """ user_id, cookies, new_api_user_id = await verify_user_and_get_session(authorization) # 如果没有 New API session 或 user_id,返回需要绑定的提示 if not cookies or not new_api_user_id: return { "success": False, "need_bind": True, "message": "请先绑定支付账户" } proxy = get_new_api_proxy() result = await proxy.get_topup_info(cookies, new_api_user_id) return result @router.get("/api/v1/payment/subscription-plans") async def get_subscription_plans(authorization: Optional[str] = Header(None)): """ 获取订阅计划列表 """ user_id, cookies, new_api_user_id = await verify_user_and_get_session(authorization) if not cookies or not new_api_user_id: return { "success": False, "need_bind": True, "message": "请先绑定支付账户" } proxy = get_new_api_proxy() result = await proxy.get_subscription_plans(cookies, new_api_user_id) return result # ============== 支付相关 API ============== @router.post("/api/v1/payment/topup") async def create_topup_order( request: TopupRequest, authorization: Optional[str] = Header(None) ): """ 创建充值订单(易支付) """ user_id, cookies, new_api_user_id = await verify_user_and_get_session(authorization) if not cookies or not new_api_user_id: return { "success": False, "need_bind": True, "message": "请先绑定支付账户" } proxy = get_new_api_proxy() result = await proxy.create_topup_order( cookies=cookies, amount=request.amount, payment_method=request.payment_method, new_api_user_id=new_api_user_id ) return result @router.post("/api/v1/payment/stripe") async def create_stripe_order( request: StripeTopupRequest, authorization: Optional[str] = Header(None) ): """ 创建 Stripe 支付订单 """ user_id, cookies, new_api_user_id = await verify_user_and_get_session(authorization) if not cookies or not new_api_user_id: return { "success": False, "need_bind": True, "message": "请先绑定支付账户" } proxy = get_new_api_proxy() result = await proxy.create_stripe_order( cookies=cookies, amount=request.amount, new_api_user_id=new_api_user_id ) return result @router.post("/api/v1/payment/creem") async def create_creem_order( request: CreemTopupRequest, authorization: Optional[str] = Header(None) ): """ 创建 Creem 支付订单 """ user_id, cookies, new_api_user_id = await verify_user_and_get_session(authorization) if not cookies or not new_api_user_id: return { "success": False, "need_bind": True, "message": "请先绑定支付账户" } proxy = get_new_api_proxy() result = await proxy.create_creem_order( cookies=cookies, product_id=request.product_id, new_api_user_id=new_api_user_id ) return result # ============== 订单相关 API ============== @router.get("/api/v1/payment/orders") async def get_order_list( page: int = 1, page_size: int = 20, authorization: Optional[str] = Header(None) ): """ 获取订单列表 """ user_id, cookies, new_api_user_id = await verify_user_and_get_session(authorization) if not cookies or not new_api_user_id: return { "success": False, "need_bind": True, "message": "请先绑定支付账户" } proxy = get_new_api_proxy() result = await proxy.get_order_list( cookies=cookies, page=page, page_size=page_size, new_api_user_id=new_api_user_id ) return result @router.get("/api/v1/payment/orders/{order_id}") async def get_order_status( order_id: str, authorization: Optional[str] = Header(None) ): """ 查询订单状态 """ user_id, cookies, new_api_user_id = await verify_user_and_get_session(authorization) if not cookies or not new_api_user_id: return { "success": False, "need_bind": True, "message": "请先绑定支付账户" } proxy = get_new_api_proxy() result = await proxy.get_order_status( cookies=cookies, order_id=order_id, new_api_user_id=new_api_user_id ) return result # ============== 订阅相关 API ============== @router.post("/api/v1/payment/subscribe") async def subscribe_plan( request: SubscribeRequest, authorization: Optional[str] = Header(None) ): """ 订阅计划(易支付) """ user_id, cookies, new_api_user_id = await verify_user_and_get_session(authorization) if not cookies or not new_api_user_id: return { "success": False, "need_bind": True, "message": "请先绑定支付账户" } proxy = get_new_api_proxy() result = await proxy.subscribe_plan( cookies=cookies, plan_id=request.plan_id, payment_method=request.payment_method, new_api_user_id=new_api_user_id ) return result @router.get("/api/v1/payment/subscription/status") async def get_subscription_status(authorization: Optional[str] = Header(None)): """ 获取订阅状态 """ user_id, cookies, new_api_user_id = await verify_user_and_get_session(authorization) if not cookies or not new_api_user_id: return { "success": False, "need_bind": True, "message": "请先绑定支付账户" } proxy = get_new_api_proxy() result = await proxy.get_subscription_status(cookies, new_api_user_id) return result @router.post("/api/v1/payment/subscription/cancel") async def cancel_subscription(authorization: Optional[str] = Header(None)): """ 取消订阅 注意:New API 不支持用户自行取消订阅,需要联系管理员 """ user_id, cookies, new_api_user_id = await verify_user_and_get_session(authorization) if not cookies or not new_api_user_id: return { "success": False, "need_bind": True, "message": "请先绑定支付账户" } # New API 不支持用户自行取消订阅,需要管理员操作 return { "success": False, "message": "如需取消订阅,请联系管理员处理" } # ============== 额度相关 API ============== @router.get("/api/v1/payment/quota") async def get_quota(authorization: Optional[str] = Header(None)): """ 获取用户额度信息 """ user_id, cookies, new_api_user_id = await verify_user_and_get_session(authorization) if not cookies or not new_api_user_id: return { "success": False, "need_bind": True, "message": "请先绑定支付账户" } proxy = get_new_api_proxy() result = await proxy.get_quota(cookies, new_api_user_id) return result