1
This commit is contained in:
@@ -153,6 +153,7 @@ async def wechat_callback(request: Request, db: AsyncSession = Depends(get_db)):
|
|||||||
logger.warning("Wechatpay-Serial header missing, skipping signature verification")
|
logger.warning("Wechatpay-Serial header missing, skipping signature verification")
|
||||||
|
|
||||||
# 解密回调数据:使用 API v3 key
|
# 解密回调数据:使用 API v3 key
|
||||||
|
# 官方文档:https://pay.weixin.qq.com/doc/v3/merchant/4012071382
|
||||||
import json
|
import json
|
||||||
body_data = json.loads(body_str) if body_str else {}
|
body_data = json.loads(body_str) if body_str else {}
|
||||||
resource = body_data.get("resource", {})
|
resource = body_data.get("resource", {})
|
||||||
@@ -161,18 +162,32 @@ async def wechat_callback(request: Request, db: AsyncSession = Depends(get_db)):
|
|||||||
logger.error("WeChat callback resource not found")
|
logger.error("WeChat callback resource not found")
|
||||||
raise HTTPException(status_code=400, detail="数据格式错误")
|
raise HTTPException(status_code=400, detail="数据格式错误")
|
||||||
|
|
||||||
|
# 验证加密算法(官方文档要求固定为 AEAD_AES_256_GCM)
|
||||||
|
algorithm = resource.get("algorithm", "")
|
||||||
|
if algorithm != "AEAD_AES_256_GCM":
|
||||||
|
logger.error(f"WeChat callback unsupported algorithm: {algorithm}")
|
||||||
|
raise HTTPException(status_code=400, detail="不支持的加密算法")
|
||||||
|
|
||||||
ciphertext = resource.get("ciphertext", "")
|
ciphertext = resource.get("ciphertext", "")
|
||||||
associated_data = resource.get("associated_data", "")
|
associated_data = resource.get("associated_data", "")
|
||||||
nonce_str = resource.get("nonce", "")
|
nonce_str = resource.get("nonce", "")
|
||||||
|
|
||||||
# 使用 AES-GCM 解密
|
# 参数验证
|
||||||
|
if not ciphertext:
|
||||||
|
logger.error("WeChat callback ciphertext is empty")
|
||||||
|
raise HTTPException(status_code=400, detail="密文为空")
|
||||||
|
if not nonce_str:
|
||||||
|
logger.error("WeChat callback nonce is empty")
|
||||||
|
raise HTTPException(status_code=400, detail="随机数为空")
|
||||||
|
|
||||||
|
# 使用 AES-GCM 解密(符合官方文档规范)
|
||||||
try:
|
try:
|
||||||
# API v3 key 需要转换为字节串
|
# API v3 key 需要转换为字节串
|
||||||
api_v3_key_bytes = api_v3_key.encode('utf-8')
|
api_v3_key_bytes = api_v3_key.encode('utf-8')
|
||||||
|
|
||||||
# ciphertext 和 nonce 是 Base64 编码的,需要解码
|
# ciphertext 和 nonce 是 Base64 编码的,需要解码
|
||||||
ciphertext_bytes = b64decode(ciphertext) if ciphertext else b''
|
ciphertext_bytes = b64decode(ciphertext)
|
||||||
nonce_bytes = b64decode(nonce_str) if nonce_str else b''
|
nonce_bytes = b64decode(nonce_str)
|
||||||
|
|
||||||
# associated_data 是字符串,直接编码
|
# associated_data 是字符串,直接编码
|
||||||
associated_data_bytes = associated_data.encode('utf-8') if associated_data else b''
|
associated_data_bytes = associated_data.encode('utf-8') if associated_data else b''
|
||||||
@@ -180,8 +195,8 @@ async def wechat_callback(request: Request, db: AsyncSession = Depends(get_db)):
|
|||||||
aesgcm = AESGCM(api_v3_key_bytes)
|
aesgcm = AESGCM(api_v3_key_bytes)
|
||||||
decrypted_str = aesgcm.decrypt(ciphertext_bytes, associated_data_bytes, nonce_bytes)
|
decrypted_str = aesgcm.decrypt(ciphertext_bytes, associated_data_bytes, nonce_bytes)
|
||||||
except InvalidTag:
|
except InvalidTag:
|
||||||
logger.error("WeChat callback decryption failed: Invalid tag")
|
logger.error("WeChat callback decryption failed: Invalid tag (key or data mismatch)")
|
||||||
raise HTTPException(status_code=400, detail="数据解密失败")
|
raise HTTPException(status_code=400, detail="数据解密失败(密钥或数据不匹配)")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"WeChat callback decryption failed: {e}")
|
logger.error(f"WeChat callback decryption failed: {e}")
|
||||||
raise HTTPException(status_code=400, detail="数据解密失败")
|
raise HTTPException(status_code=400, detail="数据解密失败")
|
||||||
|
|||||||
Reference in New Issue
Block a user