Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
280cdd4
feat: complete java sdk v0 foundation and typed complement models
javorosas Apr 7, 2026
8f4498e
refactor: group complement models into dedicated folders
javorosas Apr 7, 2026
41f5a68
refactor: use accessor-style sdk surface
javorosas Apr 7, 2026
d3c96cd
refactor: adopt java time types for dates
javorosas Apr 7, 2026
4b1afc0
refactor: type remaining code-like fields
javorosas Apr 7, 2026
5e9e2e0
feat: add streaming download helpers
javorosas Apr 7, 2026
9e48d70
refactor: switch organization uploads to file bytes
javorosas Apr 7, 2026
d3f067b
refactor: simplify organization uploads
javorosas Apr 7, 2026
0b5f40e
fix: address copilot review comments
javorosas Apr 7, 2026
205dc04
refactor: use snake case object mappers
javorosas Apr 7, 2026
226d2e2
refactor: remove redundant json property mappings
javorosas Apr 7, 2026
2a9fa53
refactor: drop redundant complement annotation
javorosas Apr 7, 2026
b5a7b10
feat: expose typed api error
javorosas Apr 7, 2026
84546f3
docs: add api error example
javorosas Apr 7, 2026
d63af00
refactor: simplify error surface
javorosas Apr 7, 2026
8fc2de3
ci: add java 25 to test matrix
javorosas Apr 7, 2026
4f30d1b
ci: add maven central publish workflow
javorosas Apr 7, 2026
4235401
refactor: use okhttp for all transports
javorosas Apr 7, 2026
0d4b018
docs: clarify sdk compatibility
javorosas Apr 7, 2026
a0c3eaa
docs: add spanish readme
javorosas Apr 7, 2026
be2e5ca
release: set version to 1.0.0
javorosas Apr 7, 2026
f80a6f5
docs: rename requirements to compatibility
javorosas Apr 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ on:

jobs:
test:
name: Test (Java 11, 17, 21)
name: Test (Java 11, 17, 21, 25)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
java: [11, 17, 21]
java: [11, 17, 21, 25]

steps:
- name: Checkout
Expand Down
104 changes: 104 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: Publish Java SDK

on:
push:
branches:
- main

permissions:
contents: read

jobs:
publish:
name: Publish to Maven Central
runs-on: ubuntu-latest
env:
CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 25
cache: maven
server-id: central
server-username: CENTRAL_USERNAME
server-password: CENTRAL_PASSWORD
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-passphrase: GPG_PASSPHRASE

- name: Check release gate
id: gate
shell: bash
run: |
set -euo pipefail

python3 <<'PY' >> "$GITHUB_OUTPUT"
import sys
import urllib.request
import xml.etree.ElementTree as ET

ns = {"m": "http://maven.apache.org/POM/4.0.0"}
pom = ET.parse("pom.xml").getroot()
version = pom.findtext("m:version", namespaces=ns)
if version is None:
print("publish=false")
print("reason=missing version")
sys.exit(0)

if version.endswith("-SNAPSHOT"):
print("publish=false")
print(f"current_version={version}")
print("reason=snapshot version")
sys.exit(0)

metadata_url = "https://repo.maven.apache.org/maven2/io/facturapi/facturapi-java/maven-metadata.xml"
latest = "0.0.0"
try:
with urllib.request.urlopen(metadata_url, timeout=20) as response:
metadata = ET.fromstring(response.read())
latest = metadata.findtext("./versioning/release") or metadata.findtext("./versioning/latest") or latest
except Exception:
latest = "0.0.0"

def parse_semver(value: str):
core = value.split("+", 1)[0]
main, _, prerelease = core.partition("-")
major, minor, patch = (int(part) for part in main.split(".")[:3])
return major, minor, patch, prerelease

def is_greater(left: str, right: str) -> bool:
left_parts = parse_semver(left)
right_parts = parse_semver(right)
if left_parts[:3] != right_parts[:3]:
return left_parts[:3] > right_parts[:3]
left_pre = left_parts[3]
right_pre = right_parts[3]
if left_pre == right_pre:
return False
if not left_pre:
return True
if not right_pre:
return False
return left_pre > right_pre

publish = is_greater(version, latest)
print(f"current_version={version}")
print(f"latest_version={latest}")
print(f"publish={'true' if publish else 'false'}")
print(f"reason={'current version is newer than published version' if publish else 'published version is current or newer'}")
PY

- name: Publish
if: steps.gate.outputs.publish == 'true'
run: mvn -B -ntp -DskipTests -DpublishRelease=true deploy

- name: Skip publish
if: steps.gate.outputs.publish != 'true'
run: |
echo "Skipping publish: ${{ steps.gate.outputs.reason }} (${{ steps.gate.outputs.current_version }} vs ${{ steps.gate.outputs.latest_version }})"
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Build artifacts
target/

# Local Maven cache override used in this workspace
.m2/

# IDE files
.idea/
*.iml
.vscode/

# OS files
.DS_Store
106 changes: 106 additions & 0 deletions README.es.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# facturapi-java

SDK oficial de Java para [Facturapi](https://www.facturapi.io).

English: [README.md](README.md)

[![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)
[![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)
[![Java](https://img.shields.io/badge/Java-11%2B-ED8B00?style=for-the-badge&logo=openjdk&logoColor=white)](https://openjdk.org/)

## Compatibilidad

- Java 11+
- Kotlin/JVM
- Android 8.0 (API level 26) o superior
- Spring Boot, Jakarta EE, Quarkus, Micronaut y otras apps JVM de servidor

## Instalación

Maven:

```xml
<dependency>
<groupId>io.facturapi</groupId>
<artifactId>facturapi-java</artifactId>
<version>1.0.0</version>
</dependency>
```

Gradle:

```gradle
implementation("io.facturapi:facturapi-java:1.0.0")
```

## Inicio rápido

```java
import io.facturapi.Facturapi;
import java.util.Map;

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

var customer = facturapi.customers().create(Map.of(
"legal_name", "Mi Empresa SA de CV",
"tax_id", "XAXX010101000",
"tax_system", "601",
"email", "cliente@example.com"
), null);

var invoice = facturapi.invoices().create(Map.of(
"customer", customer.getId(),
"items", java.util.List.of(Map.of("quantity", 1, "product", "prod_123"))
), null);

System.out.println(invoice.getId());
```

## Subidas

```java
import java.io.File;

var organization = facturapi.organizations().uploadLogo(
"org_123",
new File("logo.png")
);

var updated = facturapi.organizations().uploadCertificate(
"org_123",
new File("certificate.cer"),
new File("certificate.key"),
"secret"
);
```

## Errores

```java
import io.facturapi.FacturapiException;

try {
facturapi.customers().retrieve("cus_123");
} catch (FacturapiException e) {
System.out.println(e.getMessage());
System.out.println(e.getStatusCode());
System.out.println(e.getErrorCode());
System.out.println(e.getErrorPath());
}
```

## Diseño

- Las entradas usan diccionarios JSON flexibles (`Map<String, Object>`).
- Las salidas son modelos Java tipados (`Invoice`, `Customer`, `SearchResult<T>`, etc.).
- Los errores exponen directamente los campos útiles del error del API en `FacturapiException`.
- La autenticación usa `Authorization: Bearer <apiKey>`.

## Configuración

```java
Facturapi facturapi = Facturapi.builder("sk_test_...")
.apiVersion("v2")
.timeout(java.time.Duration.ofSeconds(20))
.build();
```
76 changes: 64 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,23 @@

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

## Requirements
Español: [README.es.md](README.es.md)

[![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)
[![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)
[![Java](https://img.shields.io/badge/Java-11%2B-ED8B00?style=for-the-badge&logo=openjdk&logoColor=white)](https://openjdk.org/)

## Compatibility

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

## Installation

Maven:

```xml
<dependency>
<groupId>io.facturapi</groupId>
Expand All @@ -17,27 +27,75 @@ Official Java SDK for [Facturapi](https://www.facturapi.io).
</dependency>
```

Gradle:

```gradle
implementation("io.facturapi:facturapi-java:1.0.0")
```

## Quickstart

```java
import com.facturapi.Facturapi;
import io.facturapi.Facturapi;
import java.util.Map;

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

var customer = facturapi.customers.create(Map.of(
var customer = facturapi.customers().create(Map.of(
"legal_name", "Mi Empresa SA de CV",
"tax_id", "XAXX010101000",
"tax_system", "601",
"email", "cliente@example.com"
), null);

var invoice = facturapi.invoices.create(Map.of(
"customer", customer.get("id").asText(),
var invoice = facturapi.invoices().create(Map.of(
"customer", customer.getId(),
"items", java.util.List.of(Map.of("quantity", 1, "product", "prod_123"))
), null);

System.out.println(invoice.getId());
```

## Uploads

```java
import java.io.File;

var organization = facturapi.organizations().uploadLogo(
"org_123",
new File("logo.png")
);

var updated = facturapi.organizations().uploadCertificate(
"org_123",
new File("certificate.cer"),
new File("certificate.key"),
"secret"
);
```

## Errors

```java
import io.facturapi.FacturapiException;

try {
facturapi.customers().retrieve("cus_123");
} catch (FacturapiException e) {
System.out.println(e.getMessage());
System.out.println(e.getStatusCode());
System.out.println(e.getErrorCode());
System.out.println(e.getErrorPath());
}
```

## Design

- Inputs use flexible JSON dictionaries (`Map<String, Object>`).
- Outputs are typed Java models (`Invoice`, `Customer`, `SearchResult<T>`, etc.).
- Errors expose the useful API error fields directly on `FacturapiException`.
- Auth uses `Authorization: Bearer <apiKey>`.

## Configuration

```java
Expand All @@ -46,9 +104,3 @@ Facturapi facturapi = Facturapi.builder("sk_test_...")
.timeout(java.time.Duration.ofSeconds(20))
.build();
```

## Notes

- Uses `Authorization: Bearer <apiKey>`.
- Supports custom base URL for integration testing.
- Includes compatibility aliases (`all`, `del`, `lisLiveApiKeys`).
Loading
Loading