From 987ffa787bfe61909fa2c84ea3c565cda8a3a03c Mon Sep 17 00:00:00 2001 From: javorosas Date: Fri, 17 Jul 2026 16:18:44 +0200 Subject: [PATCH 1/3] fix!: return Java API error codes as strings --- CHANGELOG.md | 6 ++++++ pom.xml | 2 +- .../java/io/facturapi/FacturapiException.java | 8 ++++---- .../facturapi/http/FacturapiHttpClient.java | 14 ++------------ .../io/facturapi/FacturapiHttpClientTest.java | 19 +++++++++++++++++++ 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba72fd2..3024348 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). +## [2.0.0] - 2026-07-17 + +### Changed + +- Change `FacturapiException.getErrorCode()` to return strings, including converted legacy numeric codes. + ## [1.3.0] - 2026-06-07 ### Added diff --git a/pom.xml b/pom.xml index 4235f4a..a69187f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.facturapi facturapi-java - 1.3.0 + 2.0.0 facturapi-java Official Java SDK for Facturapi https://github.com/facturapi/facturapi-java diff --git a/src/main/java/io/facturapi/FacturapiException.java b/src/main/java/io/facturapi/FacturapiException.java index 0e0839c..482baed 100644 --- a/src/main/java/io/facturapi/FacturapiException.java +++ b/src/main/java/io/facturapi/FacturapiException.java @@ -7,7 +7,7 @@ public class FacturapiException extends RuntimeException { private final int statusCode; - private final Object errorCode; + private final String errorCode; private final String errorPath; private final String errorLocation; private final JsonNode errors; @@ -29,14 +29,14 @@ public FacturapiException(String message, Throwable cause) { this.headers = Collections.emptyMap(); } - public FacturapiException(String message, int statusCode, Object errorCode, String errorPath) { + public FacturapiException(String message, int statusCode, String errorCode, String errorPath) { this(message, statusCode, errorCode, errorPath, null, null, null, Collections.emptyMap()); } public FacturapiException( String message, int statusCode, - Object errorCode, + String errorCode, String errorPath, String errorLocation, JsonNode errors, @@ -57,7 +57,7 @@ public int getStatusCode() { return statusCode; } - public Object getErrorCode() { + public String getErrorCode() { return errorCode; } diff --git a/src/main/java/io/facturapi/http/FacturapiHttpClient.java b/src/main/java/io/facturapi/http/FacturapiHttpClient.java index 0fee0d7..b0febcb 100644 --- a/src/main/java/io/facturapi/http/FacturapiHttpClient.java +++ b/src/main/java/io/facturapi/http/FacturapiHttpClient.java @@ -210,7 +210,7 @@ private static JsonNode firstDefined(JsonNode node, String... keys) { private FacturapiException buildApiException(String bodyText, Response response) { int statusCode = response.code(); int resolvedStatus = statusCode; - Object errorCode = null; + String errorCode = null; String errorPath = null; String errorLocation = null; JsonNode errors = null; @@ -246,17 +246,7 @@ private FacturapiException buildApiException(String bodyText, Response response) JsonNode codeNode = firstDefined(root, "code"); if (codeNode != null && !codeNode.isNull()) { - if (codeNode.isTextual()) { - errorCode = codeNode.asText(); - } else if (codeNode.isIntegralNumber()) { - errorCode = codeNode.intValue(); - } else if (codeNode.isNumber()) { - errorCode = codeNode.numberValue(); - } else if (codeNode.isBoolean()) { - errorCode = codeNode.asBoolean(); - } else { - errorCode = codeNode.toString(); - } + errorCode = codeNode.isValueNode() ? codeNode.asText() : codeNode.toString(); } JsonNode pathNode = firstDefined(root, "path"); diff --git a/src/test/java/io/facturapi/FacturapiHttpClientTest.java b/src/test/java/io/facturapi/FacturapiHttpClientTest.java index 4917970..e8d6eac 100644 --- a/src/test/java/io/facturapi/FacturapiHttpClientTest.java +++ b/src/test/java/io/facturapi/FacturapiHttpClientTest.java @@ -77,4 +77,23 @@ void throwsFacturapiExceptionWithApiMessage() { assertEquals("3", ex.getHeaders().get("Retry-After").get(0)); assertEquals("log_123", ex.getHeaders().get("x-facturapi-log-id").get(0)); } + + @Test + void convertsNumericApiErrorCodesToStrings() { + StubHttpClient httpClient = new StubHttpClient(); + httpClient.enqueueJson(400, "{\"message\":\"Invalid customer\",\"code\":400}"); + + FacturapiHttpClient client = new FacturapiHttpClient( + FacturapiConfig.builder("sk_test_123") + .httpClient(httpClient.client()) + .build() + ); + + FacturapiException ex = assertThrows( + FacturapiException.class, + () -> client.get("/customers/cus_1", null, GenericResponse.class) + ); + + assertEquals("400", ex.getErrorCode()); + } } From 8119a344ebce958dcbc7ba83e13451dda1b2daf9 Mon Sep 17 00:00:00 2001 From: javorosas Date: Fri, 17 Jul 2026 16:21:26 +0200 Subject: [PATCH 2/3] fix: only normalize numeric API error codes --- src/main/java/io/facturapi/http/FacturapiHttpClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/facturapi/http/FacturapiHttpClient.java b/src/main/java/io/facturapi/http/FacturapiHttpClient.java index b0febcb..a25c3b4 100644 --- a/src/main/java/io/facturapi/http/FacturapiHttpClient.java +++ b/src/main/java/io/facturapi/http/FacturapiHttpClient.java @@ -245,8 +245,8 @@ private FacturapiException buildApiException(String bodyText, Response response) } JsonNode codeNode = firstDefined(root, "code"); - if (codeNode != null && !codeNode.isNull()) { - errorCode = codeNode.isValueNode() ? codeNode.asText() : codeNode.toString(); + if (codeNode != null && (codeNode.isTextual() || codeNode.isNumber())) { + errorCode = codeNode.asText(); } JsonNode pathNode = firstDefined(root, "path"); From 337d2e5e45c0444177b47ebb490960648be478bb Mon Sep 17 00:00:00 2001 From: javorosas Date: Fri, 17 Jul 2026 16:45:35 +0200 Subject: [PATCH 3/3] fix: model property tax accounts as arrays --- .../java/io/facturapi/models/InvoiceItem.java | 9 ++++++--- .../io/facturapi/FacturapiResourcesTest.java | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/facturapi/models/InvoiceItem.java b/src/main/java/io/facturapi/models/InvoiceItem.java index adcb675..dd35db8 100644 --- a/src/main/java/io/facturapi/models/InvoiceItem.java +++ b/src/main/java/io/facturapi/models/InvoiceItem.java @@ -14,7 +14,8 @@ public class InvoiceItem { private InvoiceItemThirdParty thirdParty; private String complement; private List parts = new ArrayList<>(); - private String propertyTaxAccount; + @JsonProperty("property_tax_account") + private List propertyTaxAccounts = new ArrayList<>(); public Double getQuantity() { return quantity; } public void setQuantity(Double quantity) { this.quantity = quantity; } @@ -30,6 +31,8 @@ public class InvoiceItem { public void setComplement(String complement) { this.complement = complement; } public List getParts() { return parts; } public void setParts(List parts) { this.parts = parts; } - public String getPropertyTaxAccount() { return propertyTaxAccount; } - public void setPropertyTaxAccount(String propertyTaxAccount) { this.propertyTaxAccount = propertyTaxAccount; } + @JsonProperty("property_tax_account") + public List getPropertyTaxAccounts() { return propertyTaxAccounts; } + @JsonProperty("property_tax_account") + public void setPropertyTaxAccounts(List propertyTaxAccounts) { this.propertyTaxAccounts = propertyTaxAccounts; } } diff --git a/src/test/java/io/facturapi/FacturapiResourcesTest.java b/src/test/java/io/facturapi/FacturapiResourcesTest.java index b523184..18ae467 100644 --- a/src/test/java/io/facturapi/FacturapiResourcesTest.java +++ b/src/test/java/io/facturapi/FacturapiResourcesTest.java @@ -16,10 +16,12 @@ import io.facturapi.enums.Taxability; import io.facturapi.http.FacturapiConfig; import io.facturapi.models.Customer; +import io.facturapi.models.InvoiceItem; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.LocalDate; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; @@ -320,4 +322,18 @@ void objectMapperDeserializesCodeEnums() throws Exception { assertEquals(TaxType.IEPS, tax.getType()); assertEquals(TaxFactor.EXENTO, tax.getFactor()); } + + @Test + void objectMapperDeserializesPropertyTaxAccountsAsArrays() throws Exception { + var mapper = FacturapiConfig.builder("sk_test").build().getObjectMapper(); + + var empty = mapper.readValue("{\"property_tax_account\":[]}", InvoiceItem.class); + var accounts = mapper.readValue( + "{\"property_tax_account\":[\"0102030405\"]}", + InvoiceItem.class + ); + + assertEquals(List.of(), empty.getPropertyTaxAccounts()); + assertEquals(List.of("0102030405"), accounts.getPropertyTaxAccounts()); + } }