PAYATHON 2026

支付宝沙箱环境签约后扣款报错 ACQ.AGREEMENT_ERROR

支付阿杰

结论

ACQ.AGREEMENT_ERROR 表示支付宝扣款时,协议有效性或扣款规则校验未通过。协议状态为 NORMAL 只能说明签约关系存在,不代表当前已经满足周期扣款条件。

结合“按天扣款、execute_time 设置为签约当天、尚未成功扣款”这些情况,常见原因是首次执行日期设置得太早。签约完成时,当天的扣款时间窗口可能已经过去,或者协议的周期计划还没有生效。按天签约时,建议将 execute_time 设置为签约后的日期,通常从次日开始,并在允许扣款的时间到达后再发起交易。

lastDeductTime 为 null,通常只表示还没有成功扣款。nextDeductTime 为 null 也不能单独证明协议不可用,尤其是在沙箱环境中,相关字段可能不会及时生成或完整返回。

正确设置 execute_time

execute_time 是签约周期规则的一部分,不是调用扣款接口时传入的交易执行时间。

当 period_type 为 DAY 时,应按接口要求传入完整日期,例如:

{
  "period_type": "DAY",
  "period": 1,
  "execute_time": "2026-09-09",
  "single_amount": "0.01"
}

如果签约日期是 2026-09-08,首次执行日期可以设置为 2026-09-09。不要使用已经过去的日期,也不建议将首次执行日期设为签约当天。

使用 MONTH 周期时,execute_time 的格式和含义通常不同,不能直接套用 DAY 类型的日期格式。具体要求应以当前签约产品的接口文档为准。

签约参数示例

以下代码只展示与问题有关的核心结构。实际请求中仍需保留应用对应的 product_code、personal_product_code、sign_scene 等签约参数。

AlipayUserAgreementPageSignRequest request =
        new AlipayUserAgreementPageSignRequest();

request.setBizContent("""
{
  "external_agreement_no": "AGREEMENT_20260908_001",
  "product_code": "实际签约产品码",
  "personal_product_code": "实际个人产品码",
  "sign_scene": "实际签约场景",
  "period_rule_params": {
    "period_type": "DAY",
    "period": 1,
    "execute_time": "2026-09-09",
    "single_amount": "0.01"
  }
}
""");

日期应根据签约完成时间动态计算,不要直接写死在代码中:

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

LocalDate firstDeductDate = LocalDate.now(ZoneId.of("Asia/Shanghai"))
        .plusDays(1);

String executeTime = firstDeductDate.format(
        DateTimeFormatter.ISO_LOCAL_DATE
);

协议扣款示例

签约成功后,先通过协议查询接口取得支付宝生成的协议号,再将该协议号传入扣款请求。核心参数如下:

AlipayTradePayRequest request = new AlipayTradePayRequest();

request.setBizContent("""
{
  "out_trade_no": "ORDER_20260909_001",
  "total_amount": "0.01",
  "subject": "周期扣款测试",
  "product_code": "实际扣款产品码",
  "agreement_params": {
    "agreement_no": "支付宝返回的协议号"
  }
}
""");

AlipayTradePayResponse response = alipayClient.execute(request);

不要将商户侧的 external_agreement_no 当作支付宝协议号传入。两者的含义通常是:

  • external_agreement_no:商户生成的外部协议编号。
  • agreement_no:支付宝在签约成功后生成的协议编号。

扣款请求需要传入哪些协议字段,取决于已经开通的产品和签约模式。如果产品文档要求提供 auth_confirm_no、用户标识或其他参数,也需要一并传入。

建议的排查顺序

  1. 使用 AlipayUserAgreementQueryRequest 查询协议,不要用交易查询接口代替协议查询。
  2. 确认查询结果中的协议状态为 NORMAL,并检查协议号是否与扣款请求中的 agreement_no 完全一致。
  3. 检查 period_rule_params.period_type、period 和 execute_time 是否符合产品规则。
  4. 按天扣款时,将首次执行日期改为签约次日或更晚,然后重新签约。已有协议的周期规则通常不能通过修改本地请求直接变更。
  5. 等到允许扣款的日期再发起请求,并为每次请求生成唯一的 out_trade_no。
  6. 确认扣款金额没有超过 single_amount,也没有超过协议约定的总金额、总次数或其他额度限制。
  7. 确认签约和扣款使用同一个沙箱应用、相同的网关环境及匹配的沙箱买家账号。

关于两个时间字段

第一次成功扣款前,lastDeductTime 为 null 一般是正常现象。

nextDeductTime 为 null,可能表示周期计划尚未形成,也可能是沙箱环境没有完整模拟该字段。不能仅凭这个字段判断协议无效,需要结合协议状态、周期规则、首次执行日期和扣款接口的返回结果进行判断。

如果将首次执行日期调整到未来并重新签约,在生效日期后使用正确的 agreement_no 扣款,仍然返回相同错误,请保存完整的 code、sub_code、sub_msg、支付宝请求号和请求参数,再通过支付宝开放平台技术支持确认。ACQ.AGREEMENT_ERROR 是一个较宽泛的业务错误,仅凭该错误码无法判断是否还存在沙箱产品权限或产品参数不匹配的问题。

备注:内容仅供参考。