PAYATHON 2026

正式环境支付成功后收不到异步回调通知

支付小周

结论

支付成功不等于异步通知一定能送达。仅凭订单号无法判断平台是否已经发送通知。根据现有信息,建议先排查以下回调地址:

http://api-live-test-mi.changyinshow.com/biznotify/payment_notify/alipay

通知失败的常见原因有:公网无法访问、DNS 解析异常、防火墙或 WAF 拦截、接口重定向、处理超时、返回内容不符合支付平台要求,以及通知到达后验签或业务处理失败。

先在支付平台后台查询订单 0732462022121514454348644469003 的通知记录,再结合网关和应用日志,确认通知具体停在哪一层。

排查步骤

1. 查询支付平台的通知记录

在支付平台后台通过订单号查询:

0732462022121514454348644469003

需要确认:

  • 订单是否属于当前正式环境商户。
  • 支付状态是否确实为成功。
  • 创建订单时实际提交的 notify_url 是否与预期一致。
  • 平台是否发送过异步通知。
  • 每次通知的时间、响应状态码、响应内容和失败原因。
  • 通知是否仍在自动重试周期内。

如果后台没有发送记录,应检查下单请求中的通知地址、商户配置和订单环境。如果后台显示已经发送,再继续检查服务端访问链路。

2. 验证回调地址能否从公网访问

异步通知由支付平台服务器主动发起,因此回调地址必须能从公网访问。只在公司内网或服务器本机测试无法说明问题。

可以从外部网络执行:

curl -i -X POST \
  'http://api-live-test-mi.changyinshow.com/biznotify/payment_notify/alipay' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data 'test_notify=1'

检查以下内容:

  • 域名能否正常解析。
  • 80 端口是否开放。
  • 请求是否进入负载均衡、网关或 Nginx。
  • 是否返回 301、302、403、404、405 或 5xx。
  • 响应时间是否过长。
  • 接口是否仅允许内网 IP 访问。
  • WAF、安全组或防火墙是否拦截了支付平台的请求。

测试请求没有合法签名,业务接口返回验签失败属于正常现象,但网关和应用至少应当收到请求。

3. 检查正式环境配置

域名中含有 live-test。需要确认这只是测试命名,并且该域名确实指向正式环境可访问的公网服务。检查时应以正式环境实际加载的配置为准,不能只看配置文件内容。

例如:

payment:
  alipay:
    notify-url: http://api-live-test-mi.changyinshow.com/biznotify/payment_notify/alipay

还要确认:

  • 正式环境是否覆盖了这项配置。
  • 下单代码是否通过参数传入了其他 notify_url。
  • 修改配置后,应用是否已经重新加载。
  • 多实例部署时,各实例的配置是否一致。
  • 回调域名是否指向正确的正式环境入口。

最终应以创建订单时实际发送给支付平台的请求参数为准。

4. 检查网关和应用日志

按照通知发生时间依次查询:

  1. DNS 和负载均衡日志;
  2. CDN、WAF 或 API 网关日志;
  3. Nginx access/error 日志;
  4. 应用访问日志;
  5. 回调业务日志和异常日志。

可以按接口路径检索 Nginx 日志:

grep '/biznotify/payment_notify/alipay' /var/log/nginx/access.log
grep '/biznotify/payment_notify/alipay' /var/log/nginx/error.log

如果 Nginx 中没有记录,应检查公网链路、域名解析和安全策略。如果 Nginx 有记录,但应用没有记录,需要检查反向代理路由。如果应用已经收到请求,则继续检查参数解析、验签、异常信息和最终响应内容。

5. 检查接口是否支持平台使用的请求方式

支付回调通常使用 POST,参数一般以 application/x-www-form-urlencoded 格式提交。接口不能只接受 JSON,也不能依赖登录状态或 CSRF Token。

以 Spring Boot 为例,可以先记录必要的通知信息,再进行验签和业务处理:

