PAYATHON 2026

外部站点调用的 POST action 应返回什么?

支付小周

明确结论

这个 POST action 应该返回明确的 HTTP 成功响应,不需要返回 View。

在经典 ASP.NET MVC 项目中,可以返回正文为空的 200 OK:

[HttpPost]
public ActionResult Notify()
{
    // 验证并处理 IPN 数据

    return new HttpStatusCodeResult(HttpStatusCode.OK);
}

也可以返回 EmptyResult。正常情况下,它会生成一个没有响应正文的成功响应:

[HttpPost]
public ActionResult Notify()
{
    // 验证并处理 IPN 数据

    return new EmptyResult();
}

ASP.NET Core 通常这样写:

[HttpPost]
public IActionResult Notify()
{
    // 验证并处理 IPN 数据

    return Ok();
}

Ok() 返回 HTTP 200 OK,响应正文可以为空。

为什么要显式返回状态码

支付服务调用 IPN 地址时,通常不需要 HTML 页面,只需要知道服务器是否成功接收并处理了通知。HTTP 状态码正是双方确认处理结果的标准方式。

通常可以这样理解:

  • 2xx:通知已成功接收或处理。
  • 4xx:请求本身有问题,例如签名错误、参数无效,或者无法验证通知来源。
  • 5xx:服务器暂时无法完成处理,支付服务可能会稍后重试。

哪些状态码会触发重试,响应正文是否必须包含特定内容,要看支付服务的 IPN 协议。请以该支付服务的官方文档为准,不能假定所有服务都只要求 200 OK。

推荐的处理方式

action 可以按以下顺序处理请求:

  1. 读取 POST 数据。
  2. 验证签名、来源和必要字段。
  3. 检查这条通知是否已经处理。
  4. 更新订单或支付数据。
  5. 数据提交成功后返回 200 OK。
  6. 验证失败或处理异常时,返回适当的非 2xx 状态码。

经典 ASP.NET MVC 示例:

using System;
using System.Net;
using System.Web.Mvc;

public class PaymentController : Controller
{
    [HttpPost]
    public ActionResult Notify(PaymentNotification notification)
    {
        if (notification == null)
        {
            return new HttpStatusCodeResult(
                HttpStatusCode.BadRequest,
                "Invalid notification"
            );
        }

        if (!VerifyNotification(notification))
        {
            return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
        }

        try
        {
            ProcessNotification(notification);
            return new HttpStatusCodeResult(HttpStatusCode.OK);
        }
        catch (Exception)
        {
            // 在这里记录异常
            return new HttpStatusCodeResult(
                HttpStatusCode.InternalServerError
            );
        }
    }

    private bool VerifyNotification(PaymentNotification notification)
    {
        // 根据支付服务的协议验证签名和通知内容
        return true;
    }

    private void ProcessNotification(PaymentNotification notification)
    {
        // 更新支付状态
    }
}

如果支付服务的协议要求返回特定文本,可以使用 ContentResult:

[HttpPost]
public ActionResult Notify()
{
    // 处理通知

    return Content("OK");
}

此时响应通常为 200 OK,正文是 OK。只有支付服务明确要求时,才需要返回这段内容。

不建议使用 void

在某些 ASP.NET MVC 版本和执行路径中,将 action 写成 void 可能最终也会得到空响应,但这种写法没有清楚表达接口的 HTTP 语义,也不方便针对验证失败、服务器异常等情况返回不同结果。

更稳妥的写法是让 action 返回 ActionResult、IActionResult 或相应的结果类型,并明确指定状态码。

注意事项

必须验证通知真实性

IPN 地址是公开接口,任何人都可能向它发送 POST 请求。不能只根据请求中的订单号或“支付成功”字段更新订单。应按照支付服务的规范验证签名、证书、消息摘要或回查结果。

保证幂等性

支付平台可能重复发送同一条通知。处理逻辑应根据通知 ID、交易 ID 或订单状态,避免重复扣减库存、重复记账和重复发货。

成功处理后再返回 200

如果数据库更新失败,接口却仍然返回 200 OK,支付服务可能会认为通知已经处理完毕并停止重试。只有在关键数据成功提交后,才能返回成功状态。

不要在通知接口中返回页面或重定向

IPN 是服务器之间的回调,不是浏览器发起的页面请求。通常不需要返回 View,也不应重定向到支付成功页面。用户支付后的页面跳转与服务器端 IPN 应分成两个独立流程处理。

控制处理时间

如果业务处理耗时较长,可以先完成通知校验和必要的数据落库,再把后续工作交给可靠的后台任务。能否在任务入队后立即返回 200 OK,取决于任务队列是否支持可靠持久化,以及支付服务如何定义成功响应。

备注:内容仅供参考。