PAYATHON 2026

周期扣款接口与alipay.trade.pay如何返回扣款结果?

支付小周

结论

alipay.trade.pay 会同步返回当前支付请求的处理结果。配置 notify_url 后,支付宝还可能通过异步通知发送交易状态。因此,它既不是“只实时返回”,也不是“只异步回调”。

周期扣款的返回方式要看具体接口文档:

  • 如果同步响应中有明确的交易状态,可以先根据同步结果处理。
  • 如果接口支持 notify_url,还要接收异步通知,并通过通知或主动查询更新最终状态。
  • 如果同步结果是处理中、未知或超时,不能直接判断扣款成功或失败,需要使用支付宝交易号或商户订单号查询。

生产系统不应只依赖一种结果返回渠道。

如何判断 alipay.trade.pay 的结果

调用 alipay.trade.pay 后,支付宝会同步返回接口响应,通常可以这样处理:

  1. 响应明确表示支付成功时,立即更新订单状态。
  2. 响应明确表示失败时,根据错误码结束订单、修正参数或重试。
  3. 遇到网络超时、系统繁忙或状态无法确认等情况时,将订单标记为“支付处理中”,再调用交易查询接口确认。
  4. 配置了 notify_url 后,还要接收异步通知并完成验签。

HTTP 请求成功只代表通信成功,不代表支付成功。业务判断要看支付宝响应中的业务结果和交易状态,不能只看 HTTP 状态码。

周期扣款接口如何处理

“周期扣款接口”可能指签约扣款、周期支付或代扣等不同产品,其接口名称和通知机制不完全相同。在没有明确接口名称和产品版本时,无法断定结果一定来自同步响应或异步通知。

接入时需要检查对应接口文档,确认:

  • 是否支持 notify_url
  • 同步响应中是否包含交易状态
  • 是否提供交易查询接口
  • 哪些响应码分别表示成功、失败或处理中
  • 异步通知是否会重复发送
  • 通知中的交易状态是否为最终状态

系统可以统一按照“同步受理、异步更新、主动查询兜底”的方式处理:

发起扣款
  ├─ 同步明确成功:记录成功,等待异步通知校验或补充信息
  ├─ 同步明确失败:记录失败,根据错误码决定是否重试
  └─ 同步结果未知:记录处理中,调用查询接口确认

收到异步通知
  ├─ 验签
  ├─ 校验商户号、订单号、金额和币种
  ├─ 幂等更新订单
  └─ 按接口要求返回成功应答

Java 调用示例

以下示例用于说明处理思路。具体请求参数要根据实际签约产品和支付宝 SDK 版本调整:

AlipayTradePayRequest request = new AlipayTradePayRequest();
request.setNotifyUrl("https://example.com/alipay/notify");
request.setBizContent("""
{
  "out_trade_no": "ORDER_202609090001",
  "scene": "bar_code",
  "auth_code": "CUSTOMER_AUTH_CODE",
  "subject": "订单支付",
  "total_amount": "10.00"
}
""");

AlipayTradePayResponse response = alipayClient.execute(request);

if (response.isSuccess()) {
    // 同步业务处理成功。
    // 仍应保存 trade_no、out_trade_no 等信息,并保证后续通知幂等。
    markOrderPaid(
        response.getOutTradeNo(),
        response.getTradeNo()
    );
} else {
    String code = response.getCode();
    String subCode = response.getSubCode();

    if (isResultUnknown(code, subCode)) {
        // 超时或状态不确定时,不要直接标记失败。
        markOrderProcessing("ORDER_202609090001");
        scheduleTradeQuery("ORDER_202609090001");
    } else {
        markOrderFailed(
            "ORDER_202609090001",
            code,
            subCode
        );
    }
}

不要仅根据示例为 isResultUnknown 定义一组固定错误码。具体实现要参考当前接口文档和实际接入产品的错误码说明。

异步通知处理示例

处理异步通知前必须先验签。下面是一个简化示例:

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

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

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

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

    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 = findOrder(outTradeNo);
    if (order == null || !order.getTotalAmount().equals(totalAmount)) {
        return "failure";
    }

    // 使用 out_trade_no 或 trade_no 保证重复通知不会重复入账。
    updateOrderIdempotently(outTradeNo, tradeNo, tradeStatus);

    return "success";
}

实际验签时,要确认公钥类型与接入模式一致,例如支付宝公钥模式或证书模式。异步通知应返回什么格式,也要以对应接口文档为准。

同步结果不确定时主动查询

发生超时、连接中断或业务状态未知时,可以通过 alipay.trade.query 查询:

AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();
queryRequest.setBizContent("""
{
  "out_trade_no": "ORDER_202609090001"
}
""");

AlipayTradeQueryResponse queryResponse =
    alipayClient.execute(queryRequest);

if (queryResponse.isSuccess()) {
    String tradeStatus = queryResponse.getTradeStatus();

    switch (tradeStatus) {
        case "TRADE_SUCCESS":
        case "TRADE_FINISHED":
            markOrderPaid(
                queryResponse.getOutTradeNo(),
                queryResponse.getTradeNo()
            );
            break;

        case "WAIT_BUYER_PAY":
            markOrderProcessing(queryResponse.getOutTradeNo());
            break;

        case "TRADE_CLOSED":
            markOrderClosed(queryResponse.getOutTradeNo());
            break;

        default:
            markOrderProcessing(queryResponse.getOutTradeNo());
    }
}

如果周期扣款产品有专用查询接口,应使用该接口,不能默认调用 alipay.trade.query。

注意事项

  • 同步返回、异步通知和主动查询的到达顺序不固定,更新订单时必须保证幂等。
  • 网络超时不等于扣款失败,直接重新扣款可能导致重复扣费。
  • 异步通知可能重复发送,也可能早于同步响应到达。
  • 除了验签,还要校验 app_id、商户身份、订单号、金额和币种等字段。
  • 金额应使用精确的小数类型或最小货币单位处理,不要直接用二进制浮点数比较。
  • 回调接口应尽快完成必要校验并返回成功。耗时业务可以交给内部消息队列异步执行。
  • 对长期处于“处理中”的订单,需要设置定时查询、重试上限和人工核对机制。
  • 周期扣款的具体规则以实际使用的支付宝产品、API 名称和当前官方接口文档为准。

备注:内容仅供参考。