PAYATHON 2026

支付回调notify_url使用什么方式?如何获取参数?

支付阿杰

明确结论

notify_url 一般由支付平台服务器发起异步回调。大多数支付接口使用 HTTP POST 请求,但参数编码方式并不统一,常见的有:

  • application/x-www-form-urlencoded:从表单参数中读取
  • application/json:从请求体中读取 JSON
  • text/xml 或 application/xml:从请求体中读取 XML

所以,不能只看回调是否使用 POST,还要根据支付平台的接口文档和请求头 Content-Type 判断参数的读取方式。

回调处理步骤

收到支付通知后,可以按以下顺序处理:

  1. 读取请求方法、请求头和原始请求体。
  2. 根据 Content-Type 解析参数。
  3. 按照平台规定的规则验证签名。
  4. 核对订单号、商户号、支付金额、币种和支付状态。
  5. 使用数据库事务或唯一约束保证幂等,防止重复入账。
  6. 业务处理完成后,返回支付平台要求的成功响应。

如果发生网络超时,或者平台没有收到正确响应,同一条通知可能会重复发送。因此,回调接口必须能够安全地重复执行。

如何获取回调参数

表单格式

如果请求头类似:

Content-Type: application/x-www-form-urlencoded

参数通常在 POST 表单中,例如:

order_no=202609130001&amount=99.00&status=SUCCESS&sign=...

PHP 可以这样读取:

<?php

$params = $_POST;

$orderNo = $params['order_no'] ?? '';
$amount = $params['amount'] ?? '';
$status = $params['status']'] ?? '';
$sign = $params['sign'] ?? '';

Java Spring Boot 可以使用:

@PostMapping("/payment/notify")
public String notify(@RequestParam Map<String, String> params) {
    String orderNo = params.get("order_no");
    String amount = params.get("amount");
    String status = params.get("status");
    String sign = params.get("sign");

    // 验签并处理订单
    return "success";
}

JSON 格式

如果请求头类似:

Content-Type: application/json

需要读取并解析请求体,不能按普通表单参数处理。

Node.js Express 示例:

app.post("/payment/notify", express.json(), async (req, res) => {
  const { order_no, amount, status, sign } = req.body;

  // 验签并处理订单
  res.send("success");
});

Spring Boot 示例:

@PostMapping("/payment/notify")
public String notify(@RequestBody Map<String, Object> body) {
    String orderNo = String.valueOf(body.get("order_no"));
    String status = String.valueOf(body.get("status"));

    // 验签并处理订单
    return "success";
}

XML 格式

部分支付接口会通过 POST 请求发送 XML:

<xml>
    <order_no>202609130001</order_no>
    <status>SUCCESS</status>
    <sign>...</sign>
</xml>

这种情况下,需要先读取原始请求体,再使用经过安全配置的 XML 解析器处理,不能直接从查询字符串或表单参数中取值。

验签时为什么要保留原始请求体

有些支付平台会直接使用原始请求体参与签名验证。如果框架先将 JSON 或 XML 解析成对象,再重新序列化,字段顺序、空格或数字格式可能发生变化,最终导致验签失败。

Express 可以在解析 JSON 时保留原始数据:

app.use(express.json({
  verify: (req, res, buffer) => {
    req.rawBody = buffer;
  }
}));

app.post("/payment/notify", async (req, res) => {
  const signature = req.get("X-Signature");
  const rawBody = req.rawBody;

  const valid = verifySignature(rawBody, signature);

  if (!valid) {
    return res.status(400).send("invalid signature");
  }

  // 验签成功后处理业务
  res.send("success");
});

签名对应的请求头、签名算法和参与验签的数据,都要严格按照支付平台的文档处理。

注意事项

  • 不要只根据客户端跳转结果判断支付是否成功,应以服务端异步通知或主动查询结果为准。
  • 验签通过前,不要更新订单状态。
  • 除了检查 status,还必须核对回调金额与本地订单金额。
  • 回调接口必须支持幂等处理。同一条支付通知即使执行多次,最终结果也要保持一致。
  • 参数名称和大小写要以接口文档为准,不要自行猜测。
  • 成功响应可能是 success、JSON 或 XML,具体格式由支付平台规定。
  • 如果业务处理时间较长,应避免让回调请求长时间阻塞,否则平台可能认为通知失败并发起重试。
  • 调试时可以记录请求方法、Content-Type、请求头和原始请求体,但日志必须脱敏,防止签名、证书信息或用户隐私泄露。
  • notify_url 通常需要允许支付平台从公网访问,并正确配置 HTTPS、域名和防火墙规则。具体网络要求以支付平台文档为准。

备注:内容仅供参考。