PAYATHON 2026

支付宝免密支付成功或失败后是否有消息回调?

支付阿杰

结论

免密支付结束后,支付宝不会分别发送”成功回调”和”失败回调”。

支付宝免密支付通常仍由具体的支付接口完成,例如 alipay.trade.pay。不同结果的处理方式如下:

  • 支付成功:请求中正确配置 notify_url 后,支付宝会向该地址发送支付结果异步通知,常见的交易状态是 TRADE_SUCCESS。
  • 支付失败:失败原因通常会直接出现在支付接口的同步响应中,包括 code、sub_code、sub_msg 等字段,一般不会另行发送”支付失败”异步通知。
  • 结果不明确:发生超时、网络中断或系统仍在处理时,不能直接判定支付失败,需要调用 alipay.trade.query 查询交易状态。

商户系统不能依赖”失败回调”更新订单。完整的状态处理需要结合接口同步响应、异步通知和主动查询。

为什么失败通常没有异步通知

异步通知主要用于告知商户,交易状态已经发生确定的变化。许多支付失败在接口调用阶段就能确认,比如余额不足、签约关系无效、风控拦截或参数错误,支付宝会通过同步响应返回这些信息。

接口调用超时不等于支付失败。商户没有收到同步响应时,支付宝可能已经受理请求,甚至已经完成扣款。如果此时直接将订单标记为失败,支付宝交易和商户订单的状态可能出现不一致。

推荐的处理流程

1. 先处理同步响应

调用免密支付接口后,可以按响应字段分类处理:

  • code == 10000:接口调用成功,但最终交易结果仍应以异步通知或主动查询为准。
  • 返回明确的业务失败码:记录 sub_code 和 sub_msg,将本次支付尝试标记为失败。
  • 出现超时、网络异常或状态不明确:将订单标记为”处理中”,再调用查询接口确认结果。

不能只根据 HTTP 状态码判断支付是否成功。

2. 接收支付成功异步通知

发起支付请求时,需要配置可从公网访问的 notify_url。收到通知后,应依次完成以下检查:

  1. 验证支付宝签名;
  2. 核对 app_id;
  3. 核对 out_trade_no;
  4. 核对 total_amount;
  5. 确认通知中的交易状态;
  6. 幂等更新订单;
  7. 处理成功后返回纯文本 success。

支付成功时,常见的 trade_status 包括:

  • TRADE_SUCCESS:交易支付成功,可以将订单更新为已支付。
  • TRADE_FINISHED:交易已经结束且不可退款。当前产品和交易流程是否会收到该状态,应以所接入产品的官方接口文档为准。

WAIT_BUYER_PAY、TRADE_CLOSED 等状态可能出现在交易查询结果中,但这并不表示支付宝一定会针对每种状态发送异步通知。

3. 对不明确结果主动查询

如果支付接口超时、连接中断或返回处理中状态,应使用原支付请求的 out_trade_no 调用 alipay.trade.query。

示意请求如下:

{
  "out_trade_no": "ORDER_202609130001"
}

查询后可以根据 trade_status 处理:

TRADE_SUCCESS   -> 支付成功
TRADE_FINISHED  -> 交易完成
WAIT_BUYER_PAY  -> 尚未支付或仍在处理中
TRADE_CLOSED    -> 交易关闭

如果首次查询仍得不到确定结果,可以按业务需要进行有限次数的延迟重试,并设置最终关单或人工核对机制。

异步通知处理示例

下面是一个简化的 Java 风格示例。具体的 SDK 类名和初始化方式,应以项目所用的支付宝 Java SDK 版本为准。

@PostMapping("/alipay/notify")
public String handleAlipayNotify(HttpServletRequest request) throws Exception {
    Map<String, String> params = new HashMap<>();

    for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
        params.put(entry.getKey(), String.join(",", entry.getValue()));
    }

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

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

    String appId = params.get("app_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");

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

    if (!EXPECTED_APP_ID.equals(appId)) {
        return "failure";
    }

    if (!order.getTotalAmount().toPlainString().equals(totalAmount)) {
        return "failure";
    }

    if ("TRADE_SUCCESS".equals(tradeStatus)
            || "TRADE_FINISHED".equals(tradeStatus)) {
        // updatePaid 必须具备幂等性,避免重复通知导致重复入账。
        orderService.updatePaid(outTradeNo, tradeNo);
    }

    return "success";
}

同步调用可以按以下逻辑处理:

try {
    AlipayTradePayResponse response = alipayClient.execute(request);

    if ("10000".equals(response.getCode())) {
        // 不要只依赖同步响应完成最终入账。
        // 可暂记为处理中,等待异步通知或主动查询。
        paymentService.markProcessing(outTradeNo);
    } else {
        paymentService.markFailed(
            outTradeNo,
            response.getSubCode(),
            response.getSubMsg()
        );
    }
} catch (Exception e) {
    // 网络异常不代表支付失败。
    paymentService.markUnknown(outTradeNo);
    paymentService.scheduleQuery(outTradeNo);
}

注意事项

  • “免密”指的是用户授权和扣款方式,不表示支付结果一定有成功、失败两类异步回调。
  • 异步通知可能重复发送,因此订单更新、记账和发货等操作必须保证幂等。
  • 验签成功只能证明通知通过了签名校验,仍需核对商户订单号、金额和应用身份,确认通知属于当前订单。
  • 回调接口处理成功后应返回纯文本 success,否则支付宝可能按规则重试通知。
  • 网络超时、SDK 异常或没有收到通知,都不能直接视为支付失败。
  • 不能只凭前端结果、同步响应或通知中的某一个字段发货。发货前应完成签名和业务字段校验。
  • 如果使用的不是 alipay.trade.pay,而是周期扣款、代扣或其他免密产品,通知名称和状态字段可能有所不同,应以对应产品的接口文档和签约能力为准。

备注:内容仅供参考。