diff --git a/video-gen-api/app/api/v1/payments.py b/video-gen-api/app/api/v1/payments.py index c83d2034..5c8f3840 100644 --- a/video-gen-api/app/api/v1/payments.py +++ b/video-gen-api/app/api/v1/payments.py @@ -153,6 +153,7 @@ async def wechat_callback(request: Request, db: AsyncSession = Depends(get_db)): logger.warning("Wechatpay-Serial header missing, skipping signature verification") # 解密回调数据:使用 API v3 key + # 官方文档:https://pay.weixin.qq.com/doc/v3/merchant/4012071382 import json body_data = json.loads(body_str) if body_str else {} 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") 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", "") associated_data = resource.get("associated_data", "") 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: # API v3 key 需要转换为字节串 api_v3_key_bytes = api_v3_key.encode('utf-8') # ciphertext 和 nonce 是 Base64 编码的,需要解码 - ciphertext_bytes = b64decode(ciphertext) if ciphertext else b'' - nonce_bytes = b64decode(nonce_str) if nonce_str else b'' + ciphertext_bytes = b64decode(ciphertext) + nonce_bytes = b64decode(nonce_str) # associated_data 是字符串,直接编码 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) decrypted_str = aesgcm.decrypt(ciphertext_bytes, associated_data_bytes, nonce_bytes) except InvalidTag: - logger.error("WeChat callback decryption failed: Invalid tag") - raise HTTPException(status_code=400, detail="数据解密失败") + logger.error("WeChat callback decryption failed: Invalid tag (key or data mismatch)") + raise HTTPException(status_code=400, detail="数据解密失败(密钥或数据不匹配)") except Exception as e: logger.error(f"WeChat callback decryption failed: {e}") raise HTTPException(status_code=400, detail="数据解密失败")