Skip to content

Commit 9fd928c

Browse files
committed
Fix misrouted endpoints and client signatures to match API
1 parent 294f085 commit 9fd928c

12 files changed

+152
-31
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Added
1515

16+
- Expose webhook methods through `FacturapiClient`/`IFacturapiClient`.
17+
- New organization endpoints: `GetCurrentAsync` (`/organizations/me`), `CheckDomainAvailabilityAsync`, `UpdateReceiptsAsync`, and `UpdateDomainAsync`.
18+
- Added `DomainAvailability` model for domain check responses.
19+
- Added `Tool.HealthCheckAsync` for `/check`.
1620
- `FacturapiException.Status` now surfaces the HTTP status code when available.
1721
- Introduced `IFacturapiClient` so consumers can mock the client surface in tests.
1822
- Optional `CancellationToken` parameters on client methods to allow request cancellation from callers.
@@ -21,6 +25,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2125

2226
- `FacturapiClient` now implements `IDisposable`; call `Dispose()` when finished (or wrap in `using`) to release HTTP resources. If not disposed, garbage collection will eventually clean up, but explicit disposal avoids lingering HTTP connections.
2327

28+
### Fixed
29+
30+
- `Invoices.PreviewPdfAsync` now calls the documented POST endpoint with a JSON body (breaking change to the method signature).
31+
- `Receipts.CreateGlobalInvoiceAsync` posts directly to `/receipts/global-invoice` and no longer requires an id (breaking change to the signature).
32+
- Receipt routes now hit `/receipts/{id}` for cancel, invoice, email, and PDF download instead of invoice endpoints.
33+
- `Organizations.CreateSeriesGroupAsync` uses POST (not PUT) to `/organizations/{id}/series-group`, matching the API.
34+
2435
## [4.11.0] - 2025-12-10
2536

2637
### Added

FacturapiClient.cs

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using Facturapi.Wrappers;
2+
using System;
23
using System.Net.Http;
34
using System.Net.Http.Headers;
45
using System.Text;
@@ -7,15 +8,16 @@ namespace Facturapi
78
{
89
public class FacturapiClient : IFacturapiClient
910
{
10-
public Wrappers.CustomerWrapper Customer { get; private set; }
11-
public Wrappers.ProductWrapper Product { get; private set; }
12-
public Wrappers.InvoiceWrapper Invoice { get; private set; }
13-
public Wrappers.OrganizationWrapper Organization { get; private set; }
14-
public Wrappers.ReceiptWrapper Receipt { get; private set; }
15-
public Wrappers.RetentionWrapper Retention { get; private set; }
16-
public Wrappers.CatalogWrapper Catalog { get; private set; }
17-
public Wrappers.CatalogWrapper CartaporteCatalog { get; private set; }
18-
public Wrappers.ToolWrapper Tool { get; private set; }
11+
public CustomerWrapper Customer { get; private set; }
12+
public ProductWrapper Product { get; private set; }
13+
public InvoiceWrapper Invoice { get; private set; }
14+
public OrganizationWrapper Organization { get; private set; }
15+
public ReceiptWrapper Receipt { get; private set; }
16+
public RetentionWrapper Retention { get; private set; }
17+
public CatalogWrapper Catalog { get; private set; }
18+
public CatalogWrapper CartaporteCatalog { get; private set; }
19+
public ToolWrapper Tool { get; private set; }
20+
public WebhookWrapper Webhook { get; private set; }
1921
private readonly HttpClient httpClient;
2022

2123
public FacturapiClient(string apiKey, string apiVersion = "v2")
@@ -27,15 +29,16 @@ public FacturapiClient(string apiKey, string apiVersion = "v2")
2729
};
2830
this.httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", apiKeyBase64);
2931

30-
this.Customer = new Wrappers.CustomerWrapper(apiKey, apiVersion, this.httpClient);
31-
this.Product = new Wrappers.ProductWrapper(apiKey, apiVersion, this.httpClient);
32-
this.Invoice = new Wrappers.InvoiceWrapper(apiKey, apiVersion, this.httpClient);
33-
this.Organization = new Wrappers.OrganizationWrapper(apiKey, apiVersion, this.httpClient);
34-
this.Receipt = new Wrappers.ReceiptWrapper(apiKey, apiVersion, this.httpClient);
35-
this.Retention = new Wrappers.RetentionWrapper(apiKey, apiVersion, this.httpClient);
36-
this.Catalog = new Wrappers.CatalogWrapper(apiKey, apiVersion, this.httpClient);
37-
this.CartaporteCatalog = new Wrappers.CatalogWrapper(apiKey, apiVersion, this.httpClient);
38-
this.Tool = new Wrappers.ToolWrapper(apiKey, apiVersion, this.httpClient);
32+
this.Customer = new CustomerWrapper(apiKey, apiVersion, this.httpClient);
33+
this.Product = new ProductWrapper(apiKey, apiVersion, this.httpClient);
34+
this.Invoice = new InvoiceWrapper(apiKey, apiVersion, this.httpClient);
35+
this.Organization = new OrganizationWrapper(apiKey, apiVersion, this.httpClient);
36+
this.Receipt = new ReceiptWrapper(apiKey, apiVersion, this.httpClient);
37+
this.Retention = new RetentionWrapper(apiKey, apiVersion, this.httpClient);
38+
this.Catalog = new CatalogWrapper(apiKey, apiVersion, this.httpClient);
39+
this.CartaporteCatalog = new CatalogWrapper(apiKey, apiVersion, this.httpClient);
40+
this.Tool = new ToolWrapper(apiKey, apiVersion, this.httpClient);
41+
this.Webhook = new WebhookWrapper(apiKey, apiVersion, this.httpClient);
3942
}
4043

