PAYATHON 2026

alipay.trade.precreate 支付成功回调如何接收?

支付小周

结论

alipay.trade.precreate 支持支付结果异步通知。创建预支付订单时,需要通过公共请求参数 notify_url 指定回调地址。用户扫码完成支付后,支付宝会向该地址发送 HTTP POST 表单请求。

服务端收到通知后,需要依次完成以下处理:

  1. 使用支付宝公钥验签;
  2. 校验 app_id、seller_id、out_trade_no 和 total_amount;
  3. 根据 trade_status 判断交易状态;
  4. 幂等更新订单;
  5. 返回纯文本 success。

如果创建预支付订单时没有传入有效的 notify_url,或者支付宝无法通过公网访问该地址,服务端就收不到预期的支付通知。

创建订单时设置 notify_url

notify_url 是支付宝接口的公共请求参数,不能放在 biz_content 中。使用 Java SDK 时,可以直接调用 setNotifyUrl:

AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();

request.setNotifyUrl("https://pay.example.com/alipay/notify");

request.setBizContent("""
{
  "out_trade_no": "ORDER_202609090001",
  "total_amount": "0.01",
  "subject": "测试订单"
}
""");

AlipayTradePrecreateResponse response = alipayClient.execute(request);

if (response.isSuccess()) {
    String qrCode = response.getQrCode();
    // 将 qrCode 生成二维码供用户扫码
} else {
    // 记录 sub_code、sub_msg 等错误信息
}

如果自行拼装请求,应把 notify_url 放在公共参数区域。例如:

method=alipay.trade.precreate
app_id=你的应用ID
charset=utf-8
sign_type=RSA2
timestamp=2026-09-09 12:00:00
version=1.0
notify_url=https://pay.example.com/alipay/notify
biz_content={...}
sign=...

notify_url 也参与请求签名,因此不能在签名完成后再添加或修改。

接收异步通知

支付宝发送的是 application/x-www-form-urlencoded 格式的 HTTP POST 请求,不是 JSON。下面是使用 Spring Boot 接收通知的示例:

@PostMapping(
    value = "/alipay/notify",
    produces = "text/plain;charset=UTF-8"
)
public String receiveAlipayNotify(HttpServletRequest request) {
    Map<String, String> params = new HashMap<>();

    request.getParameterMap().forEach((key, values) -> {
        if (values != null && values.length > 0) {
            params.put(key, String.join(",", values));
        }
    });

    try {
        boolean valid = AlipaySignature.rsaCheckV1(
            params,
            ALIPAY_PUBLIC_KEY,
            "UTF-8",
            "RSA2"
        );

        if (!valid) {
            return "failure";
        }

        String appId = params.get("app_id");
        String sellerId = params.get("seller_id");
        String outTradeNo = params.get("out_trade_no");
        String tradeNo = params.get("trade_no");
        String tradeStatus = params.get("trade_status");
        String totalAmount = params.get("total_amount");

        // 以下信息必须与本地订单进行比对
        if (!EXPECTED_APP_ID.equals(appId)) {
            return "failure";
        }

        if (!EXPECTED_SELLER_ID.equals(sellerId)) {
            return "failure";
        }

        Order order = orderService.findByOutTradeNo(outTradeNo);
        if (order == null) {
            return "failure";
        }

        if (new BigDecimal(totalAmount).compareTo(order.getTotalAmount()) != 0) {
            return "failure";
        }

        if ("TRADE_SUCCESS".equals(tradeStatus)
                || "TRADE_FINISHED".equals(tradeStatus)) {
            // 必须幂等处理:重复通知不能重复记账或重复发货
            orderService.markPaidIfUnpaid(
                outTradeNo,
                tradeNo,
                tradeStatus
            );
        }

        return "success";
    } catch (AlipayApiException e) {
        // 记录异常,但不要打印私钥等敏感信息
        return "failure";
    }
}

不同版本的 SDK 在具体类名和方法签名上可能略有差异,但处理方式相同:先取得所有表单参数,再使用支付宝公钥和通知中的签名参数完成验签。

