PAYATHON 2026

支付宝异步通知验证成功但订单查询不到付款状态

支付老李

结论

异步通知验签成功,只能说明通知参数的签名通过了支付宝公钥验证,不能据此认定订单已经付款。

确认支付结果时,还需要检查以下内容:

  • trade_status 是否为 TRADE_SUCCESS 或 TRADE_FINISHED
  • app_id、seller_id 是否属于当前商户
  • out_trade_no、total_amount 是否与本地订单一致
  • 查询所用的应用、网关环境和订单号类型是否正确
  • alipay.trade.query 返回的 code、sub_code 和 sub_msg 是否表明查询成功

如果验签成功,但在支付宝侧查不到交易,通常是因为正式环境与沙箱环境混用、商户账号或应用不一致,或者混淆了支付宝交易号 trade_no 和商户订单号 out_trade_no。

异步通知参数是怎样生成的

支付状态变化后,支付宝会根据交易记录生成通知参数,再通过 HTTP POST 请求发送到支付请求中指定的 notify_url。

这些参数通常分为三类:

  • 商户在请求中提供并由支付宝回传的信息,例如 out_trade_no、total_amount、subject
  • 支付宝交易系统生成的信息,例如 trade_no、trade_status、gmt_payment
  • 用于识别和验证通知的信息,例如 app_id、seller_id、charset、sign_type、sign

支付宝按照签名规则对待签名参数排序、拼接,再使用支付宝私钥生成 sign。商户收到通知后,需要使用支付宝公钥验签。

不同支付产品和接口包含的参数可能不同,应以对应支付接口的“异步通知参数”说明为准。可以在支付宝开放平台文档中心搜索:

  • 异步通知
  • 异步通知验签
  • alipay.trade.query
  • 电脑网站支付 异步通知
  • 手机网站支付 异步通知
  • 当面付 异步通知

开放平台的文档路径可能调整,不建议长期依赖非官方转载页面。

为什么验签成功却查不到付款状态

1. 只验证了签名,没有验证交易状态

验签成功不代表 trade_status 一定是支付成功。支付宝标准交易中常见的状态包括:

状态含义
WAIT_BUYER_PAY等待买家付款
TRADE_CLOSED交易关闭
TRADE_SUCCESS交易支付成功
TRADE_FINISHED交易结束,不可退款

业务系统通常只在收到 TRADE_SUCCESS 或 TRADE_FINISHED 时,才把订单更新为已支付。

2. 查询时用错了订单号

支付宝交易中有两个容易混淆的编号:

  • out_trade_no:商户创建订单时生成的商户订单号
  • trade_no:支付宝创建交易后生成的支付宝交易号

调用 alipay.trade.query 时,可以使用其中任意一种编号查询,但参数名和值必须对应。不能把 trade_no 的值传给 out_trade_no。

3. 沙箱和正式环境混用

沙箱交易只能使用沙箱网关和沙箱应用查询,正式交易必须通过正式网关查询。

常见的配置错误有:

  • 交易发生在沙箱,查询请求却发到了正式网关
  • 交易发生在正式环境,查询时使用了沙箱应用
  • 回调服务混用了两套环境的配置
  • 查询时加载了另一个环境的 app_id、私钥或支付宝公钥

4. 查询使用了不同的应用或商户身份

异步通知中的 app_id、seller_id 应当与当前订单所属的应用和商户一致。

如果平台维护了多个支付宝应用,或者采用服务商代商户调用模式,还需要确认查询请求使用了正确的商户授权关系和授权令牌。否则,即使订单号正确,也可能无法查询到当前调用身份下的交易。

5. 门户后台的商户账号或筛选条件不正确

支付宝开放平台应用、商家平台账号和实际收款账号并不是同一个概念。应根据通知中的 seller_id 确认实际收款方,同时检查后台设置的时间范围、交易状态和订单号类型。

6. 忽略了查询接口的业务错误

接口正常返回不代表订单查询成功。以下信息都需要记录并检查:

  • code
  • msg
  • sub_code
  • sub_msg
  • 原始响应报文

订单不存在、参数错误、调用身份不匹配等问题,通常可以从 sub_code 和 sub_msg 中找到线索。不能只看 HTTP 状态码或 SDK 的 isSuccess() 来判断付款状态。

7. 通知与查询之间存在短暂时序差异

正常情况下,收到支付成功通知后应当能够查询到交易。但受网络重试、系统延迟或异常链路影响,短时间内也可能查询失败。

