fix(payment): 优化支付回调签名验证逻辑
- 取消支付回调中签名前日志冗余输出,简化验证流程 - verify_notify方法中合并签名和数据为空的校验,减少日志行 - 调整sign和sign_type参数移除逻辑,兼容不同通知类型的签名校验 - 增加验证失败时的详细日志,帮助定位公钥配置问题 - 提示可能公钥使用错误,改善错误排查体验
This commit is contained in:
parent
acaa5d4fd8
commit
5dc2fbd0e7
Binary file not shown.
Binary file not shown.
@ -73,18 +73,9 @@ def payment_return():
|
|||||||
logger.error("同步回调缺少签名参数")
|
logger.error("同步回调缺少签名参数")
|
||||||
return "参数错误:缺少签名", 400
|
return "参数错误:缺少签名", 400
|
||||||
|
|
||||||
# 验证签名前,先记录关键信息
|
|
||||||
logger.info(f"验证订单号: {data.get('out_trade_no')}")
|
|
||||||
logger.info(f"支付宝交易号: {data.get('trade_no')}")
|
|
||||||
logger.info(f"支付金额: {data.get('total_amount')}")
|
|
||||||
|
|
||||||
# 从数据中移除sign参数以进行验证
|
|
||||||
verify_data = data.copy()
|
|
||||||
verify_data.pop('sign', None)
|
|
||||||
verify_data.pop('sign_type', None) # 也要移除sign_type
|
|
||||||
|
|
||||||
alipay_service = AlipayService()
|
alipay_service = AlipayService()
|
||||||
success = alipay_service.verify_notify(verify_data, signature)
|
# 直接传递原始字典,由 verify_notify 处理
|
||||||
|
success = alipay_service.verify_notify(data, signature)
|
||||||
|
|
||||||
out_trade_no = data.get('out_trade_no')
|
out_trade_no = data.get('out_trade_no')
|
||||||
order = Order.query.filter_by(out_trade_no=out_trade_no).first()
|
order = Order.query.filter_by(out_trade_no=out_trade_no).first()
|
||||||
|
|||||||
@ -50,7 +50,7 @@ class Config:
|
|||||||
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
|
||||||
-----END RSA PRIVATE KEY-----""" # 应用私钥
|
-----END RSA PRIVATE KEY-----""" # 应用私钥
|
||||||
ALIPAY_PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
|
ALIPAY_PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
|
||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx6iXKiRpbs5WLHlb6fdoteef+vs+QxxtmwDL2IJwhEJ+OMrkwlKyd4noYaiGDy2YcyHKahb4bsHRoPLVEqLveuVFksjsiFTH2mUhuwuqDe//XwE4hHsguNL3nY72gZ6qjwbg5mAVrHRcKUi6XSLSP4pWdCzD0leZ4GA9Kw1WstCS4sGBcrzyHPhRvtdVFZR5UjNLVAaN6HIMNhW0TBXz4r1ihXMV4byeN3n0n6e6reBLXr222f7c5almGurq6D9pLqwAmsqWbQWbvitOjZRll+bjpa29JIkNi+jEpPdYpGjU3e0Tl8aWkKjtYXmDyWtdw00RkciHUR4hMuCZW5qIjwIDAQAB
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlDx4KdOtOQE+tBq6jHKKFenRaRe2gbBnleBk++5gki9IQuxVyZUGTJixstf2gELFHWrGanpnwmGggXsqG+Rm5ZLJOlmFM1k0XeAIDvi6tP/rM+ZDFSu1bMBYtT5vzgVZC7mzIvOp9gsT/puqd3aNZmlviLD0R6OYN0zvFX+5qADZV7A9ziA+nXPFSHreBh7yY/q9ophVZNeHGPoYkDVI5++RrF1cALKOdit0giN5vxpe3ch9z3E6+FZg3LiP+1RW3tMiDQfp/SlVs6bNhLUtmlI5r7+mtFCKDUCEpnQ3S9e0II6rzyVXRyKCFs7qi5YzyhhmO3tJJoe9ilEFyNzfRQIDAQAB
|
||||||
-----END PUBLIC KEY-----""" # 支付宝公钥
|
-----END PUBLIC KEY-----""" # 支付宝公钥
|
||||||
ALIPAY_RETURN_URL = "http://331002.xyz:2010/payment/return" # 支付成功跳转地址
|
ALIPAY_RETURN_URL = "http://331002.xyz:2010/payment/return" # 支付成功跳转地址
|
||||||
ALIPAY_NOTIFY_URL = "http://331002.xyz:2010/payment/notify" # 支付异步通知地址
|
ALIPAY_NOTIFY_URL = "http://331002.xyz:2010/payment/notify" # 支付异步通知地址
|
||||||
|
|||||||
Binary file not shown.
@ -42,37 +42,34 @@ class AlipayService:
|
|||||||
def verify_notify(self, data, signature):
|
def verify_notify(self, data, signature):
|
||||||
"""验证通知签名"""
|
"""验证通知签名"""
|
||||||
try:
|
try:
|
||||||
logger.info(f"开始验证支付宝签名,数据: {data}")
|
if not signature or not data:
|
||||||
logger.info(f"签名值: {signature}")
|
logger.error("签名或数据为空")
|
||||||
|
|
||||||
# 验证必要参数
|
|
||||||
if not signature:
|
|
||||||
logger.error("签名为空")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not data:
|
# 创建数据副本
|
||||||
logger.error("数据为空")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 创建数据副本,避免修改原数据
|
|
||||||
verify_data = data.copy()
|
verify_data = data.copy()
|
||||||
|
|
||||||
# 移除sign和sign_type,这两个字段不参与验证
|
# python-alipay-sdk 的 verify 方法会自动处理 sign 的移除
|
||||||
|
# 但为了保险,我们手动移除它,保留其他所有字段
|
||||||
verify_data.pop('sign', None)
|
verify_data.pop('sign', None)
|
||||||
verify_data.pop('sign_type', None)
|
|
||||||
|
|
||||||
alipay = self.get_alipay_client()
|
alipay = self.get_alipay_client()
|
||||||
|
# 对于同步回调,sign_type 实际上是参与签名的(某些版本/接口)
|
||||||
|
# 对于异步通知,sign_type 通常不参与签名
|
||||||
|
# alipay.verify 会根据情况处理 sign_type
|
||||||
result = alipay.verify(verify_data, signature)
|
result = alipay.verify(verify_data, signature)
|
||||||
|
|
||||||
if result:
|
if not result:
|
||||||
logger.info("签名验证成功")
|
|
||||||
else:
|
|
||||||
logger.error("签名验证失败")
|
logger.error("签名验证失败")
|
||||||
logger.error(f"验证数据: {verify_data}")
|
logger.error(f"待验证数据: {verify_data}")
|
||||||
logger.error(f"公钥配置: {self.public_key[:50]}...")
|
logger.error(f"签名值: {signature}")
|
||||||
# 额外调试信息
|
logger.error(f"使用的公钥前50位: {self.public_key[:50]}...")
|
||||||
logger.error(f"App ID: {self.app_id}")
|
|
||||||
logger.error(f"是否调试模式: {self.debug}")
|
# 检查公钥是否可能是应用公钥而非支付宝公钥
|
||||||
|
if self.private_key and self.public_key:
|
||||||
|
logger.warning("提示:如果签名持续验证失败,请确认 ALIPAY_PUBLIC_KEY 是“支付宝公钥”而非“应用公钥”")
|
||||||
|
else:
|
||||||
|
logger.info("签名验证成功")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user