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..a25c3b4 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;
@@ -245,18 +245,8 @@ 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();
- }
+ if (codeNode != null && (codeNode.isTextual() || codeNode.isNumber())) {
+ errorCode = codeNode.asText();
}
JsonNode pathNode = firstDefined(root, "path");
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/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());
+ }
}
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());
+ }
}