This commit is contained in:
2026-06-10 17:49:08 +08:00
parent 10bd2bd6a0
commit 25ffd45e40
+64 -28
View File
@@ -149,16 +149,18 @@ 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"
try:
_alipay_client = DefaultAlipayClient(config, logger)
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)
_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
@@ -204,10 +206,27 @@ 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=generate_order_no(),
order_no=order_no,
amount=price,
credits=total_credits,
payment_method=method,
@@ -239,14 +258,9 @@ async def create_recharge_order(
# Real payment: delegate to WeChat or Alipay
if method == "wechat":
_create_wechat_order(order, db_configs)
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("支付宝预下单失败,请检查配置或稍后重试")
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]
return order
@@ -274,10 +288,17 @@ def _create_wechat_order(order: PaymentOrder, db_configs: dict[str, str]) -> Non
# ---------------------------------------------------------------------------
def _create_alipay_order(order: PaymentOrder, db_configs: dict[str, str]) -> str | None:
def _create_alipay_order_with_params(
order_no: str,
amount: float,
credits: float,
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).
This version accepts parameters directly instead of an order object,
allowing us to call it before creating the database record.
Returns the ``qr_code`` URL on success, or ``None`` on failure.
"""
app_id = db_configs.get("payment_alipay_app_id", "")
@@ -306,14 +327,14 @@ def _create_alipay_order(order: PaymentOrder, db_configs: dict[str, str]) -> str
# 构造业务参数
model = AlipayTradePrecreateModel()
model.out_trade_no = order.order_no
model.total_amount = f"{order.amount:.2f}"
model.subject = f"充值订单 {order.order_no}"
model.out_trade_no = order_no
model.total_amount = f"{amount:.2f}"
model.subject = f"充值订单 {order_no}"
model.product_code = "QR_CODE_OFFLINE"
body_parts = []
if order.credits > 0:
body_parts.append(f"{order.credits}积分")
if credits > 0:
body_parts.append(f"{credits}积分")
if body_parts:
model.body = " ".join(body_parts)
@@ -323,7 +344,7 @@ def _create_alipay_order(order: PaymentOrder, db_configs: dict[str, str]) -> str
# 执行API调用
response_content = client.execute(request)
if not response_content:
logger.error(f"Alipay precreate failed: empty response, order_no={order.order_no}")
logger.error(f"Alipay precreate failed: empty response, order_no={order_no}")
return None
# 解析响应结果
@@ -333,7 +354,7 @@ def _create_alipay_order(order: PaymentOrder, db_configs: dict[str, str]) -> str
if response.is_success():
qr_url = response.qr_code
logger.info(
f"Alipay precreate success: order_no={order.order_no}, "
f"Alipay precreate success: order_no={order_no}, "
f"qr_url={qr_url}"
)
return qr_url
@@ -341,15 +362,30 @@ def _create_alipay_order(order: PaymentOrder, db_configs: dict[str, str]) -> str
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.order_no}"
f"sub_msg={response.sub_msg}, order_no={order_no}"
)
return None
except Exception:
logger.exception(f"Alipay precreate exception: order_no={order.order_no}")
logger.exception(f"Alipay precreate exception: order_no={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
# ---------------------------------------------------------------------------