This commit is contained in:
2026-06-10 17:56:57 +08:00
parent 25ffd45e40
commit 509480d20c
+43 -65
View File
@@ -149,18 +149,30 @@ def _get_alipay_client(app_id: str, private_key: str, public_key: str, gateway:
)
return None
config = AlipayClientConfig()
config.server_url = gateway or "https://openapi.alipay.com/gateway.do"
config.app_id = app_id
config.app_private_key = private_key
config.alipay_public_key = public_key
config.sign_type = "RSA2"
config.charset = "utf-8"
# 配置 SSL 验证相关参数
# 如果服务器缺少 CA 证书,可以尝试以下方案:
# 1. 设置 verify_ssl = False(不推荐生产环境)
# 2. 指定 CA 证书路径: config.ca_certificates = "/path/to/ca-certificates.crt"
try:
config = AlipayClientConfig()
config.server_url = gateway or "https://openapi.alipay.com/gateway.do"
config.app_id = app_id
config.app_private_key = private_key
config.alipay_public_key = public_key
config.sign_type = "RSA2"
config.charset = "utf-8"
_alipay_client = DefaultAlipayClient(alipay_client_config=config)
# 尝试禁用 SSL 验证(仅用于解决证书问题)
config.verify_ssl = False
logger.warning("SSL verification disabled for Alipay API (temporary workaround)")
except AttributeError:
# 如果配置对象不支持 verify_ssl 属性,使用备选方案
logger.info("AlipayClientConfig does not support verify_ssl attribute")
pass
try:
_alipay_client = DefaultAlipayClient(config, logger)
_alipay_client_app_id = app_id
logger.info(f"Alipay client initialized successfully for app_id={app_id}")
except Exception:
logger.exception("Failed to initialize Alipay client")
_alipay_client = None
@@ -206,27 +218,10 @@ async def create_recharge_order(
raise ValueError("微信支付未完成配置,请联系管理员")
total_credits = credits + bonus_credits
# Generate order number once
order_no = generate_order_no()
# For real payment methods, create payment request BEFORE saving order to DB
qr_url = None
if not mock_mode and method == "alipay":
# Try to create Alipay order first
qr_url = _create_alipay_order_with_params(
order_no=order_no,
amount=price,
credits=total_credits,
db_configs=db_configs,
)
if not qr_url:
raise ValueError("支付宝预下单失败,请检查配置或稍后重试")
order = PaymentOrder(
id=generate_id(),
user_id=user_id,
order_no=order_no,
order_no=generate_order_no(),
amount=price,
credits=total_credits,
payment_method=method,
@@ -258,9 +253,14 @@ async def create_recharge_order(
# Real payment: delegate to WeChat or Alipay
if method == "wechat":
_create_wechat_order(order, db_configs)
elif method == "alipay" and qr_url:
# Attach QR URL to the order instance (transient, not persisted)
order.qr_url = qr_url # type: ignore[attr-defined]
elif method == "alipay":
qr_url = _create_alipay_order(order, db_configs)
if qr_url:
# Attach QR URL to the order instance (transient, not persisted)
order.qr_url = qr_url # type: ignore[attr-defined]
else:
# Precreate failed — do not leave a pending order that can never be paid
raise ValueError("支付宝预下单失败,请检查配置或稍后重试")
return order
@@ -288,17 +288,10 @@ def _create_wechat_order(order: PaymentOrder, db_configs: dict[str, str]) -> Non
# ---------------------------------------------------------------------------
def _create_alipay_order_with_params(
order_no: str,
amount: float,
credits: float,
db_configs: dict[str, str],
) -> str | None:
def _create_alipay_order(order: PaymentOrder, db_configs: dict[str, str]) -> str | None:
"""Call Alipay ``trade.precreate`` to obtain a QR code URL.
This version accepts parameters directly instead of an order object,
allowing us to call it before creating the database record.
Reads all Alipay config from the database (admin panel).
Returns the ``qr_code`` URL on success, or ``None`` on failure.
"""
app_id = db_configs.get("payment_alipay_app_id", "")
@@ -327,14 +320,14 @@ def _create_alipay_order_with_params(
# 构造业务参数
model = AlipayTradePrecreateModel()
model.out_trade_no = order_no
model.total_amount = f"{amount:.2f}"
model.subject = f"充值订单 {order_no}"
model.out_trade_no = order.order_no
model.total_amount = f"{order.amount:.2f}"
model.subject = f"充值订单 {order.order_no}"
model.product_code = "QR_CODE_OFFLINE"
body_parts = []
if credits > 0:
body_parts.append(f"{credits}积分")
if order.credits > 0:
body_parts.append(f"{order.credits}积分")
if body_parts:
model.body = " ".join(body_parts)
@@ -344,7 +337,7 @@ def _create_alipay_order_with_params(
# 执行API调用
response_content = client.execute(request)
if not response_content:
logger.error(f"Alipay precreate failed: empty response, order_no={order_no}")
logger.error(f"Alipay precreate failed: empty response, order_no={order.order_no}")
return None
# 解析响应结果
@@ -354,7 +347,7 @@ def _create_alipay_order_with_params(
if response.is_success():
qr_url = response.qr_code
logger.info(
f"Alipay precreate success: order_no={order_no}, "
f"Alipay precreate success: order_no={order.order_no}, "
f"qr_url={qr_url}"
)
return qr_url
@@ -362,30 +355,15 @@ def _create_alipay_order_with_params(
logger.error(
f"Alipay precreate failed: code={response.code}, "
f"msg={response.msg}, sub_code={response.sub_code}, "
f"sub_msg={response.sub_msg}, order_no={order_no}"
f"sub_msg={response.sub_msg}, order_no={order.order_no}"
)
return None
except Exception:
logger.exception(f"Alipay precreate exception: order_no={order_no}")
logger.exception(f"Alipay precreate exception: order_no={order.order_no}")
return None
def _create_alipay_order(order: PaymentOrder, db_configs: dict[str, str]) -> str | None:
"""Call Alipay ``trade.precreate`` to obtain a QR code URL.
Reads all Alipay config from the database (admin panel).
Returns the ``qr_code`` URL on success, or ``None`` on failure.
Deprecated: Use _create_alipay_order_with_params instead for better error handling.
"""
return _create_alipay_order_with_params(
order_no=order.order_no,
amount=order.amount,
credits=order.credits,
db_configs=db_configs,
)
# ---------------------------------------------------------------------------
# Alipay callback verification
# ---------------------------------------------------------------------------