PAYATHON 2026

QuickBooks PHP API 添加 Payment 时出现 Invalid ID 错误

支付小周

结论

$Invoice->getId() 返回的 {-10643} 不是 QuickBooks 接受的原始 Invoice ID,也不代表 ID 是负数。这是 quickbooks-php 对 Intuit ID 类型的内部表示。大括号和连字符都属于包装格式,实际应发送给 API 的 ID 是 10643。

设置 LinkedTxn.TxnId 前,应通过库提供的 QuickBooks_IPP_IDS::usableIDType() 提取可用于请求的 ID:

$invoiceId = QuickBooks_IPP_IDS::usableIDType($Invoice->getId());
$LinkedTxn->setTxnId($invoiceId);

最终生成的 XML 应为:

<TxnId>10643</TxnId>

不能是:

<TxnId>{-10643}</TxnId>

原因

在某些版本中,quickbooks-php 会将 Intuit 的复合 ID 表示为:

{domain-id}

当 domain 为空时,结果可能显示为:

{-10643}

这里的 - 是内部格式的分隔符,不是数学意义上的负号。如果直接将这个包装后的值传给:

$LinkedTxn->setTxnId($invoiceId);

请求 XML 中就会包含 {、} 和 -。QuickBooks API 无法将 {-10643} 解析为合法的实体 ID,因此会返回:

2030: Invalid ID
Id should be a valid number. Supplied value:{-10643}

请求内容也表明,出错的位置是 LinkedTxn.TxnId:

<LinkedTxn>
    <TxnId>{-10643}</TxnId>
    <TxnType>Invoice</TxnType>
</LinkedTxn>

修改方法

取得 Invoice 后,先将内部 ID 转换为 API 可接受的 ID:

$Invoice = $invoices[0];

$invoiceId = QuickBooks_IPP_IDS::usableIDType(
    $Invoice->getId()
);

再创建关联:

$LinkedTxn = new QuickBooks_IPP_Object_LinkedTxn();
$LinkedTxn->setTxnId($invoiceId);
$LinkedTxn->setTxnType('Invoice');

$Line->setLinkedTxn($LinkedTxn);

完整的核心代码可以写成:

$invoices = $InvoiceService->query(
    $Context,
    $realm,
    "SELECT * FROM Invoice WHERE DocNumber = '" . $invoiceNumber . "'"
);

if (!$invoices || !isset($invoices[0])) {
    throw new RuntimeException(
        'Invoice not found: ' . $invoiceNumber
    );
}

$Invoice = $invoices[0];

$invoiceId = QuickBooks_IPP_IDS::usableIDType(
    $Invoice->getId()
);

$CustomerRef = $Invoice->getCustomerRef();

$PaymentService = new QuickBooks_IPP_Service_Payment();

$Payment = new QuickBooks_IPP_Object_Payment();
$Payment->setPaymentRefNum($invoiceNumber);
$Payment->setTxnDate(date('Y-m-d'));
$Payment->setTotalAmt($depositAmount);
$Payment->setCustomerRef($CustomerRef);

$Line = new QuickBooks_IPP_Object_Line();
$Line->setAmount($depositAmount);

$LinkedTxn = new QuickBooks_IPP_Object_LinkedTxn();
$LinkedTxn->setTxnId($invoiceId);
$LinkedTxn->setTxnType('Invoice');

$Line->setLinkedTxn($LinkedTxn);
$Payment->addLine($Line);

if ($paymentId = $PaymentService->add(
    $Context,
    $realm,
    $Payment
)) {
    print('Our new Payment ID is: [' . $paymentId . ']');
} else {
    print($PaymentService->lastError());
}

如何确认修复是否生效

发送 Payment 前,检查转换前后的值:

debug($Invoice->getId());
debug($invoiceId);

预期结果类似:

{-10643}
10643

还应检查:

$PaymentService->lastRequest()

其中的 LinkedTxn 应变为:

<LinkedTxn xmlns="http://schema.intuit.com/finance/v3">
    <TxnId>10643</TxnId>
    <TxnType>Invoice</TxnType>
</LinkedTxn>

注意事项

不要直接使用 abs()、删除负号或仅通过字符串替换来处理这个值。例如:

$invoiceId = abs($Invoice->getId());

这种写法依赖 PHP 的隐式类型转换,也没有正确处理库的 ID 包装格式。应优先使用:

QuickBooks_IPP_IDS::usableIDType()

还需注意:

  • 确认查询结果不为空后,再访问 $invoices[0]。
  • TxnId 必须使用 Invoice 的内部实体 ID,不能使用 DocNumber。
  • PaymentRefNum 可以填写业务上的付款编号,但不能代替 LinkedTxn.TxnId。
  • Payment 的 CustomerRef 应与所关联 Invoice 的客户一致。
  • 如果当前安装的 quickbooks-php 版本没有 usableIDType(),需要通过该版本的 ID 对象接口取得原始 value,或者升级到包含此辅助方法的兼容版本。不要将带大括号的内部表示原样写入 XML。

备注:内容仅供参考。