PAYATHON 2026

ACQ.PAYMENT_FAIL 等余额不足错误码有什么区别?

支付小周

结论

这三个错误码对应不同的失败层级:

错误码含义常见场景
ACQ.PAYMENT_FAIL通用支付失败支付渠道只返回“支付失败”,或平台无法将原因归类为更具体的错误码
ACQ.BUYER_BALANCE_NOT_ENOUGH买家的支付账户余额不足买家使用余额类资金渠道付款,但可用余额不足
ACQ.BUYER_BANKCARD_BALANCE_NOT_ENOUGH买家银行卡可用余额或额度不足买家使用银行卡付款,发卡行返回余额不足、可用额度不足等结果

ACQ.PAYMENT_FAIL 并不专指余额不足,而是一个范围更广的失败结果。即使实际原因是余额不足,只要银行或支付渠道没有返回可识别的具体信息,最终也可能被映射为 ACQ.PAYMENT_FAIL。

为什么实际更容易遇到 ACQ.PAYMENT_FAIL

支付请求通常会经过支付平台、收单机构和银行等多个环节。错误码能具体到什么程度,取决于下游渠道提供的信息,以及支付平台采用的映射策略。

常见情况包括:

  • 买家使用支付账户余额付款,系统能够明确判断余额不足时,可能返回 ACQ.BUYER_BALANCE_NOT_ENOUGH。
  • 买家使用银行卡付款,银行明确返回余额不足或信用卡可用额度不足时,可能返回 ACQ.BUYER_BANKCARD_BALANCE_NOT_ENOUGH。
  • 银行只返回交易失败、拒绝交易等笼统结果,或平台无法准确识别失败原因时,可能返回 ACQ.PAYMENT_FAIL。
  • 部分支付产品不会通过接口同步响应提供具体的资金渠道失败原因,而是统一返回支付失败。
  • 支付方式、银行、接口产品或风控策略不同,同一类失败也可能被映射成不同的错误码。

错误码如何映射取决于支付渠道的实际行为,不能只看名称,就认为所有银行和支付产品都会稳定返回后两个细分错误码。

建议的处理方式

1. 将三个错误码统一归入“买家需要更换支付方式”

业务提示可以有所区别,但对应的后续操作基本相同:

  • ACQ.BUYER_BALANCE_NOT_ENOUGH:提示买家充值或改用其他支付方式。
  • ACQ.BUYER_BANKCARD_BALANCE_NOT_ENOUGH:提示买家检查银行卡余额或额度,或者更换银行卡。
  • ACQ.PAYMENT_FAIL:提示支付未完成,建议更换支付方式,不要直接断定是余额不足。

不建议遇到这类错误后无条件自动重试。余额不足、额度不足、银行拒绝或风控问题,通常不会因为立即重试而消失。反复发起支付还可能造成重复下单,或触发渠道限制。

2. 结合完整响应判断

处理接口结果时,不要只读取 sub_code,还应记录并检查:

  • code
  • sub_code
  • sub_msg
  • 商户订单号
  • 支付平台交易号
  • 接口名称
  • 请求时间
  • 后续订单查询结果

支付接口中的 ACQ.* 通常位于业务错误字段 sub_code,外层 code 可能只表示业务处理失败。具体字段结构仍应以当前接口的响应模型为准。

3. 无法确定支付结果时主动查询订单

遇到超时、网络中断或响应内容不足以确认交易终态时,不要直接创建新订单并重新支付。应先使用原商户订单号查询交易状态:

  • 查询结果为成功:按支付成功处理。
  • 查询结果为明确关闭或失败:允许买家重新支付。
  • 查询结果仍为处理中:按合理间隔继续查询,并等待异步通知。
  • 查询不到交易:确认原请求没有被受理后,再决定是否重新发起。

这样可以避免接口返回失败或超时,但买家实际已经被扣款,从而造成重复支付。

代码示例

下面的示例只演示错误码分类逻辑。响应类名和查询接口需要替换成项目实际使用的 SDK 类型。

public PaymentAction handlePaymentFailure(
        String code,
        String subCode,
        String subMsg) {

    if ("ACQ.BUYER_BALANCE_NOT_ENOUGH".equals(subCode)) {
        return PaymentAction.fail(
            "账户可用余额不足,请充值或更换支付方式"
        );
    }

    if ("ACQ.BUYER_BANKCARD_BALANCE_NOT_ENOUGH".equals(subCode)) {
        return PaymentAction.fail(
            "银行卡余额或可用额度不足,请更换银行卡或支付方式"
        );
    }

    if ("ACQ.PAYMENT_FAIL".equals(subCode)) {
        return PaymentAction.fail(
            "支付未完成,请更换支付方式后重试"
        );
    }

    // 超时、系统异常等不能确认支付终态的情况,
    // 应先查询订单,而不是直接重新发起支付。
    if (isPaymentStatusUnknown(code, subCode)) {
        return PaymentAction.queryOrder();
    }

    return PaymentAction.fail(
        subMsg != null ? subMsg : "支付失败,请稍后重试"
    );
}

业务系统也可以统一分类:

private static final Set<String> CHANGE_PAYMENT_METHOD_CODES = Set.of(
    "ACQ.PAYMENT_FAIL",
    "ACQ.BUYER_BALANCE_NOT_ENOUGH",
    "ACQ.BUYER_BANKCARD_BALANCE_NOT_ENOUGH"
);

public boolean shouldChangePaymentMethod(String subCode) {
    return CHANGE_PAYMENT_METHOD_CODES.contains(subCode);
}

前端提示和内部日志应分别处理。给买家的提示要简洁,内部日志则应保留原始错误信息:

log.warn(
    "Payment failed: outTradeNo={}, code={}, subCode={}, subMsg={}",
    outTradeNo,
    response.getCode(),
    response.getSubCode(),
    response.getSubMsg()
);

注意事项

  • 不要将 ACQ.PAYMENT_FAIL 固定解释为余额不足。它还可能包含银行拒绝、渠道异常、风控拦截等没有进一步细分的原因。
  • ACQ.BUYER_BANKCARD_BALANCE_NOT_ENOUGH 中的“不足”,既可能指借记卡余额不足,也可能指信用卡可用额度不足,具体取决于银行返回的结果。
  • 不要依赖某个细分错误码必然出现。不同银行、支付渠道和产品提供的错误信息粒度可能不同。
  • 不要直接向买家展示原始 sub_msg,其中可能含有内部渠道信息。应将其转换成稳定、易懂的业务提示。
  • 支付结果不确定时,应通过订单查询和异步通知确认终态,并保证通知处理的幂等性。
  • 如需确认某个接口在特定支付产品中的准确映射规则,应以该接口当前版本的官方错误码文档和实际响应为准。

备注:内容仅供参考。