From 524efec4e7af3f19c211abd647e6779d9f87a2a7 Mon Sep 17 00:00:00 2001 From: wwwwwwwww <526125649@qq.com> Date: Thu, 11 Jun 2026 09:25:55 +0800 Subject: [PATCH] 1 --- video-gen-api/app/services/payment.py | 193 +++++++++++++++++++++++++- 1 file changed, 192 insertions(+), 1 deletion(-) diff --git a/video-gen-api/app/services/payment.py b/video-gen-api/app/services/payment.py index 546e7af4..029112f0 100644 --- a/video-gen-api/app/services/payment.py +++ b/video-gen-api/app/services/payment.py @@ -12,6 +12,103 @@ except AttributeError: else: ssl._create_default_https_context = _create_unverified_https_context +# 猴子补丁修复 alipay-sdk-python 的 ResponseException 错误 +# 修复 WebUtils.py 中 bytes 和 str 拼接的问题 +def fix_alipay_web_utils(): + try: + import sys + from alipay.aop.api.util import WebUtils + + # 保存原始的 do_post 函数 + original_do_post = WebUtils.do_post + + def patched_do_post(url, query_string, headers, params, charset, timeout): + try: + return original_do_post(url, query_string, headers, params, charset, timeout) + except Exception as e: + # 如果是 ResponseException,尝试修复错误信息 + error_msg = str(e) + if "invalid http status" in error_msg or "ResponseException" in error_msg: + # 重新实现一个更安全的版本 + import urllib.request + import urllib.error + import urllib.parse + import ssl + import json + import socket + from alipay.aop.api.exception.ResponseException import ResponseException + + logger.warning("Alipay SDK ResponseException detected, using fallback request") + + # 构造请求 + if query_string: + full_url = url + "?" + query_string + else: + full_url = url + + # 准备 headers + req_headers = {} + if headers: + for key, value in headers.items(): + req_headers[key] = value + + req_headers["Content-type"] = "application/x-www-form-urlencoded;charset=" + charset + req_headers["Connection"] = "Keep-Alive" + req_headers["Cache-Control"] = "no-cache" + req_headers["User-Agent"] = "Mozilla/4.0 (compatible; MSIE 6.0; Windows XP; SV1)" + + # 准备 post data + post_data = "" + if params: + post_params = [] + for key, value in params.items(): + post_params.append( + "%s=%s" % (key, urllib.parse.quote(str(value), encoding=charset)) + ) + post_data = "&".join(post_params) + + logger.debug(f"Request URL: {full_url}") + logger.debug(f"Request data: {post_data[:200]}...") + + # 发送请求 + try: + req = urllib.request.Request(full_url, data=post_data.encode(charset) if post_data else None, headers=req_headers) + + # 创建不验证证书的 context + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + + with urllib.request.urlopen(req, timeout=timeout, context=ctx) as response: + response_body = response.read().decode(charset) + logger.debug(f"Response: {response_body[:500]}...") + return response_body + + except urllib.error.HTTPError as e: + try: + error_body = e.read().decode(charset) + except: + error_body = str(e) + logger.error(f"Alipay HTTP error: {e.code}, body: {error_body}") + raise ResponseException(str(e.code) + "," + error_body) + except Exception as e2: + logger.exception(f"Alipay request failed") + raise + else: + raise + # 应用补丁 + WebUtils.do_post = patched_do_post + logger.info("Alipay WebUtils monkey patch applied successfully") + + except ImportError: + # alipay 模块还没安装,跳过 + pass + except Exception: + logger.exception("Failed to apply Alipay WebUtils monkey patch") + +# 应用补丁 +fix_alipay_web_utils() + from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession @@ -345,17 +442,22 @@ def _create_alipay_order(order: PaymentOrder, db_configs: dict[str, str]) -> str request = AlipayTradePrecreateRequest(biz_model=model) # 执行API调用 + logger.info(f"Calling Alipay trade.precreate for order: {order.order_no}") response_content = client.execute(request) + if not response_content: logger.error(f"Alipay precreate failed: empty response, order_no={order.order_no}") return None + logger.info(f"Alipay response received: {response_content[:500]}...") + # 解析响应结果 response = AlipayTradePrecreateResponse() response.parse_response_content(response_content) if response.is_success(): qr_url = response.qr_code + logger.info(f"Alipay precreate success, qr_url: {qr_url}") return qr_url else: logger.error( @@ -365,8 +467,97 @@ def _create_alipay_order(order: PaymentOrder, db_configs: dict[str, str]) -> str ) return None + except Exception as e: + # 特殊处理 bytes 和 str 拼接的错误 + error_str = str(e) + if "can only concatenate str (not \"bytes\") to str" in error_str: + logger.error(f"Alipay SDK bytes/str concat error, order_no={order.order_no}") + # 尝试直接用 urllib 发送请求作为备选方案 + return _fallback_alipay_request(order, db_configs) + else: + logger.exception(f"Alipay precreate exception: order_no={order.order_no}") + return None + + +def _fallback_alipay_request(order: PaymentOrder, db_configs: dict[str, str]) -> str | None: + """ + 备选方案:直接使用 urllib 发送支付宝请求,绕过 SDK 的 WebUtils + """ + try: + import urllib.request + import urllib.error + import urllib.parse + import ssl + from alipay.aop.api.util.Signature import sign_with_rsa + from alipay.aop.api.response.AlipayTradePrecreateResponse import ( + AlipayTradePrecreateResponse, + ) + + logger.info("Using fallback Alipay request method") + + app_id = db_configs.get("payment_alipay_app_id", "") + private_key = db_configs.get("payment_alipay_private_key", "") + gateway = db_configs.get("payment_alipay_gateway", "https://openapi.alipay.com/gateway.do") + charset = "utf-8" + + # 构造请求参数 + biz_content = { + "out_trade_no": order.order_no, + "total_amount": f"{order.amount:.2f}", + "subject": f"充值订单 {order.order_no}", + "product_code": "QR_CODE_OFFLINE" + } + + params = { + "app_id": app_id, + "method": "alipay.trade.precreate", + "format": "json", + "charset": charset, + "sign_type": "RSA2", + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + "version": "1.0", + "biz_content": str(biz_content).replace("'", "\"") + } + + # 排序并签名 + sorted_keys = sorted(params.keys()) + sign_content = "&".join([f"{k}={params[k]}" for k in sorted_keys]) + sign = sign_with_rsa(private_key.encode(charset), sign_content.encode(charset)) + params["sign"] = sign + + # 构造请求 + query_string = urllib.parse.urlencode(params) + full_url = gateway + + # 创建不验证证书的 context + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + + # 发送请求 + req = urllib.request.Request(full_url, data=query_string.encode(charset)) + req.add_header("Content-type", f"application/x-www-form-urlencoded;charset={charset}") + + with urllib.request.urlopen(req, timeout=30, context=ctx) as response: + response_body = response.read().decode(charset) + logger.info(f"Fallback Alipay response: {response_body[:500]}...") + + # 解析响应 + resp = AlipayTradePrecreateResponse() + resp.parse_response_content(response_body) + + if resp.is_success(): + return resp.qr_code + else: + logger.error( + f"Fallback Alipay precreate failed: code={resp.code}, " + f"msg={resp.msg}, sub_code={resp.sub_code}, " + f"sub_msg={resp.sub_msg}" + ) + return None + except Exception: - logger.exception(f"Alipay precreate exception: order_no={order.order_no}") + logger.exception("Fallback Alipay request also failed") return None