4144
public void Dispose()

IFacturapiClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ public interface IFacturapiClient : IDisposable
1414
CatalogWrapper Catalog { get; }
1515
CatalogWrapper CartaporteCatalog { get; }
1616
ToolWrapper Tool { get; }
17+
WebhookWrapper Webhook { get; }
1718
}
1819
}

Models/DomainAvailability.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Facturapi
2+
{
3+
public class DomainAvailability
4+
{
5+
public bool Available { get; set; }
6+
}
7+
}

Router/HealthRouter.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Facturapi
2+
{
3+
internal static partial class Router
4+
{
5+
public static string HealthCheck()
6+
{
7+
return "check";
8+
}
9+
}
10+
}

Router/InvoiceRouter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ public static string CopyInvoice(string id)
6363
return $"invoices/{id}/copy";
6464
}
6565

66-
public static string PreviewPdf(Dictionary<string, object> query = null)
66+
public static string PreviewPdf()
6767
{
68-
return UriWithQuery("invoices/preview/pdf", query);
68+
return "invoices/preview/pdf";
6969
}
7070
}
7171
}

Router/OrganizationRouter.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ public static string ListOrganizations(Dictionary<string, object> query = null)
1010
return UriWithQuery("organizations", query);
1111
}
1212

13+
public static string OrganizationMe()
14+
{
15+
return "organizations/me";
16+
}
17+
1318
public static string RetrieveOrganization(string id)
1419
{
1520
return $"organizations/{id}";
@@ -25,6 +30,11 @@ public static string DeleteOrganization(string id)
2530
return RetrieveOrganization(id);
2631
}
2732

33+
public static string CheckDomainAvailability(Dictionary<string, object> query = null)
34+
{
35+
return UriWithQuery("organizations/domain-check", query);
36+
}
37+
2838
public static string UpdateLegal(string id)
2939
{
3040
return $"{RetrieveOrganization(id)}/legal";
@@ -40,6 +50,11 @@ public static string UploadLogo(string id)
4050
return $"{RetrieveOrganization(id)}/logo";
4151
}
4252

53+
public static string UpdateReceipts(string id)
54+
{
55+
return $"{RetrieveOrganization(id)}/receipts";
56+
}
57+
4358
public static string UploadCertificate(string id)
4459
{
4560
return $"{RetrieveOrganization(id)}/certificate";
@@ -100,5 +115,10 @@ public static string UpdateSelfInvoiceSettings(string organizationId)
100115
{
101116
return $"{RetrieveOrganization(organizationId)}/self-invoice";
102117
}
118+
119+
public static string UpdateDomain(string organizationId)
120+
{
121+
return $"{RetrieveOrganization(organizationId)}/domain";
122+
}
103123
}
104124
}

Router/ReceiptRouter.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,22 @@ public static string CancelReceipt(string id)
3030

3131
public static string InvoiceReceipt(string id)
3232
{
33-
return $"receipts/(id)/invoice";
33+
return $"receipts/{id}/invoice";
3434
}
3535

36-
public static string CreateGlobalInvoice(string id)
36+
public static string CreateGlobalInvoice()
3737
{
3838
return $"receipts/global-invoice";
3939
}
4040

4141
public static string DownloadReceiptPdf(string id)
4242
{
43-
return $"receipts/(id)/pdf";
43+
return $"receipts/{id}/pdf";
4444
}
4545

4646
public static string SendReceiptByEmail(string id)
4747
{
48-
return $"receipts/(id)/email";
48+
return $"receipts/{id}/email";
4949
}
5050
}
5151
}

