From b03194948cde455d65730281510c6eefac891586 Mon Sep 17 00:00:00 2001 From: "raul@facturapi.io" Date: Thu, 5 Nov 2026 11:22:23 -0600 Subject: [PATCH] feat(organizations): add manifiesto ZIP download URL --- CHANGELOG.md | 6 ++++++ FacturapiTest/WrapperBehaviorTests.cs | 19 +++++++++++++++++++ Models/SignedDownloadUrl.cs | 12 ++++++++++++ Router/OrganizationRouter.cs | 5 +++++ Wrappers/IOrganizationWrapper.cs | 1 + Wrappers/OrganizationWrapper.cs | 10 ++++++++++ facturapi-net.csproj | 2 +- 7 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 Models/SignedDownloadUrl.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d49595..a4caa89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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). +## [7.2.0] - 2026-11-05 + +### Added + +- Add `Organization.DownloadManifiestoZipUrlAsync` to obtain a temporary URL for downloading the signed Carta Manifiesto ZIP associated with the organization's certificate RFC. + ## [7.1.0] - 2026-10-07 ### Added diff --git a/FacturapiTest/WrapperBehaviorTests.cs b/FacturapiTest/WrapperBehaviorTests.cs index 6e38009..4905870 100644 --- a/FacturapiTest/WrapperBehaviorTests.cs +++ b/FacturapiTest/WrapperBehaviorTests.cs @@ -142,6 +142,25 @@ public async Task OrganizationDeleteSeriesAsync_UsesDeleteSeriesRoute() Assert.Equal("A", result.Name); } + [Fact] + public async Task OrganizationDownloadManifiestoZipUrlAsync_UsesExpectedRoute() + { + var handler = new RecordingHandler((request, cancellationToken) => + { + Assert.Equal(HttpMethod.Get, request.Method); + Assert.NotNull(request.RequestUri); + Assert.Equal("/v2/organizations/org_1/manifiesto/download-url", request.RequestUri.PathAndQuery); + return Task.FromResult(JsonResponse("{\"url\":\"https://storage.googleapis.com/signed-download\",\"expires_at\":\"2030-01-01T00:00:00.000Z\",\"content_type\":\"application/zip\",\"filename\":\"manifiesto_AAA010101AAA.zip\"}")); + }); + + var wrapper = new OrganizationWrapper("test_key", "v2", CreateHttpClient(handler)); + var result = await wrapper.DownloadManifiestoZipUrlAsync("org_1"); + + Assert.Equal("application/zip", result.ContentType); + Assert.Equal("manifiesto_AAA010101AAA.zip", result.Filename); + Assert.Equal(new DateTimeOffset(2030, 1, 1, 0, 0, 0, TimeSpan.Zero), result.ExpiresAt); + } + [Fact] public async Task OrganizationListTeamAccessAsync_UsesTeamRoute() { diff --git a/Models/SignedDownloadUrl.cs b/Models/SignedDownloadUrl.cs new file mode 100644 index 0000000..7740151 --- /dev/null +++ b/Models/SignedDownloadUrl.cs @@ -0,0 +1,12 @@ +using System; + +namespace Facturapi +{ + public class SignedDownloadUrl + { + public string Url { get; set; } + public DateTimeOffset ExpiresAt { get; set; } + public string ContentType { get; set; } + public string Filename { get; set; } + } +} diff --git a/Router/OrganizationRouter.cs b/Router/OrganizationRouter.cs index fe200ef..be568ca 100644 --- a/Router/OrganizationRouter.cs +++ b/Router/OrganizationRouter.cs @@ -65,6 +65,11 @@ public static string DeleteCertificate(string id) return $"{RetrieveOrganization(id)}/certificate"; } + public static string ManifiestoDownloadUrl(string id) + { + return $"{RetrieveOrganization(id)}/manifiesto/download-url"; + } + public static string GetTestApiKey(string id) { return $"{RetrieveOrganization(id)}/apikeys/test"; diff --git a/Wrappers/IOrganizationWrapper.cs b/Wrappers/IOrganizationWrapper.cs index 07c7c75..49422ee 100644 --- a/Wrappers/IOrganizationWrapper.cs +++ b/Wrappers/IOrganizationWrapper.cs @@ -16,6 +16,7 @@ public interface IOrganizationWrapper Task UploadLogoAsync(string id, Stream file, CancellationToken cancellationToken = default); Task UploadCertificateAsync(string id, Stream cerFile, Stream keyFile, string password, CancellationToken cancellationToken = default); Task DeleteCertificateAsync(string id, CancellationToken cancellationToken = default); + Task DownloadManifiestoZipUrlAsync(string id, CancellationToken cancellationToken = default); Task UpdateLegalAsync(string id, Dictionary data, CancellationToken cancellationToken = default); Task UpdateReceiptSettingsAsync(string id, Dictionary data, CancellationToken cancellationToken = default); Task UpdateCustomizationAsync(string id, Dictionary data, CancellationToken cancellationToken = default); diff --git a/Wrappers/OrganizationWrapper.cs b/Wrappers/OrganizationWrapper.cs index e55ebd2..f7ff3f7 100644 --- a/Wrappers/OrganizationWrapper.cs +++ b/Wrappers/OrganizationWrapper.cs @@ -125,6 +125,16 @@ public async Task DeleteCertificateAsync(string id, CancellationTok } } + public async Task DownloadManifiestoZipUrlAsync(string id, CancellationToken cancellationToken = default) + { + using (var response = await client.GetAsync(Router.ManifiestoDownloadUrl(id), cancellationToken).ConfigureAwait(false)) + { + await this.ThrowIfErrorAsync(response, cancellationToken).ConfigureAwait(false); + var resultString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + return JsonConvert.DeserializeObject(resultString, this.jsonSettings); + } + } + public async Task UpdateLegalAsync(string id, Dictionary data, CancellationToken cancellationToken = default) { using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json")) diff --git a/facturapi-net.csproj b/facturapi-net.csproj index 0ea2c8c..60800c9 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 - 7.1.0 + 7.2.0 $(Version) MIT false