XERO API 更新 payment amount 时出现 validation error
支付小周
结论
Xero API 不支持将已有 payment 的 Amount 更新为 0.00。Payment 创建后,不能通过修改金额把它当作 invoice note 使用。对于已有 payment,通常只能将其标记为 DELETED。是否可以删除,还要看付款状态、会计期间及其他业务限制。
如果要给 invoice 添加备注,应使用 invoice 的 History & Notes 接口。如果确实需要修改付款金额,应先删除原 payment,再用正确的金额创建新的 payment。
为什么会出现 validation error
下面的请求尝试修改一笔已有付款的金额:
api.Payments.Update(new Payment
{
Id = payment.Id,
Amount = 0
});
这个请求存在两个问题:
- 已创建 payment 的金额通常不能直接修改。
0.00不是有效的实际付款金额。Payment 代表一笔真实的付款分配,不适合作为备注或业务状态标记。
因此,SDK 即使只返回笼统的 validation error,也不表示请求缺少某个更新参数。问题在于该操作本身不符合 Payment 的更新规则。
将 payment 标记为删除则是受支持的操作:
api.Payments.Update(new Payment
{
Id = payment.Id,
Status = PaymentStatus.Deleted
});
为 invoice 添加 note
Xero Accounting API 提供了 invoice 的 History & Notes 相关接口。对应的 REST 请求通常为:
PUT /api.xro/2.0/Invoices/{InvoiceID}/History
请求体需要包含一条 HistoryRecord:
{
"HistoryRecords": [
{
"Details": "Payment was reviewed and requires manual follow-up."
}
]
}
下面是使用 HttpClient 调用该接口的示例:
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var invoiceId = "INVOICE_ID";
var json = """
{
"HistoryRecords": [
{
"Details": "Payment was reviewed and requires manual follow-up."
}
]
}
""";
using var request = new HttpRequestMessage(
HttpMethod.Put,
$"https://api.xero.com/api.xro/2.0/Invoices/{invoiceId}/History");
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
request.Headers.Add("xero-tenant-id", tenantId);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await httpClient.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException(
$"Xero request failed: {(int)response.StatusCode} {responseBody}");
}
Xero SDK 版本不同,具体方法名也可能不同,例如 CreateInvoiceHistory、CreateInvoiceHistoryAsync 或类似名称。如果旧版 SDK 没有提供相应方法,可以升级 SDK,也可以直接调用对应的 REST endpoint。
如果需要修改 payment 金额
不要尝试把金额更新为零。可以按以下步骤处理:
- 确认原 payment 是否可以删除。
- 将原 payment 的状态更新为
DELETED。 - 使用正确的金额创建新的 payment。
- 如果需要记录修改原因,再向 invoice 添加一条 history note。
示例代码如下:
if (payment != null && payment.Status != PaymentStatus.Deleted)
{
api.Payments.Update(new Payment
{
Id = payment.Id,
Status = PaymentStatus.Deleted
});
api.Payments.Create(new Payment
{
Invoice = new Invoice
{
Id = invoice.Id
},
Account = new Account
{
Id = account.Id
},
Amount = correctedAmount,
Date = paymentDate
});
}
代码中的属性名称取决于所使用的 Xero .NET SDK 版本,请以当前 SDK 的模型定义为准。
注意事项
- 已完成银行对账、处于锁定会计期间或受其他会计规则限制的 payment,可能无法直接删除。
- History note 仅用于说明和审计记录,不会改变 invoice 余额、付款状态或会计分录。
- 不要创建零金额 payment 来充当备注、占位符或状态标记。
- 捕获异常时,应记录 Xero 返回的完整响应体。详细的
ValidationErrors通常位于响应中的元素级错误信息内,SDK 抛出的顶层异常消息可能不会完整显示这些内容。 - 如果内部备注不应出现在 Xero 的历史记录中,可以将其保存在本地数据库,并通过
InvoiceID或业务引用与 Xero invoice 关联。
备注:内容仅供参考。
