PAYATHON 2026

支付后调用 payment status API 返回 HTTP 400

支付小周

结论

通常不需要在 Startup 或 appsettings.json 中配置额外的 permission。

CallbackUrl 不是主动调用 payment status API 的页面地址,而是支付流程结束后,支付平台向商户服务器发送回调请求的公网端点。该请求通常使用 POST,并携带用于查询支付结果的 token。出现 HTTP 400,一般是因为回调端点只接受 GET、参数绑定不正确,或者 ASP.NET Core 的防伪令牌校验拒绝了外部请求。

处理流程如下:

  1. 初始化 Checkout Form。
  2. 用户完成支付。
  3. 支付平台向 CallbackUrl 发送 POST 请求。
  4. 商户从表单中取得 token。
  5. 使用该 token 调用 Checkout Form Retrieve API,查询并校验最终支付状态。

配置回调端点

回调地址必须能从公网访问,并且接受 POST 请求。在 ASP.NET Core 2.1 中,可以直接从表单读取 token:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Iyzipay;
using Iyzipay.Model;
using Iyzipay.Request;

public class PaymentController : Controller
{
    private readonly Options _options;

    public PaymentController(Options options)
    {
        _options = options;
    }

    [HttpPost]
    [IgnoreAntiforgeryToken]
    public async Task<IActionResult> Callback()
    {
        string token = Request.Form["token"];

        if (string.IsNullOrWhiteSpace(token))
        {
            return BadRequest("Missing payment token.");
        }

        var request = new RetrieveCheckoutFormRequest
        {
            Token = token
        };

        CheckoutForm checkoutForm =
            await CheckoutForm.RetrieveAsync(request, _options);

        if (!string.Equals(
                checkoutForm.Status,
                "success",
                System.StringComparison.OrdinalIgnoreCase))
        {
            return BadRequest(checkoutForm.ErrorMessage);
        }

        // 还应检查 PaymentStatus、Price、PaidPrice、Currency、
        // BasketId、PaymentId 等字段是否符合当前订单。
        return Ok();
    }
}

也可以使用模型绑定:

public class PaymentCallbackRequest
{
    public string Token { get; set; }
}

[HttpPost]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> Callback(
    [FromForm] PaymentCallbackRequest callback)
{
    if (string.IsNullOrWhiteSpace(callback.Token))
    {
        return BadRequest("Missing payment token.");
    }

    var request = new RetrieveCheckoutFormRequest
    {
        Token = callback.Token
    };

    CheckoutForm result =
        await CheckoutForm.RetrieveAsync(request, _options);

    if (!string.Equals(
            result.Status,
            "success",
            System.StringComparison.OrdinalIgnoreCase))
    {
        return BadRequest(result.ErrorMessage);
    }

    return Ok();
}

SDK 版本不同,异步方法的签名可能略有差异。如果当前版本没有 RetrieveAsync,可以改用该版本提供的同步 Retrieve 方法。具体类名和方法签名以项目实际安装的 SDK 为准。

检查路由是否匹配

如果回调地址是:

request.CallbackUrl = "https://www.merchant.com/callback";

应用中必须存在与 /callback 匹配的 POST 路由,例如:

[Route("callback")]
public class PaymentCallbackController : Controller
{
    [HttpPost]
    [IgnoreAntiforgeryToken]
    public IActionResult Index([FromForm] string token)
    {
        if (string.IsNullOrWhiteSpace(token))
        {
            return BadRequest();
        }

        // 使用 token 查询支付结果
        return Ok();
    }
}

如果使用传统 MVC 路由,控制器和 Action 生成的实际路径可能是 /Payment/Callback。这时应将初始化请求改为:

request.CallbackUrl = "https://www.merchant.com/Payment/Callback";

控制器方法不会自动对应 /callback。请核对应用的路由配置和服务器访问日志,确认实际路径。

防伪令牌导致的 HTTP 400

支付平台无法携带商户网站生成的 ASP.NET Core antiforgery token。如果项目全局启用了:

services.AddMvc(options =>
{
    options.Filters.Add(
        new Microsoft.AspNetCore.Mvc.AutoValidateAntiforgeryTokenAttribute());
});

框架可能在请求进入 Action 前就直接返回 HTTP 400。此时,只需在支付回调 Action 上添加:

[IgnoreAntiforgeryToken]

不要因为一个支付回调就关闭整个站点的防伪保护。虽然回调端点不校验 antiforgery token,但仍须使用支付平台返回的 token 查询服务端支付结果,不能信任浏览器提交的金额或”支付成功”参数。

初始化和查询环境必须一致

初始化 Checkout Form 和查询支付结果时,必须使用同一套 API 凭据和同一环境:

var options = new Options
{
    ApiKey = configuration["Iyzipay:ApiKey"],
    SecretKey = configuration["Iyzipay:SecretKey"],
    BaseUrl = configuration["Iyzipay:BaseUrl"]
};

appsettings.json 可以保存这些配置,但它不是 permission 设置:

{
  "Iyzipay": {
    "ApiKey": "your-api-key",
    "SecretKey": "your-secret-key",
    "BaseUrl": "your-api-base-url"
  }
}

生产环境中不要将真实密钥提交到源代码仓库。可以使用环境变量、Secret Manager 或专用密钥管理服务。

还需要检查的项目

  • CallbackUrl 必须是支付平台能够访问的公网地址,不能使用 localhost 或内网地址。
  • HTTPS 证书必须有效,域名应与证书匹配。
  • 回调端点必须允许 POST,不能只声明 [HttpGet]。
  • token 通常来自 application/x-www-form-urlencoded 表单,应使用 [FromForm],不能只用 [FromBody] 解析 JSON。
  • 不要使用初始化请求中的 token、其他订单的 token 或已经失效的 token 查询结果。
  • 初始化和查询必须使用一致的 sandbox/production 环境及凭据。
  • 记录响应中的 Status、ErrorCode、ErrorMessage 和 ErrorGroup。仅凭 HTTP 400 无法确定具体原因。
  • 只有服务端确认支付状态、订单标识、金额和币种都符合预期后,才能将订单标记为已支付。
  • 回调可能重复到达,因此订单更新逻辑需要具备幂等性。

如果 HTTP 400 出现在回调进入应用之前,先检查路由、请求方法和 antiforgery 配置。如果回调 Action 已经取得 token,但 Retrieve API 返回 400,则应检查 token、API 凭据、请求签名,以及 sandbox/production 环境是否一致。

备注:内容仅供参考。