可以按有限次数延迟重试,例如分别间隔 1 秒、3 秒和 10 秒查询。如果持续查不到,应保存完整的通知和查询响应,再通过支付宝开放平台的问题排查渠道核实,不能直接把订单改为已支付。

推荐的处理步骤

  1. 保存收到的完整通知参数,并做好脱敏,不要记录私钥等敏感配置。
  2. 使用当前应用对应的支付宝公钥验证 sign。
  3. 核对通知中的 app_id 和 seller_id。
  4. 根据 out_trade_no 查询本地订单。
  5. 使用精确十进制方式比较 total_amount,不要直接用 double 比较金额。
  6. 确认 trade_status 是否为支付成功状态。
  7. 使用相同环境和相同应用调用 alipay.trade.query。
  8. 记录查询接口返回的完整业务状态码。
  9. 将支付处理设计成幂等操作,防止重复通知引起重复发货或重复记账。
  10. 本地事务提交成功后,向支付宝返回纯文本 success;如果处理失败,不要提前返回成功。

Java 验签与业务校验示例

下面的示例用于说明处理结构,字段值和 SDK 初始化方式需要按照实际项目配置。

import com.alipay.api.internal.util.AlipaySignature;

import java.math.BigDecimal;
import java.util.Map;

public class AlipayNotifyHandler {

    public String handle(
            Map<String, String> params,
            String alipayPublicKey,
            String expectedAppId,
            String expectedSellerId,
            LocalOrder order
    ) throws Exception {

        boolean verified = AlipaySignature.rsaCheckV1(
                params,
                alipayPublicKey,
                "UTF-8",
                "RSA2"
        );

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

        if (!expectedAppId.equals(params.get("app_id"))) {
            return "failure";
        }

        if (!expectedSellerId.equals(params.get("seller_id"))) {
            return "failure";
        }

        if (!order.getOutTradeNo().equals(params.get("out_trade_no"))) {
            return "failure";
        }

        BigDecimal notifiedAmount =
                new BigDecimal(params.get("total_amount"));

        if (order.getAmount().compareTo(notifiedAmount) != 0) {
            return "failure";
        }

        String tradeStatus = params.get("trade_status");
        boolean paid = "TRADE_SUCCESS".equals(tradeStatus)
                || "TRADE_FINISHED".equals(tradeStatus);

        if (!paid) {
            return "success";
        }

        // 必须实现幂等:订单已经支付时不能重复记账或发货。
        orderService.markPaidIfUnpaid(
                order.getOutTradeNo(),
                params.get("trade_no"),
                notifiedAmount
        );

        return "success";
    }
}

构造参数集合时,应以 Web 框架实际收到的表单参数为准。多数框架已经完成 URL 解码。在没有确认的情况下不要再次解码,否则可能造成验签失败或字段内容发生变化。

alipay.trade.query 查询示例

import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;

public AlipayTradeQueryResponse queryByOutTradeNo(
        AlipayClient alipayClient,
        String outTradeNo
) throws Exception {

    AlipayTradeQueryModel model = new AlipayTradeQueryModel();
    model.setOutTradeNo(outTradeNo);

    AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
    request.setBizModel(model);

    AlipayTradeQueryResponse response = alipayClient.execute(request);

    if (!response.isSuccess()) {
        System.err.println("code=" + response.getCode());
        System.err.println("msg=" + response.getMsg());
        System.err.println("subCode=" + response.getSubCode());
        System.err.println("subMsg=" + response.getSubMsg());
    }

    return response;
}

如果要使用支付宝交易号查询,应改为:

AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setTradeNo(tradeNo);

不要把不确定的值同时填入 trade_no 和 out_trade_no。应先检查异步通知的原始参数,确认编号类型后再查询。

注意事项

  • 验签时必须使用支付宝公钥,不能使用应用公钥或商户应用私钥。
  • 除了校验 sign,还必须核对 app_id、seller_id、订单号和金额。
  • passback_params 等字段可能经过 URL 编码,应按对应产品文档处理。
  • 金额应使用 BigDecimal、整数分或其他精确方式保存和比较。
  • 异步通知可能重复发送,订单更新、积分、库存和发货操作都需要具备幂等性。
  • 不要只根据前端同步跳转结果判断支付成功。
  • 查询接口只是确认支付结果的手段之一。如果查询结果长期与通知冲突,应暂停履约、保存原始报文并进行排查。
  • 日志应保留 out_trade_no、trade_no、app_id、seller_id、trade_status、查询返回码和请求环境,但不能输出私钥及其他敏感信息。

备注:内容仅供参考。