支付宝异步通知必须验签且响应纯文本“success”,否则持续重发;.NET Core需手动解析原始POST体、加载PEM公钥、调用VerifyNotify并移除sign/sign_type字段,验签后须主动查单确认支付状态。

支付宝异步通知必须验签,否则无法确认是否来自支付宝,且不返回 SUCCESS 会导致持续重发(25 小时内最多 8 次),实际线上常因验签失败或响应格式不对被反复轰炸。
为什么 NotifyVerify 在 .NET Core 3.1+ 中不能直接用
官方老版 SDK(如 Aop.Api.Util.AlipaySignature)依赖 System.Web,而 .NET Core 已移除该命名空间;AlipaySignature.RSACheckV1 等方法在新版 Alipay.EasySDK 中已弃用或行为不一致。直接照搬 Java/JSP 示例代码会报错或验签恒失败。
- 旧 SDK 的
AlipaySignature.RSACheckV1要求传入原始Request.Form字符串,但IFormCollection默认已 URL 解码,再拼接会导致签名不一致 -
RSACheckV1内部对参数排序、拼接逻辑与支付宝服务端不完全对齐(比如空值处理、时间字段中的空格) - 支付宝当前生产环境主流用
sign_type=RSA2,但部分老项目仍配成RSA,密钥长度不匹配会导致InvalidKeyException
Alipay.EasySDK 的 AlipayClient.VerifyNotify 怎么安全调用
新版 EasySDK 提供了封装好的验签入口,但必须满足三个前提:原始未解码参数、正确加载公钥、显式指定 sign_type。它不自动读取 Request.Form,需手动构造 IDictionary<string, string> 并保留原始编码。
- 用
await new StreamReader(Request.Body).ReadToEndAsync()读原始 POST body,再用HttpUtility.ParseQueryString解析(保留原始编码) - 从配置中加载支付宝公钥(不是应用私钥!),格式必须是 PEM,且以
-----BEGIN PUBLIC KEY-----开头 - 调用前确保
sign和sign_type字段已从字典中移除,否则VerifyNotify会内部重复移除导致 KeyError - 示例关键片段:
var body = await new StreamReader(Request.Body).ReadToEndAsync(); var form = HttpUtility.ParseQueryString(body); var paramDict = form.AllKeys.ToDictionary(k => k, k => form[k]); paramDict.Remove("sign"); paramDict.Remove("sign_type"); var result = client.VerifyNotify(paramDict, form["sign"], form["sign_type"]);
响应体必须严格输出 success 且无额外字符
支付宝只认纯文本 success(7 个 ASCII 字符,小写,无空格、无换行、无 HTML 标签、无 BOM)。任何日志、异常堆栈、JSON 包裹、甚至 WriteLine 多打一个回车都会让重试机制触发。
- 不要用
return Ok("success")—— ASP.NET Core 默认加 JSON 包裹和双引号 - 不要用
Response.WriteAsync("success")后再执行其他逻辑 —— 必须在写完后立即return,避免后续中间件追加内容 - 推荐写法:
Response.ContentType = "text/plain;charset=utf-8"; await Response.WriteAsync("success"); return; - 日志必须在
WriteAsync之前完成,否则可能干扰响应流
验签通过后仍要查单,别信 trade_status 字段
支付宝异步通知的 trade_status 是“快照值”,不是最终状态。网络抖动、支付超时、资金冻结等场景下,通知到达时状态可能是 TRADE_SUCCESS,但几秒后被冲正为 TRADE_CLOSED。
- 必须调用
alipay.trade.query接口,用out_trade_no或trade_no主动查单 - 仅当查询返回
trade_status == "TRADE_SUCCESS"且pay_time非空时,才更新订单状态并发货 - 查单失败(如超时、限流)需记录告警,但不能阻塞响应 —— 支付宝只要
success,查单应异步补救
最易被忽略的是:验签成功 ≠ 支付成功,success 响应 ≠ 订单完结。所有业务动作必须放在查单确认之后,否则会引发资损或重复发货。重试窗口长达 25 小时,意味着同一笔订单可能收到多条通知,out_trade_no 去重和幂等设计必须前置。



















