From dd882e059683774567361bb72100c6ee9194ec35 Mon Sep 17 00:00:00 2001 From: javorosas Date: Fri, 4 Sep 2026 13:27:57 +0200 Subject: [PATCH] feat: add GetPaymentSummaryAsync for payment complements Calls GET /invoices/{id}/payment-summary?amount=, which returns the related document object needed to build a payment complement (complemento de pago): installment from the payment history, previous balance, and taxes prorated to the paid amount. Bumps version to 6.8.0. Also fixes the 6.7.0 release date in the changelog. --- CHANGELOG.md | 7 ++++++- FacturapiTest/WrapperBehaviorTests.cs | 20 ++++++++++++++++++++ Models/PaymentSummary.cs | 26 ++++++++++++++++++++++++++ Router/InvoiceRouter.cs | 10 +++++++++- Wrappers/IInvoiceWrapper.cs | 1 + Wrappers/InvoiceWrapper.cs | 13 ++++++++++++- facturapi-net.csproj | 2 +- 7 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 Models/PaymentSummary.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index ff75f1d..ba6420f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [6.7.0] - 2026-08-21 +## [6.8.0] - 2026-09-04 +### Added +- Added `GetPaymentSummaryAsync` to get the related-document object needed to build a payment complement (complemento de pago): installment number, previous balance, and taxes prorated to the paid amount. + +## [6.7.0] - 2026-08-24 ### Added - Added invoice ZIP request methods: `CreateZipRequestAsync`, `ListZipRequestsAsync`, `RetrieveZipRequestAsync`, and `DownloadZipRequestAsync`. ### Fixed - Expose `InvoiceItem.PropertyTaxAccount` as a list of property tax account numbers. + ## [6.6.0] - 2026-07-01 ### Added - Added retention draft methods: `UpdateDraftAsync`, `StampDraftAsync`, and `CopyToDraftAsync`. diff --git a/FacturapiTest/WrapperBehaviorTests.cs b/FacturapiTest/WrapperBehaviorTests.cs index 9295ae5..869090d 100644 --- a/FacturapiTest/WrapperBehaviorTests.cs +++ b/FacturapiTest/WrapperBehaviorTests.cs @@ -37,6 +37,26 @@ public async Task InvoiceCreateAsync_UsesPostAndQueryString() Assert.Equal("inv_001", result.Id); } + [Fact] + public async Task InvoiceGetPaymentSummaryAsync_UsesPaymentSummaryRoute() + { + var handler = new RecordingHandler((request, cancellationToken) => + { + Assert.Equal(HttpMethod.Get, request.Method); + Assert.NotNull(request.RequestUri); + Assert.Equal("/v2/invoices/inv_123/payment-summary?amount=58", request.RequestUri.PathAndQuery); + return Task.FromResult(JsonResponse("{\"uuid\":\"6CF6CE33-1BD2-4F88-A443-33013C069169\",\"installment\":1,\"last_balance\":100,\"total\":100,\"currency\":\"MXN\",\"amount\":58,\"taxes\":[{\"base\":50,\"rate\":0.16,\"type\":\"IVA\",\"factor\":\"Tasa\",\"withholding\":false}]}")); + }); + + var wrapper = new InvoiceWrapper("test_key", "v2", CreateHttpClient(handler)); + var result = await wrapper.GetPaymentSummaryAsync("inv_123", 58); + + Assert.Equal("6CF6CE33-1BD2-4F88-A443-33013C069169", result.Uuid); + Assert.Equal(1, result.Installment); + Assert.Equal(50m, result.Taxes[0].Base); + Assert.False(result.Taxes[0].Withholding); + } + [Fact] public async Task ReceiptCancelAsync_UsesReceiptDeleteRoute() { diff --git a/Models/PaymentSummary.cs b/Models/PaymentSummary.cs new file mode 100644 index 0000000..e6c6d4a --- /dev/null +++ b/Models/PaymentSummary.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; + +namespace Facturapi +{ + public class PaymentSummary + { + public string Uuid { get; set; } + public decimal? FolioNumber { get; set; } + public string Series { get; set; } + public int Installment { get; set; } + public decimal LastBalance { get; set; } + public decimal Total { get; set; } + public string Currency { get; set; } + public decimal Amount { get; set; } + public List Taxes { get; set; } + } + + public class PaymentSummaryTax + { + public decimal Base { get; set; } + public decimal Rate { get; set; } + public string Type { get; set; } + public string Factor { get; set; } + public bool Withholding { get; set; } + } +} diff --git a/Router/InvoiceRouter.cs b/Router/InvoiceRouter.cs index e1a4f2c..7f7d2dd 100644 --- a/Router/InvoiceRouter.cs +++ b/Router/InvoiceRouter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -18,6 +18,14 @@ public static string RetrieveInvoice(string id) return $"invoices/{id}"; } + public static string RetrieveInvoicePaymentSummary(string id, double amount) + { + return UriWithQuery($"{RetrieveInvoice(id)}/payment-summary", new Dictionary + { + ["amount"] = amount + }); + } + public static string CreateInvoice(Dictionary query = null) { return UriWithQuery("invoices", query); diff --git a/Wrappers/IInvoiceWrapper.cs b/Wrappers/IInvoiceWrapper.cs index 3eafcf4..4a7bca0 100644 --- a/Wrappers/IInvoiceWrapper.cs +++ b/Wrappers/IInvoiceWrapper.cs @@ -11,6 +11,7 @@ public interface IInvoiceWrapper Task> ListAsync(Dictionary query = null, CancellationToken cancellationToken = default); Task CreateAsync(Dictionary data, Dictionary options = null, CancellationToken cancellationToken = default); Task RetrieveAsync(string id, CancellationToken cancellationToken = default); + Task GetPaymentSummaryAsync(string id, double amount, CancellationToken cancellationToken = default); Task CancelAsync(string id, Dictionary query = null, CancellationToken cancellationToken = default); Task SendByEmailAsync(string id, Dictionary data = null, CancellationToken cancellationToken = default); Task DownloadZipAsync(string id, CancellationToken cancellationToken = default); diff --git a/Wrappers/InvoiceWrapper.cs b/Wrappers/InvoiceWrapper.cs index 5e3d8c1..269ad43 100644 --- a/Wrappers/InvoiceWrapper.cs +++ b/Wrappers/InvoiceWrapper.cs @@ -1,4 +1,4 @@ -using System; +using System; using Newtonsoft.Json; using System.Collections.Generic; using System.IO; @@ -50,6 +50,17 @@ public async Task RetrieveAsync(string id, CancellationToken cancellati } } + public async Task GetPaymentSummaryAsync(string id, double amount, CancellationToken cancellationToken = default) + { + using (var response = await client.GetAsync(Router.RetrieveInvoicePaymentSummary(id, amount), cancellationToken).ConfigureAwait(false)) + { + await this.ThrowIfErrorAsync(response, cancellationToken).ConfigureAwait(false); + var resultString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var summary = JsonConvert.DeserializeObject(resultString, this.jsonSettings); + return summary; + } + } + public async Task CancelAsync(string id, Dictionary query = null, CancellationToken cancellationToken = default) { using (var response = await client.DeleteAsync(Router.CancelInvoice(id, query), cancellationToken).ConfigureAwait(false)) diff --git a/facturapi-net.csproj b/facturapi-net.csproj index 9ddde63..e7c50f6 100644 --- a/facturapi-net.csproj +++ b/facturapi-net.csproj @@ -11,7 +11,7 @@ SDK oficial de Facturapi para .NET para facturación electrónica en México (CFDI), envío de documentos, búsqueda y trazabilidad. factura factura-electronica facturacion cfdi cfdi40 sat invoice invoicing facturapi mexico Facturapi - 6.7.0 + 6.8.0 $(Version) MIT false