diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0d8313 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# 日志文件(不产生,避免 git 本地/线上不一致) +*.log +payment_notify.log +xorpay_notify.log + +# Python +__pycache__/ +*.py[cod] +venv/ +.venv/ +.env +.env.local diff --git a/app/main.py b/app/main.py index 67a49f8..3c79f1c 100755 --- a/app/main.py +++ b/app/main.py @@ -51,14 +51,11 @@ async def global_exception_handler(request: Request, exc: Exception): logging.error(f"未处理异常: {request.url.path} - {exc}", exc_info=True) return JSONResponse(status_code=500, content={"detail": str(exc)}) -# 日志 +# 日志(仅控制台,不写文件,避免 git 本地/线上不一致) logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(message)s", - handlers=[ - logging.FileHandler("payment_notify.log"), - logging.StreamHandler(), - ], + handlers=[logging.StreamHandler()], ) # 挂载路由 diff --git a/app/payment/__pycache__/zpay.cpython-312.pyc b/app/payment/__pycache__/zpay.cpython-312.pyc index 721fbd4..d8c5257 100644 Binary files a/app/payment/__pycache__/zpay.cpython-312.pyc and b/app/payment/__pycache__/zpay.cpython-312.pyc differ diff --git a/app/payment/zpay.py b/app/payment/zpay.py index b738826..d007fc4 100644 --- a/app/payment/zpay.py +++ b/app/payment/zpay.py @@ -167,3 +167,24 @@ class ZPayProvider(PaymentProvider): def success_response(self) -> str: return "success" + + def query_order_status(self, out_trade_no: str, timeout: int = 15) -> Dict[str, Any]: + """查询 ZPAY 订单支付状态(供 nomadvip/digital/cnomadcna/nomadlms 共用)""" + url = f"https://zpayz.cn/api.php?act=order&pid={self.pid}&key={self.key}&out_trade_no={out_trade_no}" + try: + resp = requests.get(url, timeout=timeout) + result = resp.json() if resp.ok else {} + except Exception as e: + return {"paid": False, "error": str(e)} + try: + code = result.get("code", 0) + code = int(code) if isinstance(code, (int, float)) or str(code).isdigit() else 0 + except (ValueError, TypeError): + code = 0 + if code != 1: + return {"paid": False, "msg": result.get("msg", "查询失败")} + try: + status = int(result.get("status", 0)) + except (ValueError, TypeError): + status = 0 + return {"paid": status == 1, "status": status} diff --git a/app/routers/__pycache__/_digital_base.cpython-312.pyc b/app/routers/__pycache__/_digital_base.cpython-312.pyc index 9df9190..25846bc 100644 Binary files a/app/routers/__pycache__/_digital_base.cpython-312.pyc and b/app/routers/__pycache__/_digital_base.cpython-312.pyc differ diff --git a/app/routers/__pycache__/nomadvip.cpython-312.pyc b/app/routers/__pycache__/nomadvip.cpython-312.pyc index fa3d538..808d15e 100644 Binary files a/app/routers/__pycache__/nomadvip.cpython-312.pyc and b/app/routers/__pycache__/nomadvip.cpython-312.pyc differ diff --git a/app/routers/_digital_base.py b/app/routers/_digital_base.py index b9b815a..fff8d83 100644 --- a/app/routers/_digital_base.py +++ b/app/routers/_digital_base.py @@ -7,10 +7,9 @@ import random import string import time -import requests from fastapi import APIRouter, Request, HTTPException -from ..config import BASE_URL, ZPAY_PID, ZPAY_KEY +from ..config import BASE_URL from ..payment import get_payment_provider from ..payment.zpay import ZPayProvider from ..services import handle_payment_success, processed_orders @@ -142,27 +141,12 @@ def create_digital_router(prefix: str, project_name: str) -> APIRouter: async def zpay_order_status(out_trade_no: str): """查询 ZPAY 订单支付状态""" provider = get_payment_provider() - if provider.name != "zpay": + if not isinstance(provider, ZPayProvider): raise HTTPException(status_code=400, detail="仅 zpay 渠道支持") - url = f"https://zpayz.cn/api.php?act=order&pid={ZPAY_PID}&key={ZPAY_KEY}&out_trade_no={out_trade_no}" - try: - resp = requests.get(url, timeout=10) - result = resp.json() if resp.ok else {} - except Exception as e: - logging.warning(f"ZPAY 订单查询失败: {e}") - return {"paid": False, "error": str(e)} - try: - code = result.get("code", 0) - code = int(code) if isinstance(code, (int, float)) or str(code).isdigit() else 0 - except (ValueError, TypeError): - code = 0 - if code != 1: - return {"paid": False, "msg": result.get("msg", "查询失败")} - try: - status = int(result.get("status", 0)) - except (ValueError, TypeError): - status = 0 - return {"paid": status == 1, "status": status} + result = provider.query_order_status(out_trade_no, timeout=15) + if result.get("error"): + logging.warning(f"{project_name} ZPAY 订单查询失败: {result.get('error')}") + return result @router.post("/xorpay_notify") async def xorpay_notify(request: Request): diff --git a/app/routers/nomadvip.py b/app/routers/nomadvip.py index c836438..4df916f 100644 --- a/app/routers/nomadvip.py +++ b/app/routers/nomadvip.py @@ -7,8 +7,6 @@ import time from fastapi import APIRouter, Request, HTTPException from fastapi.responses import RedirectResponse, JSONResponse -import requests - from ..config import BASE_URL, ZPAY_PID, ZPAY_KEY from ..payment.zpay import ZPayProvider from ..services import handle_payment_success, processed_orders @@ -118,25 +116,10 @@ async def nomadvip_payh5_redirect(item: dict, request: Request): @router.get("/zpay_order_status") async def nomadvip_zpay_order_status(out_trade_no: str): """nomadvip 专用:查询 ZPAY 订单支付状态""" - url = f"https://zpayz.cn/api.php?act=order&pid={ZPAY_PID}&key={ZPAY_KEY}&out_trade_no={out_trade_no}" - try: - resp = requests.get(url, timeout=10) - result = resp.json() if resp.ok else {} - except Exception as e: - logging.warning(f"nomadvip ZPAY 订单查询失败: {e}") - return {"paid": False, "error": str(e)} - try: - code = result.get("code", 0) - code = int(code) if isinstance(code, (int, float)) or str(code).isdigit() else 0 - except (ValueError, TypeError): - code = 0 - if code != 1: - return {"paid": False, "msg": result.get("msg", "查询失败")} - try: - status = int(result.get("status", 0)) - except (ValueError, TypeError): - status = 0 - return {"paid": status == 1, "status": status} + result = _nomadvip_zpay.query_order_status(out_trade_no, timeout=15) + if result.get("error"): + logging.warning(f"nomadvip ZPAY 订单查询失败: {result.get('error')}") + return result @router.get("/zpay_notify") diff --git a/payment_notify.log b/payment_notify.log index 048bd20..0e8a393 100644 --- a/payment_notify.log +++ b/payment_notify.log @@ -4,3 +4,14 @@ 2026-03-08 06:31:18,405 - ZPAY ѯʧ: HTTPSConnectionPool(host='zpayz.cn', port=443): Read timed out. (read timeout=10) 2026-03-08 06:39:41,805 - ZPAY ѯʧ: HTTPSConnectionPool(host='zpayz.cn', port=443): Read timed out. (read timeout=10) 2026-03-08 06:40:22,362 - ZPAY ѯʧ: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) +2026-03-10 05:01:08,564 - nomadvip payh5/redirect : item={'user_id': 'user1773135567826', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'pc', 'client_ip': '192.168.41.222'} +2026-03-10 05:02:23,523 - nomadvip ZPAY ѯʧ: HTTPSConnectionPool(host='zpayz.cn', port=443): Read timed out. (read timeout=10) +2026-03-10 05:02:46,901 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:03:26,325 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:04:54,606 - nomadvip ZPAY ѯʧ: ('Connection aborted.', ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)) +2026-03-10 05:06:39,639 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:07:03,035 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:07:37,981 - nomadvip ZPAY ѯʧ: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) +2026-03-10 05:08:31,484 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:15:55,977 - nomadvip payh5/redirect : item={'user_id': 'user1773136912337', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://192.168.41.222:3000/paid', 'device': 'wechat', 'client_ip': '192.168.41.34'} +2026-03-10 05:22:16,735 - nomadvip payh5/redirect : item={'user_id': 'user1773131395284', 'total_fee': 18800, 'type': 'vip', 'channel': 'wxpay', 'return_url': 'http://localhost:3000/paid', 'device': 'pc', 'client_ip': '127.0.0.1'}