PAYATHON 2026

如何通过 Xero API 为 invoice 的 payment 添加附件?

支付老李

结论

Xero 的公共 Accounting API 不支持直接为 Payment 添加附件。以下地址不是有效的 Attachments API endpoint:

PUT /api.xro/2.0/Payments/{PaymentID}/Attachments/{Filename}

即使 PaymentID 有效,该请求仍会返回 HTTP 404 Not Found。这里的 404 表示附件路由不存在,而不是对应的 payment 不存在。

Xero UI 可以完成这项操作,但 UI 使用的内部能力不一定会通过公共 API 开放。

可行的处理方式

目前可以采用以下替代方案:

  1. 将附件上传到 payment 所属的 Invoice。
  2. 在文件名或外部系统中记录 PaymentID,保留附件与 payment 的关联。
  3. 如果业务上必须将附件直接绑定到 payment,只能等待 Xero 在公共 API 中增加相应资源,或向 Xero 开发者支持渠道确认最新能力。

不要通过猜测 URL 来调用文档中没有声明的资源路径。Xero 只为明确支持的资源类型提供附件路由。Payments 查询接口存在,不代表它也有附件子资源。

Ruby 上传到 Invoice 的示例

以下示例使用 Net::HTTP 将附件上传到相关 invoice:

require "net/http"
require "uri"

access_token = ENV.fetch("XERO_ACCESS_TOKEN")
tenant_id    = ENV.fetch("XERO_TENANT_ID")

invoice_id = "YOUR_INVOICE_ID"
filename   = "payment-receipt.pdf"
file_path  = "/path/to/payment-receipt.pdf"

encoded_filename = URI.encode_www_form_component(filename).gsub("+", "%20")

uri = URI(
  "https://api.xero.com/api.xro/2.0/" \
  "Invoices/#{invoice_id}/Attachments/#{encoded_filename}"
)

request = Net::HTTP::Put.new(uri)
request["Authorization"] = "Bearer #{access_token}"
request["Xero-tenant-id"] = tenant_id
request["Accept"] = "application/json"
request["Content-Type"] = "application/pdf"
request.body = File.binread(file_path)

response = Net::HTTP.start(
  uri.hostname,
  uri.port,
  use_ssl: true
) do |http|
  http.request(request)
end

unless response.is_a?(Net::HTTPSuccess)
  warn "Attachment upload failed: HTTP #{response.code}"
  warn response.body
end

如果附件不是 PDF,需要根据实际文件类型设置 Content-Type,例如:

image/png
image/jpeg
text/plain
application/octet-stream

可以在文件名中加入 PaymentID,以保留附件与 payment 的关联:

filename = "payment-#{payment_id}-receipt.pdf"

注意事项

  • 上传附件通常使用 PUT,而不是获取 payment 详情时使用的 GET。
  • Authorization 和 Xero-tenant-id 请求头必须正确。
  • Filename 是 URL 路径的一部分,需要进行 URL 编码。
  • 支持的资源类型应以 Xero 当前的 Attachments API 文档为准。如果新版文档以后明确加入 Payments,再改用文档公布的 endpoint。
  • 不建议调用 Xero UI 使用的未公开接口。这类接口可能依赖浏览器会话,也可能随时变化,不在公共 API 的兼容性承诺范围内。

备注:内容仅供参考。