Wrappers/InvoiceWrapper.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,10 @@ public async Task<Invoice> CopyToDraftAsync(string id, CancellationToken cancell
168168
}
169169
}
170170

171-
public async Task<Stream> PreviewPdfAsync(Dictionary<string, object> query = null, CancellationToken cancellationToken = default)
171+
public async Task<Stream> PreviewPdfAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default)
172172
{
173-
using (var response = await client.GetAsync(Router.PreviewPdf(query), cancellationToken))
173+
using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"))
174+
using (var response = await client.PostAsync(Router.PreviewPdf(), content, cancellationToken))
174175
{
175176
await this.ThrowIfErrorAsync(response, cancellationToken);
176177
var responseStream = await response.Content.ReadAsStreamAsync();

Wrappers/OrganizationWrapper.cs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,28 @@ public async Task<SearchResult<Organization>> ListAsync(Dictionary<string, objec
2626
}
2727
}
2828

29+
public async Task<Organization> GetCurrentAsync(CancellationToken cancellationToken = default)
30+
{
31+
using (var response = await client.GetAsync(Router.OrganizationMe(), cancellationToken))
32+
{
33+
await this.ThrowIfErrorAsync(response, cancellationToken);
34+
var resultString = await response.Content.ReadAsStringAsync();
35+
var organization = JsonConvert.DeserializeObject<Organization>(resultString, this.jsonSettings);
36+
return organization;
37+
}
38+
}
39+
40+
public async Task<DomainAvailability> CheckDomainAvailabilityAsync(string domain, CancellationToken cancellationToken = default)
41+
{
42+
using (var response = await client.GetAsync(Router.CheckDomainAvailability(new Dictionary<string, object> { ["domain"] = domain }), cancellationToken))
43+
{
44+
await this.ThrowIfErrorAsync(response, cancellationToken);
45+
var resultString = await response.Content.ReadAsStringAsync();
46+
var availability = JsonConvert.DeserializeObject<DomainAvailability>(resultString, this.jsonSettings);
47+
return availability;
48+
}
49+
}
50+
2951
public async Task<Organization> CreateAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default)
3052
{
3153
using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"))
@@ -115,6 +137,18 @@ public async Task<Organization> UpdateLegalAsync(string id, Dictionary<string, o
115137
}
116138
}
117139

140+
public async Task<Organization> UpdateReceiptsAsync(string id, Dictionary<string, object> data, CancellationToken cancellationToken = default)
141+
{
142+
using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"))
143+
using (var response = await client.PutAsync(Router.UpdateReceipts(id), content, cancellationToken))
144+
{
145+
await this.ThrowIfErrorAsync(response, cancellationToken);
146+
var resultString = await response.Content.ReadAsStringAsync();
147+
var organization = JsonConvert.DeserializeObject<Organization>(resultString, this.jsonSettings);
148+
return organization;
149+
}
150+
}
151+
118152
public async Task<Organization> UpdateCustomizationAsync(string id, Dictionary<string, object> data, CancellationToken cancellationToken = default)
119153
{
120154
using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"))
@@ -127,6 +161,18 @@ public async Task<Organization> UpdateCustomizationAsync(string id, Dictionary<s
127161
}
128162
}
129163

164+
public async Task<Organization> UpdateDomainAsync(string id, Dictionary<string, object> data, CancellationToken cancellationToken = default)
165+
{
166+
using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"))
167+
using (var response = await client.PutAsync(Router.UpdateDomain(id), content, cancellationToken))
168+
{
169+
await this.ThrowIfErrorAsync(response, cancellationToken);
170+
var resultString = await response.Content.ReadAsStringAsync();
171+
var organization = JsonConvert.DeserializeObject<Organization>(resultString, this.jsonSettings);
172+
return organization;
173+
}
174+
}
175+
130176
public async Task<string> GetTestApiKeyAsync(string id, CancellationToken cancellationToken = default)
131177
{
132178
using (var response = await client.GetAsync(Router.GetTestApiKey(id), cancellationToken))
@@ -188,7 +234,7 @@ public async Task<List<SeriesGroup>> ListSeriesGroupAsync(string id, Cancellatio
188234
public async Task<SeriesGroup> CreateSeriesGroupAsync(string id, Dictionary<string, object> data, CancellationToken cancellationToken = default)
189235
{
190236
using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"))
191-
using (var response = await client.PutAsync(Router.CreateSeriesGroup(id), content, cancellationToken))
237+
using (var response = await client.PostAsync(Router.CreateSeriesGroup(id), content, cancellationToken))
192238
{
193239
await this.ThrowIfErrorAsync(response, cancellationToken);
194240
var resultString = await response.Content.ReadAsStringAsync();

0 commit comments

Comments
 (0)