refactor: 优化后端支付与微信支付

- 优化payment/config.py、wechat_pay.py
- 优化routers/payment.py、plans.py、quota.py、websocket.py
- 更新main.py、.env.production

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
iammm0
2026-02-11 16:06:15 +08:00
parent 240a184da8
commit 44b405d647
8 changed files with 274 additions and 38 deletions

View File

@@ -20,6 +20,10 @@ class WeChatPayConfig:
private_key: str = "" # 商户 API 私钥内容PEM 字符串,可放环境变量,与 private_key_path 二选一)
cert_serial_no: str = "" # 商户证书序列号
notify_url: str = "" # 支付结果回调地址
# 平台公钥模式(二选一):当环境无法访问 api.mch.weixin.qq.com 时使用,无需拉取平台证书
platform_public_key: str = "" # 微信支付平台公钥 PEM 内容(与 platform_public_key_path 二选一)
platform_public_key_path: str = "" # 平台公钥文件路径
platform_public_key_id: str = "" # 微信支付平台公钥 ID与 platform_public_key 同时配置)
@property
def is_configured(self) -> bool:
@@ -29,6 +33,12 @@ class WeChatPayConfig:
has_key, self.cert_serial_no, self.notify_url
])
@property
def use_platform_public_key(self) -> bool:
"""是否使用平台公钥模式(无需访问微信拉取证书)"""
has_pub = bool(self.platform_public_key.strip()) or bool(self.platform_public_key_path.strip())
return has_pub and bool(self.platform_public_key_id.strip())
@dataclass
class AlipayConfig:
@@ -58,19 +68,28 @@ class PaymentConfig:
@classmethod
def from_env(cls) -> "PaymentConfig":
"""从环境变量加载配置"""
# 微信私钥:优先使用 WECHAT_PAY_PRIVATE_KEYPEM 内容),否则用 WECHAT_PAY_PRIVATE_KEY_PATH文件路径
# 微信私钥:优先使用 WECHAT_PAY_PRIVATE_KEY_PATH文件避免 .env 中长 PEM 的转义问题;否则用 WECHAT_PAY_PRIVATE_KEY
wechat_private_key_path = os.getenv("WECHAT_PAY_PRIVATE_KEY_PATH", "").strip()
wechat_private_key = os.getenv("WECHAT_PAY_PRIVATE_KEY", "").strip()
if wechat_private_key and "\\n" in wechat_private_key:
# 去除可能被解析进来的引号与 BOM
if wechat_private_key:
wechat_private_key = wechat_private_key.strip('"').strip("'").lstrip("\ufeff")
wechat_private_key = wechat_private_key.replace("\\n", "\n")
wechat_platform_pub = os.getenv("WECHAT_PAY_PLATFORM_PUBLIC_KEY", "").strip()
if wechat_platform_pub and "\\n" in wechat_platform_pub:
wechat_platform_pub = wechat_platform_pub.replace("\\n", "\n")
config = cls(
wechat=WeChatPayConfig(
app_id=os.getenv("WECHAT_PAY_APP_ID", ""),
mch_id=os.getenv("WECHAT_PAY_MCH_ID", ""),
api_v3_key=os.getenv("WECHAT_PAY_API_V3_KEY", ""),
private_key_path=os.getenv("WECHAT_PAY_PRIVATE_KEY_PATH", ""),
private_key=wechat_private_key,
private_key_path=wechat_private_key_path,
private_key=wechat_private_key if not wechat_private_key_path else "", # 有路径时不再用 env 内容
cert_serial_no=os.getenv("WECHAT_PAY_CERT_SERIAL_NO", ""),
notify_url=os.getenv("WECHAT_PAY_NOTIFY_URL", ""),
platform_public_key=wechat_platform_pub,
platform_public_key_path=os.getenv("WECHAT_PAY_PLATFORM_PUBLIC_KEY_PATH", "").strip(),
platform_public_key_id=os.getenv("WECHAT_PAY_PLATFORM_PUBLIC_KEY_ID", "").strip(),
),
alipay=AlipayConfig(
app_id=os.getenv("ALIPAY_APP_ID", ""),
@@ -84,7 +103,9 @@ class PaymentConfig:
# 日志输出配置状态
if config.wechat.is_configured:
logger.info(f"微信支付配置已加载: APP_ID={config.wechat.app_id}, MCH_ID={config.wechat.mch_id}")
mode = "平台公钥模式" if config.wechat.use_platform_public_key else "平台证书模式"
key_src = "WECHAT_PAY_PRIVATE_KEY(环境变量)" if config.wechat.private_key.strip() else "WECHAT_PAY_PRIVATE_KEY_PATH(文件)"
logger.info(f"微信支付配置已加载: APP_ID={config.wechat.app_id}, MCH_ID={config.wechat.mch_id}, 模式={mode}, 商户私钥={key_src}")
else:
logger.warning("微信支付配置不完整,微信支付将不可用")