外部站点调用的 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 可以按以下顺序处理请求:
- 读取 POST 数据。
- 验证签名、来源和必要字段。
- 检查这条通知是否已经处理。
- 更新订单或支付数据。
- 数据提交成功后返回
200 OK。 - 验证失败或处理异常时,返回适当的非
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,取决于任务队列是否支持可靠持久化,以及支付服务如何定义成功响应。
备注:内容仅供参考。
