PAYATHON 2026

当面付付款码支付取消密码输入后能否关单?

支付老李

结论

用户取消输入密码后,付款码订单通常不会立即变成失败状态,交易仍可能处于 WAIT_BUYER_PAY。因此,不能仅凭客户端的取消操作认定订单失败。

商户可以主动终止订单,但付款码支付可能出现支付结果尚未返回,或支付与关单同时发生的情况。建议先调用 alipay.trade.query 查询交易状态。对于支付结果不确定的付款码交易,应优先使用 alipay.trade.cancel 撤销交易。确认订单尚未支付后,才适合调用 alipay.trade.close 关单。

原因

用户取消密码输入,只表示当前付款确认流程已经终止,并不代表支付宝交易失败。此时可能出现以下状态:

  • WAIT_BUYER_PAY:交易已创建,正在等待用户付款。
  • TRADE_SUCCESS:支付已经成功。
  • TRADE_CLOSED:交易已经关闭。
  • 查询结果暂不明确:可能是网络超时、系统延迟,或支付结果仍在处理中。

如果商户在交易状态尚未明确时直接修改本地订单,可能导致支付宝侧已经扣款,本地订单却被标记为关闭。

alipay.trade.cancel 适合处理付款码支付结果不确定的情况。交易尚未支付时,接口会执行撤销;如果交易刚刚支付成功,资金能否退回以及接口会返回什么结果,应以实际响应和支付宝当前的接口规则为准。

建议处理步骤

  1. 调用 alipay.trade.pay 后,如果接口明确返回支付成功,正常完成订单。
  2. 如果接口返回 10003、超时或其他等待支付的结果,进入查询流程。
  3. 用户取消密码输入后,调用 alipay.trade.query 查询支付宝侧的交易状态。
  4. 按查询结果处理:
    • TRADE_SUCCESS:按支付成功处理,不再关单。
    • TRADE_CLOSED:同步关闭本地订单。
    • WAIT_BUYER_PAY:调用 alipay.trade.cancel 撤销交易。如果业务只处理未支付订单,也可以在确认交易尚未支付后调用 alipay.trade.close。
    • 状态不明确:继续进行有限次数的查询。如果仍无法确认,应执行撤销,不要直接认定支付失败。
  5. 支付宝侧确认撤销或关单成功后,才能将本地订单更新为关闭状态。
  6. 如果撤销失败或返回结果不明确,应再次查询交易的最终状态。

代码示例

以下是 Java 风格的处理流程,具体类名和方法以当前使用的支付宝 SDK 版本为准:

AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();
queryRequest.setBizContent(
    "{\"out_trade_no\":\"" + outTradeNo + "\"}"
);

AlipayTradeQueryResponse queryResponse = alipayClient.execute(queryRequest);

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

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

        case "TRADE_CLOSED":
            markOrderClosed(outTradeNo);
            break;

        case "WAIT_BUYER_PAY":
            cancelTrade(alipayClient, outTradeNo);
            break;

        default:
            scheduleTradeQuery(outTradeNo);
            break;
    }
} else {
    // 查询失败不代表支付失败,应重试查询或进入撤销流程
    scheduleTradeQuery(outTradeNo);
}

撤销付款码交易:

private void cancelTrade(
        AlipayClient alipayClient,
        String outTradeNo) throws AlipayApiException {

    AlipayTradeCancelRequest cancelRequest = new AlipayTradeCancelRequest();
    cancelRequest.setBizContent(
        "{\"out_trade_no\":\"" + outTradeNo + "\"}"
    );

    AlipayTradeCancelResponse cancelResponse =
        alipayClient.execute(cancelRequest);

    if (cancelResponse.isSuccess()) {
        // 建议再查询一次,确认支付宝侧最终状态
        scheduleTradeQuery(outTradeNo);
    } else {
        // 撤销失败不能直接关闭本地订单
        scheduleTradeQuery(outTradeNo);
    }
}

如果已经确认交易尚未支付,也可以调用关单接口:

AlipayTradeCloseRequest closeRequest = new AlipayTradeCloseRequest();
closeRequest.setBizContent(
    "{\"out_trade_no\":\"" + outTradeNo + "\"}"
);

AlipayTradeCloseResponse closeResponse =
    alipayClient.execute(closeRequest);

if (closeResponse.isSuccess()) {
    markOrderClosed(outTradeNo);
} else {
    // 关单失败后重新查询,防止支付与关单并发
    scheduleTradeQuery(outTradeNo);
}

注意事项

  • 不要将“用户关闭密码框”直接视为支付失败。
  • 不要只依赖前端页面显示的状态,应以服务端查询结果为准。
  • 支付、查询、撤销和关单必须使用同一个 out_trade_no。
  • 更新本地订单时必须保证幂等,避免重复通知或重复查询造成订单状态回退。
  • 订单一旦标记为支付成功,不能再更新为待支付或普通关闭状态。
  • 撤销或关单接口调用超时,不代表操作失败。应通过 alipay.trade.query 查询并确认最终状态。
  • alipay.trade.close 和 alipay.trade.cancel 的适用状态及具体行为,可能因产品接入方式或接口规则而不同,应以当前使用的支付宝开放平台接口文档和实际响应码为准。付款码支付结果不确定时,使用撤销通常比直接关单更稳妥。

备注:内容仅供参考。