@PostMapping(
    value = "/biznotify/payment_notify/alipay",
    consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE
)
public ResponseEntity<String> paymentNotify(
        @RequestParam Map<String, String> params) {

    String orderId = params.get("out_trade_no");
    String tradeStatus = params.get("trade_status");

    log.info("payment notify received, orderId={}, tradeStatus={}",
            orderId, tradeStatus);

    boolean signatureValid = verifySignature(params);
    if (!signatureValid) {
        log.warn("payment notify signature verification failed, orderId={}",
                orderId);
        return ResponseEntity.ok("failure");
    }

    try {
        paymentService.handleNotification(params);
        return ResponseEntity.ok("success");
    } catch (Exception e) {
        log.error("payment notify processing failed, orderId={}", orderId, e);
        return ResponseEntity.ok("failure");
    }
}

verifySignature 必须使用支付平台正式环境对应的公钥、字符集和签名算法实现。排查问题时也不能跳过验签。

示例中的 "success" 和 "failure" 是支付宝类回调的常见返回形式。实际返回内容必须遵循当前接入产品的官方接口文档,不能直接照搬示例。

业务处理要求

验签前保留原始参数

验签时应使用支付平台发送的完整参数。不要提前修改参数编码、删除空值或重新拼装字段,否则可能导致验签失败。

日志中也不能输出完整的敏感信息。订单号和交易号可以按排查需要记录,密钥、授权信息及用户隐私字段不得写入日志。

核对订单归属和金额

验签成功后,还需检查:

  • 商户订单号是否存在。
  • 商户号、应用 ID 是否与当前系统一致。
  • 实付金额和币种是否与本地订单一致。
  • 通知中的交易状态是否属于可处理状态。
  • 订单是否已经关闭、退款或完成。

不能仅凭回调中的“支付成功”字段直接更新订单。

保证幂等

支付平台可能多次发送同一笔通知,因此回调处理必须支持幂等。可以通过订单状态判断和数据库条件更新,避免重复入账:

UPDATE payment_order
SET status = 'PAID',
    paid_at = CURRENT_TIMESTAMP
WHERE order_id = ?
  AND status = 'UNPAID';

更新后检查受影响行数:

  • 受影响行数为 1:本次状态更新成功。
  • 受影响行数为 0:查询订单当前状态,判断是否已经处理,不能直接认定为失败。

发放权益、增加余额等操作也要设置唯一约束或幂等记录,不能依赖接口不会被重复调用。

尽快返回确认结果

回调接口不适合执行耗时较长的同步任务。建议按以下顺序处理:

  1. 接收参数;
  2. 完成验签和订单校验;
  3. 以幂等方式更新支付状态;
  4. 提交事务;
  5. 返回平台要求的成功响应;
  6. 通过消息队列或任务表处理后续业务。

即使业务已经处理成功,如果接口超时,或者返回了 HTML 页面、错误的响应内容,支付平台通常仍会将本次通知判定为失败并继续重试。

常见配置问题

还需检查这些容易遗漏的问题:

  • 回调接口被登录鉴权拦截。
  • Spring Security 或其他安全组件拦截匿名请求。
  • CSRF 校验导致 403。
  • Nginx 只允许 GET,而支付平台发送的是 POST。
  • HTTP 自动跳转到 HTTPS,但支付平台没有正确跟随跳转。
  • HTTPS 证书过期、证书链不完整或域名不匹配。
  • 网关改写路径后,请求没有转发到正确的服务。
  • 应用只解析 JSON,没有解析表单参数。
  • 正式环境使用了沙箱公钥或错误的商户配置。
  • 接口抛出异常后,被统一异常处理器转换为 500。
  • 业务已经处理成功,但最终响应内容不是平台要求的确认字符串。

建议的定位顺序

可以按以下顺序排查:

支付平台通知记录
    → 公网域名与端口
    → WAF/网关/Nginx 日志
    → 应用入口日志
    → 参数解析与验签
    → 订单校验和事务
    → HTTP 状态码及响应内容

如果平台后台显示已经向该地址发送通知,需要取得通知时间、平台记录的 HTTP 状态码和响应内容,并在同一时间范围内检索 Nginx 与应用日志。通过这些信息,可以判断问题发生在公网链路、网关转发还是应用处理阶段。

备注:内容仅供参考。