如何理解”触发通知”

“触发通知”是指交易状态发生符合接口规则的变化后,支付宝将交易结果推送到本次请求指定的 notify_url。

支付成功时,主要处理以下状态:

trade_status=TRADE_SUCCESS

部分交易在后续状态变化时,还可能收到以下通知:

trade_status=TRADE_FINISHED

业务系统不能把收到一次 HTTP 请求直接视为可以发货。只有验签和订单信息校验全部通过后,才能根据 trade_status 更新订单。

异步通知可能重复发送,所以订单处理必须具备幂等性。例如,订单只能从”待支付”更新为”已支付”一次。对于后续收到的重复通知,直接返回 success。

始终收不到通知时的排查顺序

1. 确认请求中确实传入了 notify_url

应检查最终发送给支付宝的请求参数,不能只看配置文件。常见问题包括:

  • 只配置了应用网关,没有为当前请求设置 notify_url;
  • 把 notify_url 错放在 biz_content 中;
  • 封装 SDK 时漏掉了 request.setNotifyUrl(...);
  • 完成签名后才追加 notify_url。

应用网关、授权回调地址和支付异步通知地址是三个不同的概念,不能相互替代。

2. 确认地址可从公网访问

回调地址不能使用:

http://localhost/...
http://127.0.0.1/...
http://192.168.x.x/...

应使用稳定的公网域名,例如:

https://pay.example.com/alipay/notify

还需要检查以下项目:

  • DNS 是否可以正常解析;
  • HTTPS 证书是否有效;
  • 防火墙、安全组和反向代理是否允许访问;
  • 路由是否接受 POST 请求;
  • 是否受到强制登录、验证码或鉴权拦截;
  • 是否存在 301、302 跳转;
  • WAF 是否拦截了支付宝请求;
  • 接口能否在较短时间内返回结果。

3. 不要按 JSON 解析

以下写法不适用于支付宝异步通知:

@RequestBody AlipayNotifyBody body

支付宝异步通知通常使用表单参数。服务端应通过 request.getParameterMap() 获取参数,或者直接接收对应的表单字段。

4. 检查服务端响应

处理成功后,响应体应为:

success

不要返回 JSON:

{"code":200,"message":"success"}

也不要返回 HTML 页面、额外空格或经过业务对象包装的内容。如果支付宝没有识别到成功响应,可能会继续重试通知。

5. 检查验签配置

验签必须使用支付宝公钥,不能使用应用公钥或应用私钥。同时确认:

  • sign_type 与实际请求一致,通常是 RSA2;
  • 字符集与请求配置一致,常见值为 UTF-8;
  • 验签参数包含支付宝通知中的全部有效参数;
  • 不要再次对参数值进行 URL 编码或解码;
  • 不要修改金额、时间等参数后再验签。

重要注意事项

异步通知是确认支付结果的主要渠道,但网络通知可能重复发送,也不一定会在固定时间内到达。生产环境通常还需要保留订单查询作为补偿机制,例如对长时间停留在”待支付”状态的订单进行低频查询或对账。这类查询用于容错补偿,无需让用户端持续轮询。

回调处理还应遵循以下原则:

  • 先验签,再处理业务;
  • 使用 BigDecimal 比较金额,不要使用 double;
  • 校验通知中的商户、应用和订单归属;
  • 使用商户订单号 out_trade_no 进行幂等控制;
  • 保存支付宝交易号 trade_no,用于后续退款和对账;
  • 完成可靠的本地状态更新后,再返回 success;
  • 可以把耗时操作交给消息队列,但需要保证订单落库与消息投递的一致性;
  • 不能只根据 trade_status 字段判断付款有效。

调用 alipay.trade.precreate 时,需要为当前请求设置一个支付宝能够从公网访问的 notify_url。回调接口收到通知后,应按”接收表单、验签、校验订单、幂等处理、返回 success”的顺序处理。

备注:内容仅供参考。