diff --git a/app/payment/factory.py b/app/payment/factory.py index 8db89d9..06fd5b4 100644 --- a/app/payment/factory.py +++ b/app/payment/factory.py @@ -34,6 +34,17 @@ def get_payment_provider() -> PaymentProvider: return _provider_instance +def get_payment_provider_by_name(name: str) -> PaymentProvider: + """按名称获取 Provider(用于 per-request 覆盖)""" + if name == "xorpay": + return XorPayProvider( + aid=XORPAY_AID, + secret=XORPAY_SECRET, + cashier_url=XORPAY_CASHIER_URL, + ) + return ZPayProvider(pid=ZPAY_PID, key=ZPAY_KEY) + + def reset_provider(): """重置 Provider(用于测试或切换渠道)""" global _provider_instance diff --git a/app/routers/nomadvip.py b/app/routers/nomadvip.py index be1af23..ba59f81 100644 --- a/app/routers/nomadvip.py +++ b/app/routers/nomadvip.py @@ -8,7 +8,7 @@ from fastapi import APIRouter, Request, HTTPException from fastapi.responses import RedirectResponse, JSONResponse from ..config import BASE_URL -from ..payment import get_payment_provider +from ..payment import get_payment_provider, get_payment_provider_by_name from ..payment.zpay import ZPayProvider from ..services import handle_payment_success, processed_orders @@ -21,10 +21,11 @@ def _get_notify_path(provider_name: str) -> str: @router.post("/payh5") async def nomadvip_payh5(item: dict): - """nomadvip 专用:创建支付订单,xorpay 直接返回收银台参数,zpay 返回表单参数""" + """nomadvip 专用:创建支付订单,支持 per-request provider 覆盖(微信用 xorpay,PC/H5 用 zpay)""" print("nomadvip/payh5 item:", item) - provider = get_payment_provider() - total_fee = int(item.get("total_fee", 8800)) + req_provider = (item.get("provider") or "").strip().lower() + provider = get_payment_provider_by_name(req_provider) if req_provider in ("xorpay", "zpay") else get_payment_provider() + total_fee = int(item.get("total_fee", 880)) total_fee_yuan = f"{total_fee / 100:.2f}" user_id = item.get("user_id", "unknown") pay_type = (item.get("type") or "vip").lower() @@ -70,15 +71,16 @@ async def nomadvip_payh5(item: dict): @router.post("/payh5/redirect") async def nomadvip_payh5_redirect(item: dict, request: Request): """nomadvip 专用:ZPAY mapi 接口(xorpay 无需 redirect,直接 payh5 即可)""" - provider = get_payment_provider() - if provider.name != "zpay": + req_provider = (item.get("provider") or "").strip().lower() + provider = get_payment_provider_by_name("zpay") if req_provider == "zpay" else get_payment_provider() + if not isinstance(provider, ZPayProvider): raise HTTPException(status_code=400, detail="payh5/redirect 仅支持 zpay,xorpay 请直接使用 payh5") logging.info(f"nomadvip payh5/redirect 请求: item={item}") try: - total_fee = float(item.get("total_fee", 8800)) + total_fee = float(item.get("total_fee", 880)) except (TypeError, ValueError): logging.warning(f"nomadvip payh5/redirect 无效 total_fee: {item.get('total_fee')}") - total_fee = 8800.0 + total_fee = 880.0 total_fee_yuan = f"{total_fee / 100:.2f}" user_id = item.get("user_id", "unknown") pay_type = (item.get("type") or "vip").lower() @@ -144,14 +146,13 @@ async def nomadvip_zpay_order_status(out_trade_no: str): @router.get("/order_status") async def nomadvip_order_status(order_id: str): - """nomadvip 通用:查询订单支付状态(xorpay 查 PocketBase,zpay 查 ZPAY)""" - provider = get_payment_provider() - if isinstance(provider, ZPayProvider): - result = provider.query_order_status(order_id, timeout=15) - if result.get("error"): - logging.warning(f"nomadvip ZPAY 订单查询失败: {result.get('error')}") - return result - # xorpay:查 PocketBase + """nomadvip 通用:查询订单支付状态。zpay 先查 ZPAY 实时,xorpay 或失败时查 PocketBase""" + zpay = get_payment_provider_by_name("zpay") + if isinstance(zpay, ZPayProvider): + result = zpay.query_order_status(order_id, timeout=15) + if not result.get("error") and result.get("paid"): + return result + # zpay 查不到或 xorpay:查 PocketBase(notify 后写入) try: from ..services.pb import get_pb_client @@ -165,7 +166,7 @@ async def nomadvip_order_status(order_id: str): @router.post("/xorpay_notify") async def nomadvip_xorpay_notify(request: Request): - """nomadvip 专用:XorPay 异步通知""" + """nomadvip 专用:XorPay 异步通知(路径即渠道,不依赖全局 PAYMENT_PROVIDER)""" form_data = await request.form() data = {k: v for k, v in form_data.items()} logging.info(f"nomadvip XorPay 异步通知: {data}") @@ -173,9 +174,7 @@ async def nomadvip_xorpay_notify(request: Request): if order_id in processed_orders: logging.info(f"nomadvip 订单已处理,跳过: {order_id}") return "ok" - provider = get_payment_provider() - if provider.name != "xorpay": - raise HTTPException(status_code=400, detail="xorpay_notify only for xorpay") + provider = get_payment_provider_by_name("xorpay") if not provider.verify_notify(data): logging.error(f"nomadvip XorPay 验签失败: {data}") raise HTTPException(status_code=400, detail="sign error") @@ -195,10 +194,8 @@ async def nomadvip_xorpay_notify(request: Request): @router.get("/zpay_notify") @router.post("/zpay_notify") async def nomadvip_zpay_notify(request: Request): - """nomadvip 专用:ZPAY 异步通知""" - provider = get_payment_provider() - if provider.name != "zpay": - raise HTTPException(status_code=400, detail="zpay_notify only for zpay") + """nomadvip 专用:ZPAY 异步通知(路径即渠道,不依赖全局 PAYMENT_PROVIDER)""" + provider = get_payment_provider_by_name("zpay") if request.method == "GET": data = dict(request.query_params) else: