Skip to content

Commit 021e2fc

Browse files
authored
Merge pull request #1 from FacturAPI/feat-complement-typed-models
feat: launch Java SDK foundation with typed complement models
2 parents 52bc6a4 + f80a6f5 commit 021e2fc

162 files changed

Lines changed: 6950 additions & 1403 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ on:
88

99
jobs:
1010
test:
11-
name: Test (Java 11, 17, 21)
11+
name: Test (Java 11, 17, 21, 25)
1212
runs-on: ubuntu-latest
1313
strategy:
1414
fail-fast: false
1515
matrix:
16-
java: [11, 17, 21]
16+
java: [11, 17, 21, 25]
1717

1818
steps:
1919
- name: Checkout

.github/workflows/publish.yml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
name: Publish Java SDK
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
publish:
13+
name: Publish to Maven Central
14+
runs-on: ubuntu-latest
15+
env:
16+
CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
17+
CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
18+
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Setup Java
24+
uses: actions/setup-java@v4
25+
with:
26+
distribution: temurin
27+
java-version: 25
28+
cache: maven
29+
server-id: central
30+
server-username: CENTRAL_USERNAME
31+
server-password: CENTRAL_PASSWORD
32+
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
33+
gpg-passphrase: GPG_PASSPHRASE
34+
35+
- name: Check release gate
36+
id: gate
37+
shell: bash
38+
run: |
39+
set -euo pipefail
40+
41+
python3 <<'PY' >> "$GITHUB_OUTPUT"
42+
import sys
43+
import urllib.request
44+
import xml.etree.ElementTree as ET
45+
46+
ns = {"m": "http://maven.apache.org/POM/4.0.0"}
47+
pom = ET.parse("pom.xml").getroot()
48+
version = pom.findtext("m:version", namespaces=ns)
49+
if version is None:
50+
print("publish=false")
51+
print("reason=missing version")
52+
sys.exit(0)
53+
54+
if version.endswith("-SNAPSHOT"):
55+
print("publish=false")
56+
print(f"current_version={version}")
57+
print("reason=snapshot version")
58+
sys.exit(0)
59+
60+
metadata_url = "https://repo.maven.apache.org/maven2/io/facturapi/facturapi-java/maven-metadata.xml"
61+
latest = "0.0.0"
62+
try:
63+
with urllib.request.urlopen(metadata_url, timeout=20) as response:
64+
metadata = ET.fromstring(response.read())
65+
latest = metadata.findtext("./versioning/release") or metadata.findtext("./versioning/latest") or latest
66+
except Exception:
67+
latest = "0.0.0"
68+
69+
def parse_semver(value: str):
70+
core = value.split("+", 1)[0]
71+
main, _, prerelease = core.partition("-")
72+
major, minor, patch = (int(part) for part in main.split(".")[:3])
73+
return major, minor, patch, prerelease
74+
75+
def is_greater(left: str, right: str) -> bool:
76+
left_parts = parse_semver(left)
77+
right_parts = parse_semver(right)
78+
if left_parts[:3] != right_parts[:3]:
79+
return left_parts[:3] > right_parts[:3]
80+
left_pre = left_parts[3]
81+
right_pre = right_parts[3]
82+
if left_pre == right_pre:
83+
return False
84+
if not left_pre:
85+
return True
86+
if not right_pre:
87+
return False
88+
return left_pre > right_pre
89+
90+
publish = is_greater(version, latest)
91+
print(f"current_version={version}")
92+
print(f"latest_version={latest}")
93+
print(f"publish={'true' if publish else 'false'}")
94+
print(f"reason={'current version is newer than published version' if publish else 'published version is current or newer'}")
95+
PY
96+
97+
- name: Publish
98+
if: steps.gate.outputs.publish == 'true'
99+
run: mvn -B -ntp -DskipTests -DpublishRelease=true deploy
100+
101+
- name: Skip publish
102+
if: steps.gate.outputs.publish != 'true'
103+
run: |
104+
echo "Skipping publish: ${{ steps.gate.outputs.reason }} (${{ steps.gate.outputs.current_version }} vs ${{ steps.gate.outputs.latest_version }})"

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Build artifacts
2+
target/
3+
4+
# Local Maven cache override used in this workspace
5+
.m2/
6+
7+
# IDE files
8+
.idea/
9+
*.iml
10+
.vscode/
11+
12+
# OS files
13+
.DS_Store

README.es.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# facturapi-java
2+
3+
SDK oficial de Java para [Facturapi](https://www.facturapi.io).
4+
5+
English: [README.md](README.md)
6+
7+
[![CI](https://img.shields.io/github/actions/workflow/status/facturapi/facturapi-java/ci.yml?branch=main&style=for-the-badge&label=CI)](https://github.com/facturapi/facturapi-java/actions/workflows/ci.yml)
8+
[![Maven Central](https://img.shields.io/maven-central/v/io.facturapi/facturapi-java?style=for-the-badge&label=Maven%20Central)](https://central.sonatype.com/artifact/io.facturapi/facturapi-java)
9+
[![Java](https://img.shields.io/badge/Java-11%2B-ED8B00?style=for-the-badge&logo=openjdk&logoColor=white)](https://openjdk.org/)
10+
11+
## Compatibilidad
12+
13+
- Java 11+
14+
- Kotlin/JVM
15+
- Android 8.0 (API level 26) o superior
16+
- Spring Boot, Jakarta EE, Quarkus, Micronaut y otras apps JVM de servidor
17+
18+
## Instalación
19+
20+
Maven:
21+
22+
```xml
23+
<dependency>
24+
<groupId>io.facturapi</groupId>
25+
<artifactId>facturapi-java</artifactId>
26+
<version>1.0.0</version>
27+
</dependency>
28+
```
29+
30+
Gradle:
31+
32+
```gradle
33+
implementation("io.facturapi:facturapi-java:1.0.0")
34+
```
35+
36+
## Inicio rápido
37+
38+
```java
39+
import io.facturapi.Facturapi;
40+
import java.util.Map;
41+
42+
Facturapi facturapi = new Facturapi("sk_test_...");
43+
44+
var customer = facturapi.customers().create(Map.of(
45+
"legal_name", "Mi Empresa SA de CV",
46+
"tax_id", "XAXX010101000",
47+
"tax_system", "601",
48+
"email", "cliente@example.com"
49+
), null);
50+
51+
var invoice = facturapi.invoices().create(Map.of(
52+
"customer", customer.getId(),
53+
"items", java.util.List.of(Map.of("quantity", 1, "product", "prod_123"))
54+
), null);
55+
56+
System.out.println(invoice.getId());
57+
```
58+
59+
## Subidas
60+
61+
```java
62+
import java.io.File;
63+
64+
var organization = facturapi.organizations().uploadLogo(
65+
"org_123",
66+
new File("logo.png")
67+
);
68+
69+
var updated = facturapi.organizations().uploadCertificate(
70+
"org_123",
71+
new File("certificate.cer"),
72+
new File("certificate.key"),
73+
"secret"
74+
);
75+
```
76+
77+
## Errores
78+
79+
```java
80+
import io.facturapi.FacturapiException;
81+
82+
try {
83+
facturapi.customers().retrieve("cus_123");
84+
} catch (FacturapiException e) {
85+
System.out.println(e.getMessage());
86+
System.out.println(e.getStatusCode());
87+
System.out.println(e.getErrorCode());
88+
System.out.println(e.getErrorPath());
89+
}
90+
```
91+
92+
## Diseño
93+
94+
- Las entradas usan diccionarios JSON flexibles (`Map<String, Object>`).
95+
- Las salidas son modelos Java tipados (`Invoice`, `Customer`, `SearchResult<T>`, etc.).
96+
- Los errores exponen directamente los campos útiles del error del API en `FacturapiException`.
97+
- La autenticación usa `Authorization: Bearer <apiKey>`.
98+
99+
## Configuración
100+
101+
```java
102+
Facturapi facturapi = Facturapi.builder("sk_test_...")
103+
.apiVersion("v2")
104+
.timeout(java.time.Duration.ofSeconds(20))
105+
.build();
106+
```

README.md

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,23 @@
22

33
Official Java SDK for [Facturapi](https://www.facturapi.io).
44

5-
## Requirements
5+
Español: [README.es.md](README.es.md)
6+
7+
[![CI](https://img.shields.io/github/actions/workflow/status/facturapi/facturapi-java/ci.yml?branch=main&style=for-the-badge&label=CI)](https://github.com/facturapi/facturapi-java/actions/workflows/ci.yml)
8+
[![Maven Central](https://img.shields.io/maven-central/v/io.facturapi/facturapi-java?style=for-the-badge&label=Maven%20Central)](https://central.sonatype.com/artifact/io.facturapi/facturapi-java)
9+
[![Java](https://img.shields.io/badge/Java-11%2B-ED8B00?style=for-the-badge&logo=openjdk&logoColor=white)](https://openjdk.org/)
10+
11+
## Compatibility
612

713
- Java 11+
8-
- Maven 3.8+
14+
- Kotlin/JVM
15+
- Android 8.0 (API level 26) o superior
16+
- Spring Boot, Jakarta EE, Quarkus, Micronaut, and other JVM server apps
917

1018
## Installation
1119

20+
Maven:
21+
1222
```xml
1323
<dependency>
1424
<groupId>io.facturapi</groupId>
@@ -17,27 +27,75 @@ Official Java SDK for [Facturapi](https://www.facturapi.io).
1727
</dependency>
1828
```
1929

30+
Gradle:
31+
32+
```gradle
33+
implementation("io.facturapi:facturapi-java:1.0.0")
34+
```
35+
2036
## Quickstart
2137

2238
```java
23-
import com.facturapi.Facturapi;
39+
import io.facturapi.Facturapi;
2440
import java.util.Map;
2541

2642
Facturapi facturapi = new Facturapi("sk_test_...");
2743

28-
var customer = facturapi.customers.create(Map.of(
44+
var customer = facturapi.customers().create(Map.of(
2945
"legal_name", "Mi Empresa SA de CV",
3046
"tax_id", "XAXX010101000",
3147
"tax_system", "601",
3248
"email", "cliente@example.com"
3349
), null);
3450

35-
var invoice = facturapi.invoices.create(Map.of(
36-
"customer", customer.get("id").asText(),
51+
var invoice = facturapi.invoices().create(Map.of(
52+
"customer", customer.getId(),
3753
"items", java.util.List.of(Map.of("quantity", 1, "product", "prod_123"))
3854
), null);
55+
56+
System.out.println(invoice.getId());
57+
```
58+
59+
## Uploads
60+
61+
```java
62+
import java.io.File;
63+
64+
var organization = facturapi.organizations().uploadLogo(
65+
"org_123",
66+
new File("logo.png")
67+
);
68+
69+
var updated = facturapi.organizations().uploadCertificate(
70+
"org_123",
71+
new File("certificate.cer"),
72+
new File("certificate.key"),
73+
"secret"
74+
);
3975
```
4076

77+
## Errors
78+
79+
```java
80+
import io.facturapi.FacturapiException;
81+
82+
try {
83+
facturapi.customers().retrieve("cus_123");
84+
} catch (FacturapiException e) {
85+
System.out.println(e.getMessage());
86+
System.out.println(e.getStatusCode());
87+
System.out.println(e.getErrorCode());
88+
System.out.println(e.getErrorPath());
89+
}
90+
```
91+
92+
## Design
93+
94+
- Inputs use flexible JSON dictionaries (`Map<String, Object>`).
95+
- Outputs are typed Java models (`Invoice`, `Customer`, `SearchResult<T>`, etc.).
96+
- Errors expose the useful API error fields directly on `FacturapiException`.
97+
- Auth uses `Authorization: Bearer <apiKey>`.
98+
4199
## Configuration
42100

43101
```java
@@ -46,9 +104,3 @@ Facturapi facturapi = Facturapi.builder("sk_test_...")
46104
.timeout(java.time.Duration.ofSeconds(20))
47105
.build();
48106
```
49-
50-
## Notes
51-
52-
- Uses `Authorization: Bearer <apiKey>`.
53-
- Supports custom base URL for integration testing.
54-
- Includes compatibility aliases (`all`, `del`, `lisLiveApiKeys`).

0 commit comments

Comments
 (0)