Python调用支付宝用户授权接口无法获取openid并签名报错
结论
这个异常出现在客户端生成请求签名的阶段。此时请求还没有发送给支付宝,所以问题与 openid 权限或授权码本身无关。
报错内容是:
request sign failed. int() argument must be a string, a bytes-like object or a real number, not 'Sequence'
最常见的原因是当前 Python SDK 无法正确解析应用私钥,常见情况包括:
- SDK 要求使用 PKCS#1 私钥,实际传入的却是 PKCS#8 私钥;
- 把支付宝公钥、应用公钥或证书填进了应用私钥配置;
- SDK 参数要求传入私钥正文,实际传入的却是私钥文件路径;
- 复制私钥时损坏了 PEM 头尾、换行或 Base64 内容;
- 安装的支付宝 SDK 与示例使用的不是同一个包,或者 SDK 与加密依赖不兼容。
需要先修复签名问题,再检查用户授权流程。签名失败时,请求不会到达支付宝,也就不可能返回 openid。
为什么会出现 Sequence 类型错误
RSA 私钥通常以 ASN.1 结构保存。PKCS#1 和 PKCS#8 使用的外层结构不同:
PKCS#1:
-----BEGIN RSA PRIVATE KEY-----
PKCS#8:
-----BEGIN PRIVATE KEY-----
部分支付宝 Python SDK 或相关依赖会按照 PKCS#1 结构读取私钥。如果传入 PKCS#8 私钥,解析器可能会把外层 ASN.1 Sequence 当作整数处理,因而出现下面的错误:
int() argument must be a string, a bytes-like object or a real number, not 'Sequence'
仅凭这段堆栈还不能断定私钥格式是唯一原因,但应当优先排查这一项。
排查步骤
1. 确认四类密钥没有混用
支付宝开放平台的配置通常涉及以下内容:
- 应用私钥:保存在商户服务端,用来为请求签名;
- 应用公钥:上传到支付宝开放平台;
- 支付宝公钥:用来验证支付宝响应的签名;
- 应用公钥证书、支付宝公钥证书和支付宝根证书:用于证书模式。
使用普通公钥模式时,配置关系通常如下:
client_config.app_private_key = APP_PRIVATE_KEY
client_config.alipay_public_key = ALIPAY_PUBLIC_KEY
app_private_key 中必须填写应用私钥,不能填入应用公钥或支付宝公钥。
如果应用在开放平台选择了证书模式,就需要按照证书模式初始化 SDK,不能用一个“支付宝公钥”代替完整的证书配置。具体字段应以当前 SDK 版本及支付宝对应文档为准。
2. 检查 SDK 要求的是私钥正文还是文件路径
许多版本的支付宝 Python SDK 要求传入完整的 PEM 内容:
APP_PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY-----
这里是私钥内容
-----END RSA PRIVATE KEY-----"""
不要误写成:
client_config.app_private_key = r"C:\keys\app_private_key.pem"
只有在当前 SDK 明确要求该字段接收文件路径时,才能这样配置。
如果私钥来自环境变量,还要处理其中被转义的换行:
import os
APP_PRIVATE_KEY = os.environ["ALIPAY_APP_PRIVATE_KEY"].replace("\\n", "\n")
如果私钥已经包含真实换行,则不要再次转换。
3. 检查并转换私钥格式
先查看私钥的第一行:
-----BEGIN RSA PRIVATE KEY-----
这通常表示 PKCS#1 格式。
-----BEGIN PRIVATE KEY-----
这通常表示 PKCS#8 格式。
如果当前 SDK 明确要求 PKCS#1,可以使用 OpenSSL 转换:
openssl rsa -in app_private_key_pkcs8.pem -traditional -out app_private_key_pkcs1.pem
转换完成后,检查输出文件的头部是否为:
-----BEGIN RSA PRIVATE KEY-----
如果本机 OpenSSL 版本不支持这组参数,可以查看 openssl rsa -help。不要通过手动删除头尾或修改 Base64 内容来“转换”私钥格式。
4. 验证私钥文件本身
可以用 OpenSSL 检查私钥能否正常解析:
openssl pkey -in app_private_key.pem -check -noout
如果命令提示无法读取私钥,应重新导出正确的应用私钥,不必继续调整业务参数。
同时检查以下问题:
- PEM 头尾是否完整;
- 私钥正文中是否混入空格、引号或日志内容;
- 是否误用了公钥;
- 私钥是否经过密码加密,而 SDK 不支持加载加密私钥;
- 应用私钥是否与开放平台中上传的应用公钥配对。
还可以从私钥导出公钥进行比对:
openssl pkey -in app_private_key.pem -pubout -out derived_public_key.pem
导出的公钥应与对应支付宝应用中配置的应用公钥属于同一密钥对。
5. 核对安装的 SDK 和依赖
先确认程序实际导入的是哪个包:
import alipay
import alipay.aop.api
print(alipay.__file__)
print(alipay.aop.api.__file__)
与支付宝有关的 Python 包有多个,它们的接口和密钥要求并不完全相同。安装的包需要与参考示例一致,不要将其他第三方库的初始化方法与 alipay.aop.api.DefaultAlipayClient 混用。
还可以记录相关版本,方便定位问题:
python --version
pip show alipay-sdk-python
pip show rsa
pip show pyasn1
pip freeze
如果实际包名不是 alipay-sdk-python,应以安装的包为准。在没有确认兼容关系之前,不要一次性升级或降级所有加密依赖。更稳妥的办法是在新的虚拟环境中,按照目标 SDK 声明的依赖重新安装并复现问题。
用户授权接口调用示例
下面是常见的使用授权码换取访问令牌的流程。不同 SDK 版本可能使用不同的类名、返回对象和证书模式配置,具体以实际安装的版本为准。
import json
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.domain.AlipaySystemOauthTokenModel import (
AlipaySystemOauthTokenModel,
)
from alipay.aop.api.request.AlipaySystemOauthTokenRequest import (
AlipaySystemOauthTokenRequest,
)
APP_ID = "你的应用APP_ID"
APP_PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY-----
你的应用私钥
-----END RSA PRIVATE KEY-----"""
ALIPAY_PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
支付宝公钥
-----END PUBLIC KEY-----"""
client_config = AlipayClientConfig()
client_config.server_url = "https://openapi.alipay.com/gateway.do"
client_config.app_id = APP_ID
client_config.app_private_key = APP_PRIVATE_KEY
client_config.alipay_public_key = ALIPAY_PUBLIC_KEY
client_config.sign_type = "RSA2"
client = DefaultAlipayClient(alipay_client_config=client_config)
model = AlipaySystemOauthTokenModel()
model.grant_type = "authorization_code"
model.code = "回调地址中收到的auth_code"
request = AlipaySystemOauthTokenRequest(biz_model=model)
response_text = client.execute(request)
response_data = json.loads(response_text)
token_response = response_data.get("alipay_system_oauth_token_response", {})
if token_response.get("code") != "10000":
raise RuntimeError(
f"授权码换取令牌失败:"
f"code={token_response.get('code')}, "
f"sub_code={token_response.get('sub_code')}, "
f"msg={token_response.get('msg')}, "
f"sub_msg={token_response.get('sub_msg')}"
)
access_token = token_response.get("access_token")
open_id = token_response.get("open_id")
user_id = token_response.get("user_id")
print("access_token:", access_token)
print("open_id:", open_id)
print("user_id:", user_id)
某些 SDK 版本更适合直接设置 biz_content:
request = AlipaySystemOauthTokenRequest()
request.biz_content = {
"grant_type": "authorization_code",
"code": auth_code,
}
response_text = client.execute(request)
不要随意混用这两种写法。应先查看当前 SDK 中 AlipaySystemOauthTokenRequest 及对应 Model 的定义。
签名修复后仍没有 openid 怎么办
如果请求已经成功发送,并且支付宝返回了业务响应,可以按顺序检查:
auth_code是否来自支付宝授权回调,而不是其他参数;- 授权码是否已经过期或被使用;
- 生成授权链接时使用的
app_id是否与换取令牌时一致; - 回调地址、应用环境和网关是否一致;
- 应用是否已经开通所需能力,授权范围是否正确;
- 返回结果中提供的是
user_id、open_id,还是只有其中一个; - 是否误从响应的最外层读取字段。
这些字段通常位于业务响应节点,而不是 JSON 顶层:
response_data = json.loads(response_text)
result = response_data.get("alipay_system_oauth_token_response", {})
open_id = result.get("open_id")
user_id = result.get("user_id")
open_id 和 user_id 的返回规则可能受到接口版本、应用类型、授权范围及支付宝当前开放平台规则的影响。不能根据历史示例认定接口一定会返回某个字段,应以实际响应和当前官方文档为准。
注意事项
- 不要在日志、异常页面或代码仓库中输出真实的应用私钥。
RSA2通常对应 SHA-256 签名,相关配置必须与应用和 SDK 的使用方式一致。- 沙箱与生产环境的网关、应用 ID 和密钥必须配套使用,不能交叉配置。
- 获得用户授权标识并不表示分账能力已经开通。分账还需要核对产品签约、参与方绑定、分账关系和相关接口权限。
- 先处理本地签名异常,再检查授权范围和返回字段。当前异常发生在
__prepare_request_params阶段,更换auth_code或重复请求无法解决这个问题。
备注:内容仅供参考。
