Skip to content

Latest commit

 

History

History
130 lines (100 loc) · 8.08 KB

CLAUDE.md

File metadata and controls

130 lines (100 loc) · 8.08 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Official Java SDK for FiscalAPI - a Mexican CFDI electronic invoicing service (SAT integration). Provides CFDI 4.0 invoicing, certificate management, mass downloads, payroll, and SAT catalog queries. Published to Maven Central as com.fiscalapi:fiscalapi.

Build Commands

mvn clean compile          # Compile
mvn package                # Create JAR
mvn clean deploy -Prelease # Deploy to Maven Central (requires GPG + settings.xml credentials)
mvn test                   # JUnit 5 tests in src/test (offline)

No linting or formatting tools are configured.

Tests

src/test/java holds offline JUnit 5 tests (Surefire 3.x, junit-jupiter in test scope, so nothing ships in the jar). FakeApi builds a FiscalApiHttpClient over an OkHttpClient whose interceptor answers every request with a fixed JSON from src/test/resources/fixtures/ (shaped like the API's camelCase responses, enums as integers), so each test runs the real service → HTTP client → Jackson path. ResponseToleranceTest characterizes how the SDK handles API responses with values and fields it does not model yet; a test whose name contains fixedBy or exposedBy pins a known defect and must be inverted when the SDK models them.

Architecture

Entry Point

FiscalApiClient.create(FiscalApiSettings) - Factory method creating the main client with all 11 services.

Service Layer Pattern

IFiscalApiClient (facade)
  ├── getInvoiceService()          → IInvoiceService (cancel, status, getPdf, send, getXml)
  ├── getPersonService()           → IPersonService
  ├── getProductService()          → IProductService
  ├── getTaxFileService()          → ITaxFileService (getDefaultReferences, getDefaultValues)
  ├── getCatalogService()          → ICatalogService (custom search/query)
  ├── getApiKeyService()           → IApiKeyService
  ├── getStampService()            → IStampService (transfer, withdraw; creditType selects stamps or validation credits)
  ├── getSatValidationService()    → ISatValidationService (getTypes, getTypeById, getStatuses, validate)
  ├── getManifestService()         → IManifestService (sign; firma la carta manifiesto con la FIEL)
  ├── getDownloadCatalogService()  → IDownloadCatalogService
  ├── getDownloadRuleService()     → IDownloadRuleService
  └── getDownloadRequestService()  → IDownloadRequestService (cancel, retry, delete)

Generic CRUD Base

Services over a CRUD resource extend BaseFiscalApiService<T> (implements IFiscalApiService<T>), which extends BaseImmutableFiscalApiService<T> (implements IImmutableFiscalApiService<T>):

  • getList(pageNumber, pageSize) → ApiResponse<PagedList<T>>
  • getById(id, details) → ApiResponse<T>
  • create(model) → ApiResponse<T>
  • delete(id) → ApiResponse<Boolean>
  • update(model) → ApiResponse<T> (only in BaseFiscalApiService<T> / IFiscalApiService<T>)

TaxFileService extends only BaseImmutableFiscalApiService<TaxFile> and ITaxFileService extends IImmutableFiscalApiService<TaxFile>: the API does not update certificates (PUT tax-files answers 405), so update() does not exist on the tax-file service.

Subclasses must implement getTypeParameterClass() to return the entity type for Jackson deserialization.

SatValidationService and ManifestService are the exception: sat-validations and manifests are not CRUD resources, so they implement their interfaces directly and build their own endpoint, the same way EmployerService and EmployeeService do for their nested resources. ManifestService.sign posts to manifests and reuses the existing FileResponse model, which already matches the API's file payload.

DTO Hierarchy

SerializableDto (toString() returns pretty-printed JSON)
  → AuditableDto (createdAt, updatedAt: LocalDateTime)
    → BaseDto (id: String)

All models extend BaseDto. Responses wrapped in ApiResponse<T>.

HTTP Layer

  • OkHttpClientFactory - Creates/caches OkHttpClient instances with auth headers (X-API-KEY, X-TENANT-KEY, X-API-VERSION, X-TIME-ZONE). Cache key covers api key, tenant, url, api version and time zone. Default timezone: America/Mexico_City.
  • FiscalApiHttpClient - Wraps OkHttp with Jackson. Deserialization is driven by a Jackson JavaType, so generic shapes keep their element type: get/post/put/delete for single objects, getList/postList for JSON arrays and getPagedList for paged responses. ObjectMapper configured with:
    • JavaTimeModule (LocalDateTime/ZonedDateTime support)
    • FAIL_ON_UNKNOWN_PROPERTIES = false
    • WRITE_BIGDECIMAL_AS_PLAIN = true
    • Non-null serialization inclusion
    • Custom BigDecimalSerializer in serialization/ (avoids scientific notation)

Decimal scale is load-bearing. BigDecimalSerializer writes toPlainString() as a JSON string, so the scale survives the wire. It is not registered globally: every new BigDecimal field needs @JsonSerialize(using = BigDecimalSerializer.class) on the field itself. Losing the trailing zeros makes the PAC reject the CFDI - CCE122 when TotalUSD is not 2 decimals, CFDI40179 when TasaOCuota is not 6. Callers must build values from strings (new BigDecimal("0.160000")), never from a double.

Key Packages

  • abstractions/ - Service interfaces (all prefixed with I)
  • common/ - ApiResponse, PagedList, FiscalApiSettings, BaseDto hierarchy
  • http/ - HTTP client implementation
  • models/ - All DTOs
    • models/invoicing/ - Invoice, InvoiceItem, InvoiceIssuer, InvoiceRecipient, etc.
    • models/invoicing/payroll/ - 13 payroll CFDI types (Payroll, EmployeeData, PayrollEarning, etc.)
    • models/invoicing/paymentComplement/ - Payment complement models
    • models/invoicing/localTaxes/ - Local tax models
    • models/invoicing/foreignTrade/ - Comercio Exterior complement models (all prefixed ComercioExterior* to avoid colliding with the billOfLading types). The emisor address is catalog-based (coloniaId, estadoId, codigoPostalId) while the receptor/destinatario addresses are free text (colonia, estado, codigoPostal) - that asymmetry mirrors the backend and must not be collapsed into one shared type.
    • models/manifests/ - SignManifestRequest (base64Cer, base64Key, password)
    • models/downloading/ - Mass download models
    • models/satValidations/ - SAT validation models and the SatValidationTypeIds / SatValidationStatusIds constants
  • services/ - Service implementations
  • serialization/ - Custom Jackson serializers
  • examples/ - Usage examples (payroll, local taxes, bill of lading, stamps, SAT validations, comercio exterior, manifests). They live in src/main/java, so they ship in the published jar and must compile. Credentials are placeholders (<API_KEY>, <TENANT_KEY>); never commit a real key.

Two Modes of Operation

The SDK supports two invoicing modes (see examples/):

  • By references - Pass entity IDs, server resolves full data
  • By values - Pass complete entity data inline

Configuration

FiscalApiSettings settings = new FiscalApiSettings();
settings.setApiUrl("https://test.fiscalapi.com");  // or https://live.fiscalapi.com
settings.setApiKey("sk_test_...");
settings.setTenant("...");
settings.setDebugMode(true);  // Logs requests/responses to console
// settings.setApiVersion("v4");  // default
// settings.setTimeZone("America/Mexico_City");  // default
FiscalApiClient client = FiscalApiClient.create(settings);

Spring Boot integration: Use @Value properties + @Bean registration (see README.md for full pattern).

API Endpoint Pattern

{apiUrl}/api/{apiVersion}/{resource}/{id?}/{action?}

Example: POST api/v4/invoices, GET api/v4/invoices/{id}?details=true

Deployment

GitHub Actions workflow (.github/workflows/deploy.yml): manual dispatch, builds with Java 8 Temurin, signs with GPG, deploys to Maven Central.

Dependencies

  • OkHttp3 4.12.0 (HTTP)
  • Jackson 2.14.2 + JSR310 module (JSON + Java 8 time)
  • Java 8+ (source/target)