Python 支付宝订单加签遇到 UnicodeDecodeError
结论
问题不在 charset,而在于调用的方法不对。
使用 AlipayTradeAppPayRequest 生成 App 支付订单串时,应调用:
client.sdk_execute(payreq)
而不是:
client.execute(payreq)
sdk_execute() 会在本地组装参数、完成 RSA2 签名,并返回可以直接交给支付宝客户端 SDK 的订单字符串。execute() 会向支付宝网关发送服务端请求,再尝试按 UTF-8 解码响应内容,所以代码才会在 __parse_response() 中触发 UnicodeDecodeError。
正确的实现方式
下面是支付宝 App 支付的示例:
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.domain.AlipayTradeAppPayModel import AlipayTradeAppPayModel
from alipay.aop.api.request.AlipayTradeAppPayRequest import AlipayTradeAppPayRequest
config = AlipayClientConfig()
config.server_url = "https://openapi.alipay.com/gateway.do"
config.app_id = "你的 APP_ID"
config.app_private_key = """-----BEGIN PRIVATE KEY-----
你的应用私钥
-----END PRIVATE KEY-----"""
config.alipay_public_key = """-----BEGIN PUBLIC KEY-----
支付宝公钥
-----END PUBLIC KEY-----"""
config.charset = "utf-8"
config.sign_type = "RSA2"
client = DefaultAlipayClient(
alipay_client_config=config,
logger=None
)
model = AlipayTradeAppPayModel()
model.subject = "订单标题"
model.out_trade_no = "202609090001"
model.total_amount = "0.01"
model.product_code = "QUICK_MSECURITY_PAY"
model.timeout_express = "30m"
payreq = AlipayTradeAppPayRequest(biz_model=model)
payreq.notify_url = "https://example.com/alipay/notify"
# 生成已经签名的 App 支付订单串,不向支付宝网关发送请求
order_string = client.sdk_execute(payreq)
print(order_string)
服务端只需把 order_string 返回给 App,客户端再将这段字符串交给支付宝 App SDK 发起支付。
execute() 和 sdk_execute() 的区别
sdk_execute()
这个方法用于生成客户端调用参数,例如 App 支付:
order_string = client.sdk_execute(payreq)
它会组装公共参数和业务参数,完成 URL 编码,使用应用私钥进行 RSA2 签名,然后返回支付订单字符串。整个过程不会访问支付宝网关,也不需要解析网关响应。
execute()
这个方法用于服务端直接调用支付宝 OpenAPI,例如查询订单或申请退款:
response = client.execute(request)
它会访问支付宝网关,并将响应内容解码、解析为 JSON。当前异常就发生在解码阶段:
response_str = response_str.decode(self.__config.charset)
因此,反复修改 charset 无法解决方法调用错误。
如果服务端接口也出现相同错误
查询订单、退款等接口本来就应该使用 execute()。如果调用这些接口时仍然报错,需要检查收到的原始响应。
支付宝 OpenAPI 的正常响应通常是与配置字符集一致的 JSON。如果实际收到的是 GBK 页面、代理错误页或其他二进制内容,使用 UTF-8 强制解码就会失败。
可以按以下方向排查:
- 确认
server_url是否正确:
# 正式环境
https://openapi.alipay.com/gateway.do
# 沙箱环境
https://openapi.alipaydev.com/gateway.do
- 检查请求是否经过公司代理、抓包工具、网关或 WAF。
- 确认返回内容是否为 HTML 错误页,而不是支付宝 JSON。
- 确认安装的 SDK 来自支付宝官方发布渠道。
- 检查应用私钥、支付宝公钥和签名类型是否匹配。
- 检查是否混用了沙箱应用信息和正式环境网关。
调试时,可以临时在 SDK 解码前打印部分原始数据,确认实际返回的内容:
print(repr(response_str[:500]))
这段代码只适合调试。不要用下面的方式绕过解码错误:
response_str.decode("utf-8", errors="ignore")
忽略非法字节可能会破坏 JSON 内容或签名验签结果。
关于官方 Python 示例
支付宝开放平台提供 Python SDK,代码中的 alipay.aop.api 是常见的模块结构。在官方 SDK 的说明或示例中,生成 App 支付订单通常使用 sdk_execute(),查询、退款等服务端 OpenAPI 则使用 execute()。
开放平台的文档入口和 SDK 发布方式可能会调整。下载时应以支付宝开放平台当前提供的 SDK、示例或 API 调试工具为准,避免使用长期未维护的第三方封装。
对于当前代码,把:
response_content = client.execute(payreq)
改为:
response_content = client.sdk_execute(payreq)
通常就能解决问题。
备注:内容仅供参考。
