PAYATHON 2026

ASP.NET MVC 银行支付 callback 返回时出现 404 error

支付阿杰

结论

最可能的原因是银行回调使用了 HTTP POST,而 PaymentCallback 只接受 HTTP GET。

浏览器地址栏只显示 URL,看不到请求方法。银行提交回调时,即使参数位于查询字符串中,请求方法仍可能是 POST。在地址栏按 Enter 后,浏览器会重新发送 GET 请求,因此可以正常进入 action。

为什么会返回 404

[HttpGet] 是 ASP.NET MVC 选择 action 的条件:

[HttpGet]
[AllowAnonymous]
public ActionResult PaymentCallback(string au, int order_id)
{
    // ...
}

银行发送 POST 请求时,MVC 虽然能找到 PaymentCallback 对应的路由,但请求方法不符合 [HttpGet],所以不会选择这个 action。在某些 IIS 和 MVC 配置下,这会返回 404,不一定是 405。

在地址栏按 Enter 时,浏览器发送的是:

GET /Transaction/PaymentCallback/?au=70158292914&order_id=27

这个请求符合 [HttpGet],因此 action 可以正常执行。

推荐修复方式

先查看支付网关文档,确认回调使用 GET 还是 POST。如果银行规定使用 POST,应将 action 改为 [HttpPost]:

[HttpPost]
[AllowAnonymous]
public ActionResult PaymentCallback(string au, int order_id)
{
    // 验证回调数据
    // 向银行查询或验证交易结果
    // 更新订单状态

    return Content("OK");
}

ASP.NET MVC 的默认模型绑定通常既能读取查询字符串,也能读取表单字段。因此,即使请求方法是 POST,参数仍在 URL 查询字符串中,通常也能正常绑定。

如果支付网关可能发送 GET 或 POST,可以明确允许这两种方法:

[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
[AllowAnonymous]
public ActionResult PaymentCallback(string au, int order_id)
{
    // ...
    return Content("OK");
}

也可以暂时移除 [HttpGet],确认问题是否由请求方法不匹配引起。不过在生产环境中,最好根据支付网关协议明确限制允许的方法。

如何确认实际请求方法

不要只根据浏览器地址栏判断请求方法。可以通过以下方式确认:

  • 查看 IIS 日志中记录的请求方法。
  • 使用浏览器开发者工具的 Network 面板。
  • 查阅支付网关的技术文档或回调示例。
  • 在测试环境中记录 Request.HttpMethod、查询字符串和表单字段。

例如:

string method = Request.HttpMethod;
string auFromQuery = Request.QueryString["au"];
string auFromForm = Request.Form["au"];

如果确认银行发送的确实是 GET,再检查 IIS 请求筛选、URL Rewrite、反向代理、WAF,以及系统是否根据 Referer、User-Agent 或来源 IP 拦截请求。不过,遇到“银行返回时 404,在地址栏按 Enter 后正常”的情况,应先排查 GET 和 POST 是否不一致。

支付回调的安全注意事项

支付回调通常需要使用 [AllowAnonymous],因为银行服务器或从银行页面返回的用户没有本站的登录会话。但允许匿名访问,并不意味着可以直接信任回调参数:

  • 不要只根据 au 或 order_id 将订单标记为已支付。
  • 按照银行协议验证签名、交易编号、金额、商户号和订单号。
  • 必要时主动调用银行的交易验证接口。
  • 确保回调处理具备幂等性,避免重复通知导致重复入账。
  • 不要依赖 Referer 判断请求是否来自银行,因为该请求头可能缺失,也可能被伪造。
  • 跨站银行 POST 通常无法携带本站生成的防伪令牌,因此不要直接为该回调添加 [ValidateAntiForgeryToken]。应改用支付网关提供的签名或验证机制。
  • 按照支付网关协议返回指定的成功响应,否则银行可能重复发送通知。

如果银行同时提供“浏览器返回地址”和“服务器异步通知地址”,应分别实现。浏览器返回地址用于向用户展示支付结果,服务器异步通知地址负责可靠地确认交易并更新订单状态。

备注:内容仅供参考。