Spring Boot 3 Java 21 PostgreSQL Resiliencia & Retry Docker
Este informe documenta el diseño y la implementación del motor backend de Novapay, desarrollado con Spring Boot 3.2 y Java 21. La aplicación fue concebida como un middleware centralizado capaz de recibir transacciones comerciales emitidas por dispositivos TPV cliente (tablets Android, pantallas táctiles e interfaces móviles en Flutter) y procesar de forma transparente la firma digital, el encadenamiento de huellas criptográficas SHA-256 y el envío regulatorio hacia las distintas administraciones tributarias españolas.
La elección de Java 21 permite utilizar hilos virtuales (Project Loom) para atender de forma concurrente cientos de conexiones REST simultáneas provenientes de cajas registradoras sin penalización en memoria, mientras que Spring Framework proporciona abstracción para la persistencia relacional relacional con JPA/Hibernate y el empaquetado de servicios web SOAP mediante Spring WS.
Módulo Cripto Java
Implementado & Testeado
Transporte Multi-Hacienda
AEAT SOAP + TicketBAI REST
Infraestructura & Contenedores
Despliegue Multi-Entorno
Desglose porcentual del desarrollo de la aplicación backend. Se priorizó el desarrollo del motor criptográfico de encadenamiento por ser el requisito legal más estricto.
Secuencia técnica ejecutada para construir el ciclo de vida completo de la factura electrónica en el servidor:
Garantía de encadenamiento inalterable mediante hash concatenado de facturas previas.
Controladores REST `@RestController` y servicios de negocio transaccionales `@Service`.
Inscripción inmediata de registros con estado `PENDING` antes de cualquier transmisión externa.
Mecanismo asíncrono `@Scheduled` para la retransmisión autónoma ante caídas de red.
El backend aplica el principio de responsabilidad única. Cuando una aplicación cliente emite una venta, el paquete JSON ingresa por la capa de controladores REST, se transforma a entidades del dominio, se procesa en el servicio de negocio garantizando la integridad de la base de datos y finalmente se deriva al módulo de transporte correspondiente.
Haga clic en cualquiera de las capas de la izquierda para desplegar el fragmento de código Java real y su especificación técnica.
Selecciona una capa a la izquierda para desplegar los detalles de código.
Punto de entrada de peticiones REST HTTP. Desacopla la interfaz de usuario de las reglas tributarias del servidor. Recibe el DTO firmado por la sesión del cajero y delega la ejecución al servicio transaccional.
@PostMapping("/api/v1/invoices")
public ResponseEntity<InvoiceResult> create(@Valid @RequestBody InvoiceDTO invoice) {
// 1. Recepción y validación estricta del Payload JSON
// 2. Delegación síncrona al motor transaccional
InvoiceResult result = invoiceService.processInvoice(invoice);
return ResponseEntity.status(HttpStatus.CREATED).body(result);
}
Orquestador de lógica de negocio y transaccionalidad `@Transactional`. Obtiene el último hash guardado en base de datos para garantizar la secuencia ininterrumpida, genera la huella criptográfica actual y registra la factura en estado `PENDING`.
@Transactional
public InvoiceResult processInvoice(InvoiceDTO data) {
// 1. Obtención del hash inmediatamente anterior en el libro registro
String prevHash = repository.findTopByOrderByIdDesc()
.map(InvoiceEntity::getChainHash)
.orElse("00000000000000000000000000000000");
// 2. Cálculo de la nueva huella SHA-256 encadenada
String newHash = hashGenerator.calculateHash(data, prevHash);
// 3. Firma digital utilizando la clave privada RSA
String signature = signer.sign(newHash, keyManager.getPrivateKey());
// 4. Inserción obligatoria en PostgreSQL en estado PENDING
InvoiceEntity entity = repository.save(new InvoiceEntity(data, newHash, signature, "PENDING"));
// 5. Publicación de evento asíncrono para transmisión a Hacienda
eventPublisher.publishEvent(new FiscalSendEvent(entity.getId()));
return new InvoiceResult(entity.getInvoiceNumber(), newHash, "ACCEPTED_LOCAL");
}
Módulo encargado del cumplimiento estricto del algoritmo SHA-256 de VeriFactu y la firma PKCS#1 v1.5 / XAdES.
HashGenerator.java: Concatena NIF emisor, número de serie, fecha/hora ISO, importe total y hash anterior.Signer.java: Aplica algoritmo SHA256withRSA sobre los datos normalizados.KeyManager.java: Carga dinámica del almacén PKCS#12 (.p12) montado como volumen Docker./app/certs/certificado.p12.
Patrón de diseño Strategy (`FiscalSender`). Permite que el backend intercambie el cliente de transporte según la sede tributaria del contribuyente sin alterar el servicio de facturación.
public interface FiscalSender {
FiscalResponse send(InvoiceEntity invoice);
}
// Adaptador para el territorio nacional (AEAT)
@Component("aeatSender")
public class VerifactuSoapSender implements FiscalSender { ... }
// Adaptador para las haciendas forales vascas (TicketBAI)
@Component("ticketBaiSender")
public class TicketBaiRestSender implements FiscalSender { ... }
Definición DDL del esquema relacional. Almacena las facturas emitidas, los identificadores de cadena criptográfica y la respuesta (CSV) devuelta por la Agencia Tributaria.
CREATE TABLE invoices (
id BIGSERIAL PRIMARY KEY,
invoice_number VARCHAR(50) NOT NULL UNIQUE,
issue_date TIMESTAMP NOT NULL,
total_amount NUMERIC(12, 2) NOT NULL,
chain_hash VARCHAR(64) NOT NULL, -- Huella SHA-256 actual
prev_hash VARCHAR(64) NOT NULL, -- Huella del registro inmediatamente anterior
signature_data TEXT NOT NULL, -- Bloque de firma digital X.509
status VARCHAR(20) DEFAULT 'PENDING', -- PENDING, SENT, REJECTED
aeat_csv VARCHAR(100), -- Código Seguro de Verificación oficial
retry_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
España cuenta con un mapa fiscal descentralizado donde coexisten normativas distintas. El motor de Novapay actúa como un enrutador inteligente: inspecciona el NIF y el código postal del contribuyente para seleccionar automáticamente el empaquetado y el protocolo de red correspondiente.
En el punto de venta (TPV) no es admisible bloquear la pantalla de cobro ni hacer esperar al cliente final si los servidores de la Agencia Tributaria están caídos o sufren latencia. Por ello, Novapay aplica un principio de resiliencia estricto: la transacción se guarda primero en la base de datos PostgreSQL local en estado PENDING y se responde un estado OK_LOCAL a la caja registradora en menos de 50 milisegundos.
La retransmisión hacia los servidores de Hacienda la gestiona un hilo asíncrono secundario programado con el algoritmo de **Espera Incremental (Exponential Backoff)**. Esto evita saturar la red con peticiones consecutivas durante una interrupción de servicio.
Intervalos de espera exponenciales entre intentos (1s, 2s, 4s, 8s, 16s...) para prevenir tormentas de peticiones.
SENT.
Toda la solución se encapsula mediante Docker Compose para garantizar portabilidad. Nginx opera como Proxy Inverso perimetral realizando la terminación SSL HTTPS (certificados Let's Encrypt), filtrando peticiones maliciosas (límite estricto de 56KB por cuerpo para evitar desbordamientos de búfer) y reenviando el tráfico limpio a Spring Boot en el puerto local 8080.
Enlace dinámico de la IP pública mediante DuckDNS para permitir conectividad HTTPS desde las tablets TPV.
Generación y renovación automática mediante Certbot. Previene bloqueos SSL en clientes móviles.
Persistencia fuera del contenedor mediante - ./pgdata:/var/lib/postgresql/data para respaldos sencillos.
Traspaso directo del archivo docker-compose.yml a AWS/Cloud en menos de 15 minutos.