This commit is contained in:
eric
2026-03-16 11:45:46 -05:00
parent 4187cdd64b
commit e9898ab372
4 changed files with 50 additions and 48 deletions

View File

@@ -2,6 +2,7 @@
digital/cnomadcna/nomadlms 共享的支付逻辑 digital/cnomadcna/nomadlms 共享的支付逻辑
根据 prefix 生成对应 notify_path业务互不影响 根据 prefix 生成对应 notify_path业务互不影响
""" """
import asyncio
import logging import logging
import random import random
import string import string
@@ -156,6 +157,7 @@ def create_digital_router(prefix: str, project_name: str, site_id: str | None =
type_map = { type_map = {
"book": "购买书籍", "book": "购买书籍",
"meetup": "数字游民社区报名", "meetup": "数字游民社区报名",
"salon": "沙龙报名茶歇费",
"video": "视频解锁", "video": "视频解锁",
"vip": "VIP会员充值", "vip": "VIP会员充值",
} }
@@ -215,43 +217,46 @@ def create_digital_router(prefix: str, project_name: str, site_id: str | None =
if not order_id: if not order_id:
return {"paid": False, "error": "missing order_id"} return {"paid": False, "error": "missing order_id"}
try: def _query_pb() -> dict | None:
from ..services.pb import get_pb_client try:
from ..services.pb import get_pb_client
pb = get_pb_client()
records = pb.collection("payments").get_list(1, 1, {"filter": f'order_id = "{order_id}"'})
if records.items:
return {"paid": True, "source": "pocketbase"}
except Exception as error:
_safe_log(logging.WARNING, "%s order_status PocketBase failed: %s", project_name, error)
return None
pb = get_pb_client() def _query_xorpay() -> dict | None:
records = pb.collection("payments").get_list( try:
1, 1, {"filter": f'order_id = "{order_id}"'} xorpay = get_payment_provider_by_name("xorpay")
) r = xorpay.query_order_status(order_id, timeout=6)
if records.items: if r.get("paid"):
return {"paid": True, "source": "pocketbase"} return {"paid": True, "source": "xorpay", "status": r.get("status", "")}
except Exception as error: except Exception as error:
_safe_log(logging.WARNING, "%s order_status PocketBase failed: %s", project_name, error) _safe_log(logging.WARNING, "%s order_status XorPay failed: %s", project_name, error)
return None
try: def _query_zpay() -> dict | None:
xorpay = get_payment_provider_by_name("xorpay") try:
xorpay_result = xorpay.query_order_status(order_id, timeout=8) zpay = get_payment_provider_by_name("zpay")
if xorpay_result.get("paid"): if isinstance(zpay, ZPayProvider):
return { r = zpay.query_order_status(order_id, timeout=6)
"paid": True, if r.get("paid"):
"source": "xorpay", return {"paid": True, "source": "zpay", "status": r.get("status", "")}
"status": xorpay_result.get("status", ""), except Exception as error:
} _safe_log(logging.WARNING, "%s order_status ZPAY failed: %s", project_name, error)
except Exception as error: return None
_safe_log(logging.WARNING, "%s order_status XorPay failed: %s", project_name, error)
try:
zpay = get_payment_provider_by_name("zpay")
if isinstance(zpay, ZPayProvider):
zpay_result = zpay.query_order_status(order_id, timeout=8)
if zpay_result.get("paid"):
return {
"paid": True,
"source": "zpay",
"status": zpay_result.get("status", ""),
}
except Exception as error:
_safe_log(logging.WARNING, "%s order_status ZPAY failed: %s", project_name, error)
# 并行查询 PB、ZPAY、XorPay任一返回 paid 即立即返回PC 扫码用 ZPAY微信用 XorPay
pb_task = asyncio.to_thread(_query_pb)
xorpay_task = asyncio.to_thread(_query_xorpay)
zpay_task = asyncio.to_thread(_query_zpay)
results = await asyncio.gather(pb_task, xorpay_task, zpay_task)
for r in results:
if r:
return r
return {"paid": False} return {"paid": False}
@router.post("/xorpay_notify") @router.post("/xorpay_notify")

View File

@@ -247,11 +247,11 @@ def _decode_pending_email(user_id: str) -> str | None:
def _query_zpay_order(order_id: str) -> dict | None: def _query_zpay_order(order_id: str) -> dict | None:
"""查询 ZPAY 订单状态,返回 {paid, money}""" """查询 ZPAY 订单状态,返回 {paid, money}。使用 get_payment_provider_by_name 确保查询 ZPAY不依赖默认 provider"""
try: try:
from ..payment import get_payment_provider from ..payment import get_payment_provider_by_name
from ..payment.zpay import ZPayProvider from ..payment.zpay import ZPayProvider
provider = get_payment_provider() provider = get_payment_provider_by_name("zpay")
if not isinstance(provider, ZPayProvider): if not isinstance(provider, ZPayProvider):
return None return None
result = provider.query_order_status(order_id, timeout=10) result = provider.query_order_status(order_id, timeout=10)
@@ -262,11 +262,11 @@ def _query_zpay_order(order_id: str) -> dict | None:
def _query_xorpay_paid(order_id: str) -> tuple[bool, str]: def _query_xorpay_paid(order_id: str) -> tuple[bool, str]:
"""查询 XorPay 是否已支付,返回 (paid, pay_price)""" """查询 XorPay 是否已支付,返回 (paid, pay_price)。微信内订单走 XorPay需显式查 xorpay 不依赖默认 provider"""
try: try:
from ..payment import get_payment_provider from ..payment import get_payment_provider_by_name
from ..payment.xorpay import XorPayProvider from ..payment.xorpay import XorPayProvider
provider = get_payment_provider() provider = get_payment_provider_by_name("xorpay")
if not isinstance(provider, XorPayProvider): if not isinstance(provider, XorPayProvider):
return False, "" return False, ""
result = provider.query_order_status(order_id, timeout=8) result = provider.query_order_status(order_id, timeout=8)

View File

@@ -22,10 +22,12 @@ class PocketBaseSDK:
self._client: Optional[PocketBase] = None self._client: Optional[PocketBase] = None
def get_client(self) -> PocketBase: def get_client(self) -> PocketBase:
"""获取已认证的 PocketBase 客户端""" """获取已认证的 PocketBase 客户端(首次登录时认证,后续复用)"""
if self._client is None: if self._client is None:
import logging
self._client = PocketBase(self.url) self._client = PocketBase(self.url)
self._client.admins.auth_with_password(self.admin_email, self.admin_password) self._client.admins.auth_with_password(self.admin_email, self.admin_password)
logging.getLogger(__name__).info("PocketBase admin 登录成功")
return self._client return self._client
def collection(self, name: str): def collection(self, name: str):

View File

@@ -1,10 +1,7 @@
"""PocketBase 客户端(基于 SDK""" """PocketBase 客户端(基于 SDK"""
import logging
from app.config import PB_URL, PB_ADMIN_EMAIL, PB_ADMIN_PASSWORD from app.config import PB_URL, PB_ADMIN_EMAIL, PB_ADMIN_PASSWORD
from app.sdk.pocketbase import PocketBaseSDK from app.sdk.pocketbase import PocketBaseSDK
logger = logging.getLogger(__name__)
_pb_sdk: PocketBaseSDK | None = None _pb_sdk: PocketBaseSDK | None = None
@@ -20,8 +17,6 @@ def _get_pb_sdk() -> PocketBaseSDK:
def get_pb_client(): def get_pb_client():
"""获取已认证的 PocketBase 客户端""" """获取已认证的 PocketBase 客户端(复用已登录实例,不重复打日志)"""
sdk = _get_pb_sdk() sdk = _get_pb_sdk()
client = sdk.get_client() return sdk.get_client()
logger.info("PocketBase admin 登录成功")
return client