's'
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user