From c7eba3683b64ccc86551564140b54b71ff676bb2 Mon Sep 17 00:00:00 2001 From: wwwwwwwww <526125649@qq.com> Date: Thu, 11 Jun 2026 09:39:46 +0800 Subject: [PATCH] 1 --- video-gen-api/app/services/payment.py | 222 +++----------------------- 1 file changed, 24 insertions(+), 198 deletions(-) diff --git a/video-gen-api/app/services/payment.py b/video-gen-api/app/services/payment.py index 6b35e1c4..ff084bf5 100644 --- a/video-gen-api/app/services/payment.py +++ b/video-gen-api/app/services/payment.py @@ -4,11 +4,6 @@ import certifi import ssl from datetime import datetime, timedelta -# 先定义 logger -import time as _time -logger = logging.getLogger("payment") -logger.setLevel(logging.INFO) - # 尝试禁用 SSL 验证(用于解决证书问题) try: _create_unverified_https_context = ssl._create_unverified_context @@ -17,100 +12,6 @@ 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: - 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 or "can only concatenate str (not \"bytes\") to str" in error_msg: - # 重新实现一个更安全的版本 - import urllib.request - import urllib.error - import urllib.parse - import ssl - 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 @@ -123,6 +24,10 @@ from app.utils.id_gen import generate_id, generate_order_no # --------------------------------------------------------------------------- # Payment logger → log/payment/YYYY-MM-DD.log (one file per day, no cleanup) # --------------------------------------------------------------------------- +import time as _time + +logger = logging.getLogger("payment") +logger.setLevel(logging.INFO) _log_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "log", "payment") os.makedirs(_log_dir, exist_ok=True) @@ -237,7 +142,7 @@ _alipay_client = None _alipay_client_app_id = None -def _get_alipay_client(app_id: str, private_key: str, public_key: str, gateway: str = "", notify_url: str = ""): +def _get_alipay_client(app_id: str, private_key: str, public_key: str, gateway: str = ""): """Get or create an Alipay client. Recreated if app_id changes.""" global _alipay_client, _alipay_client_app_id @@ -261,8 +166,6 @@ def _get_alipay_client(app_id: str, private_key: str, public_key: str, gateway: config.alipay_public_key = public_key config.sign_type = "RSA2" config.charset = "utf-8" - config.notify_url = notify_url - config.notify_type = "json" # 先尝试使用 certifi 证书 config.ca_certificates = certifi.where() @@ -408,7 +311,7 @@ def _create_alipay_order(order: PaymentOrder, db_configs: dict[str, str]) -> str logger.warning("Alipay config missing in database (app_id / private_key)") return None - client = _get_alipay_client(app_id, private_key, public_key, gateway, notify_url) + client = _get_alipay_client(app_id, private_key, public_key, gateway) if client is None: return None @@ -438,24 +341,30 @@ def _create_alipay_order(order: PaymentOrder, db_configs: dict[str, str]) -> str # 构造请求 request = AlipayTradePrecreateRequest(biz_model=model) + + # 设置 notify_url 在 request 上 + if notify_url: + try: + if hasattr(request, 'set_notify_url'): + request.set_notify_url(notify_url) + elif hasattr(request, 'notify_url'): + request.notify_url = notify_url + logger.info(f"Set notify_url for order {order.order_no}: {notify_url}") + except Exception as e: + logger.warning(f"Failed to set notify_url: {e}") # 执行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( @@ -466,96 +375,13 @@ 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("Fallback Alipay request also failed") + # 处理 SDK 内部的 bytes/str 错误 + if "TypeError" in str(e) and ("bytes" in str(e) or "str" in str(e)): + logger.error( + f"Alipay SDK TypeError (bytes/str issue): order_no={order.order_no}, " + f"error={str(e)}" + ) + logger.exception(f"Alipay precreate exception: order_no={order.order_no}") return None