如何使用 Xero API 将单笔 Payment 应用于多个发票和贷项通知单
结论
Xero 的 Payment 不是一笔付款中的“分配明细”,而是某笔金额与一张 Invoice 或 CreditNote 的应用关系。每个 Payment 只能引用一张单据。
涉及多张单据时,需要:
- 按每张
Invoice或CreditNote拆分金额; - 为每张单据分别创建一个
Payment; - 在同一次 API 请求中提交多个
Payment对象; - 使用贷项通知单抵扣发票时创建
Allocation,不要将其作为普通银行付款处理。
为什么必须拆分 Payment
Payment 的主要结构如下:
Payment
├── Invoice 或 CreditNote
├── Account
├── Date
└── Amount
Invoice 和 CreditNote 用于指定这笔金额应用到哪张单据。它们不是单据集合,也不能同时设置。
例如,收到一笔 1,000 元的款项,分配方式为:
- 发票 A:
600 - 发票 B:
300 - 发票 C:
100
API 中需要创建三个 Payment:
Payment 1 -> Invoice A -> 600
Payment 2 -> Invoice B -> 300
Payment 3 -> Invoice C -> 100
这三个对象可以使用相同的银行账户、付款日期和业务参考号,但在 Xero 的数据模型中,它们仍是三条独立的付款应用记录。
C# 示例:将一笔金额拆分到多张发票
下面用简化代码展示数据结构。具体方法名称可能随 Xero .NET SDK 版本而变化,但请求模型和拆分方式不变。
var paymentDate = DateTime.UtcNow.Date;
var bankAccountId = Guid.Parse("BANK-ACCOUNT-ID");
var payments = new List<Payment>
{
new Payment
{
Invoice = new Invoice
{
InvoiceID = Guid.Parse("INVOICE-A-ID")
},
Account = new Account
{
AccountID = bankAccountId
},
Date = paymentDate,
Amount = 600.00m,
Reference = "RECEIPT-2026-001"
},
new Payment
{
Invoice = new Invoice
{
InvoiceID = Guid.Parse("INVOICE-B-ID")
},
Account = new Account
{
AccountID = bankAccountId
},
Date = paymentDate,
Amount = 300.00m,
Reference = "RECEIPT-2026-001"
},
new Payment
{
Invoice = new Invoice
{
InvoiceID = Guid.Parse("INVOICE-C-ID")
},
Account = new Account
{
AccountID = bankAccountId
},
Date = paymentDate,
Amount = 100.00m,
Reference = "RECEIPT-2026-001"
}
};
var request = new Payments
{
_Payments = payments
};
// 方法签名取决于实际使用的 Xero .NET SDK 版本。
// await accountingApi.CreatePaymentsAsync(accessToken, tenantId, request);
一次 API 请求可以包含多个 Payment,但其中每个 Payment 只能对应一张单据。
贷项通知单抵扣发票时使用 Allocation
如果要用贷项通知单的余额抵扣发票,通常不应创建经过银行账户的 Payment,而应在相应的 CreditNote 下创建 Allocation。
例如,用一张贷项通知单分别抵扣两张发票:
var allocations = new Allocations
{
_Allocations = new List<Allocation>
{
new Allocation
{
Invoice = new Invoice
{
InvoiceID = Guid.Parse("INVOICE-A-ID")
},
Amount = 150.00m,
Date = DateTime.UtcNow.Date
},
new Allocation
{
Invoice = new Invoice
{
InvoiceID = Guid.Parse("INVOICE-B-ID")
},
Amount = 50.00m,
Date = DateTime.UtcNow.Date
}
}
};
// 方法签名取决于 SDK 版本。
// await accountingApi.CreateCreditNoteAllocationAsync(
// accessToken,
// tenantId,
// creditNoteId,
// allocations);
这一操作不会产生新的现金流,只会把已有的贷项余额分配给发票。
同时涉及现金付款和贷项抵扣
如果一张发票由现金和贷项通知单共同结清,两部分需要分别处理:
发票总额:1,000
贷项抵扣:200
银行付款:800
对应操作如下:
- 创建
Allocation,将贷项通知单中的200分配给该发票; - 创建
Payment,将银行账户中的800应用于该发票。
不要先创建一笔 1,000 的 Payment,再额外分配 200 的贷项,否则可能造成超额付款。
关于 Batch Payment
如果处理的是应付账款批量付款,可以考虑 Xero 的 BatchPayment 模型。它可以将多笔供应商付款放在同一批次中,但每个付款项目仍需分别关联对应的单据。
当前业务能否使用 BatchPayment,取决于:
- 单据是
ACCPAY还是ACCREC; - 当前使用的 Xero Accounting API 和 .NET SDK 是否提供相关接口;
- 是否需要生成银行付款文件;
- 目标类型的贷项通知单能否包含在同一批次中。
因此,不能仅为了避开 Payment 只能关联一张单据的限制,就认为 BatchPayment 可以覆盖所有收款、付款和贷项抵扣场景。
注意事项
- 每笔分配金额不能超过对应单据当前可支付或可抵扣的余额。
- 所有
Payment.Amount的合计应与实际银行收付款金额一致。 - 多个
Payment可以使用同一个Reference,以便追踪它们所属的同一笔外部交易,但系统不会因此将它们合并成一个Payment对象。 - 不要在同一个
Payment中同时设置Invoice和CreditNote。 - 批量请求未必具有数据库事务式的“全部成功或全部失败”语义。需要检查 API 返回的每条结果,并处理验证错误或部分失败。
- 重试请求前,先确认哪些记录已经创建,以免重复记账。可以保存外部交易号、单据 ID、分配金额以及 Xero 返回的
PaymentID。 - 如果希望多项分配在银行对账界面显示为一笔汇总交易,还需结合 Xero 的批量付款或银行对账流程进行设计。仅为多个
Payment设置相同的Reference,不能保证它们在界面中自动合并。
备注:内容仅供参考。
