支付宝公钥和应用公钥填反是验签失败的最常见原因:应用公钥需上传至开放平台并传给alipay实例的app_public_key_string,支付宝公钥则用于验证通知并作为alipay_public_key_string传入。

支付宝公钥和应用公钥填反了,回调验签永远失败
这是最常卡住人的点:把「支付宝公钥」当成「应用公钥」填进后台,或者反过来。验签失败时 alipay.verify() 返回 False,但错误不报具体原因,容易误以为是代码或 URL 问题。
- 「应用公钥」是你生成的(用
openssl生成 RSA2 密钥对后得到的app_private_key.pem对应的公钥),要上传到支付宝开放平台「应用公钥」栏位,并在 SDK 初始化时传给Alipay实例的app_public_key_string - 「支付宝公钥」是支付宝提供的(在开放平台「支付宝公钥」栏位复制),用于验证支付宝发来的通知,要作为
alipay_public_key_string传入 - Django 接收回调时,
request.POST.dict()必须先转成字典再丢给alipay.verify();不能直接传QueryDict,否则验签会静默失败
Django视图里处理支付宝异步通知(notify_url)必须返回success
支付宝收到非 "success" 响应(包括 HTTP 500、空响应、JSON、"OK")就会持续重发通知,最多 25 小时。这不是 bug,是支付宝的设计逻辑。
- 回调视图必须用
@csrf_exempt装饰,支付宝请求不带 CSRF token - 必须校验
sign和sign_type字段存在,且sign_type == "RSA2"(别硬编码,从 POST 取) - 验签通过后,立刻查数据库确认该
out_trade_no是否已处理过——防止重复通知导致重复发货/充值 - 无论业务逻辑是否成功,只要验签通过且不是重复通知,第一行响应就必须是纯文本
"success",且 HTTP 状态码为 200
生成支付链接时 sign_type 和 charset 必须显式指定
Python SDK 默认用 utf-8 编码拼接参数,但如果你的 Django settings.py 里 DEFAULT_CHARSET 不是 utf-8,或前端页面声明了 gbk,支付宝网关可能解析签名失败,返回 "ILLEGAL_SIGN"。
- 调用
alipay.api_alipay_trade_page_pay()时,必须传sign_type="RSA2"和charset="utf-8",不能依赖默认值 -
return_url和notify_url必须是**绝对 URL**(如"https://yourdomain.com/alipay/return/"),不能用reverse()拼相对路径 - 如果用 Nginx 或负载均衡,确保
request.build_absolute_uri()能正确生成 HTTPS 链接(检查SECURE_PROXY_SSL_HEADER配置)
沙箱环境调试时,务必用沙箱版支付宝公钥和 app_id
开发阶段用正式环境密钥+沙箱 app_id,或反过来,都会导致「支付成功但收不到回调」「验签通过但订单状态不更新」这类玄学问题。
- 沙箱
app_id在支付宝开放平台「沙箱环境」页获取,和正式app_id完全不同,不能混用 - 沙箱「支付宝公钥」在沙箱页面单独提供,和正式环境的公钥内容不同,必须替换
- 沙箱买家账号需用沙箱页面里的「买家账号」登录手机支付宝 App 扫码,用自己真实账号扫沙箱链接会提示「该应用未上线」
验签逻辑本身不复杂,真正耗时间的是密钥配对、URL 协议一致性、以及支付宝那边“只告诉你错了,不说哪错了”的交互风格。别在 verify() 返回 False 时反复改代码,先抓包看 POST 数据里有没有 sign、app_id、alipay_public_key_string 是不是你当前用的那一套。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











