PAYATHON 2026

如何使用 Xero API 将单笔 Payment 应用于多个发票和贷项通知单

支付老李

结论

Xero 的 Payment 不是一笔付款中的“分配明细”,而是某笔金额与一张 Invoice 或 CreditNote 的应用关系。每个 Payment 只能引用一张单据。

涉及多张单据时,需要:

  1. 按每张 Invoice 或 CreditNote 拆分金额;
  2. 为每张单据分别创建一个 Payment;
  3. 在同一次 API 请求中提交多个 Payment 对象;
  4. 使用贷项通知单抵扣发票时创建 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

对应操作如下:

  1. 创建 Allocation,将贷项通知单中的 200 分配给该发票;
  2. 创建 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,不能保证它们在界面中自动合并。

备注:内容仅供参考。