如何通过 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 开放。
可行的处理方式
目前可以采用以下替代方案:
- 将附件上传到 payment 所属的
Invoice。 - 在文件名或外部系统中记录
PaymentID,保留附件与 payment 的关联。 - 如果业务上必须将附件直接绑定到 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 的兼容性承诺范围内。
备注:内容仅供参考。
