upadrian / ucfe-client
PHP client for UCFE (Uruguay Comprobante Fiscal Electrónico / e-Factura) SOAP CfeService
Requires
- php: >=8.1
- ext-dom: *
- ext-openssl: *
- ext-simplexml: *
- ext-soap: *
Requires (Dev)
- phpunit/phpunit: ^10.0 || ^11.0
- vlucas/phpdotenv: ^5.5
Suggests
- upadrian/cfe: Allows direct instantiation and XML transmission of Uruguay CFE invoice models
- vlucas/phpdotenv: Allows loading configuration directly from .env files with UcfeConfig::fromEnv()
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-09-17 14:32:59 UTC
README
Cliente PHP moderno y fuertemente tipado para consumir la plataforma UCFE (Uruware Comprobante Fiscal Electrónico) de Uruware (Uruguay / DGI) mediante SOAP 1.1 nativo (SoapClient).
Permite transportar, numerar, firmar y enviar a DGI los Comprobantes Fiscales Electrónicos (CFE) generados por upadrian/cfe o cualquier fuente XML.
📚 Documentación y Recursos
- Guía Completa de Integración Uruware UCFE — Conceptos, ciclo de vida de comprobantes, toma de decisiones con
CodRta, reintentos e idempotencia. - Ejemplos Prácticos Ejecutables (
examples/):examples/01_ping_eco.php: Prueba de conectividad con Operación 820.examples/02_enviar_cfe_sin_firmar_310.php: Flujo estándar recomendado de emisión 310.examples/03_integracion_upadrian_cfe.php: Integración directa con modelosupadrian/cfe.examples/04_consultar_estado_360.php: Polling de estado y manejo deCodRta 11(360).examples/05_anulacion_320.php: Anulación de CFE antes de envío a DGI (320).examples/06_envio_lote_340.php: Envío en lote (340) y forzado de ensobrado (500).
Características Principales
- PHP 8.1+ & Tipado Estricto:
declare(strict_types=1)en todas las clases y DTOs. - Alineación con Especificación Oficial de Uruware: Soporte nativo para todas las operaciones UCFE (
310,340,350,360,320,220,230,500,800,820,300,330). - Flujo Recomendado Delegado (310): Permite enviar el CFE sin firmar; UCFE asigna automáticamente Serie, Número correlativo oficial, rango CAE y firma digitalmente.
- Interoperabilidad Fluida con
upadrian/cfe: Acepta directamente objetos CFE deupadrian/cfe,\Stringable,\DOMDocument,\SimpleXMLElemento cadenas XML crudas. - Idempotencia Integrada: Generador de
UUIDv4y secuencias numéricasIdReqcon soporte para reintentos seguros sin duplicar comprobantes fiscales. - Clasificación Semántica de Respuestas (
CodRta): Métodos de conveniencia en DTOs (isSuccess(),isAcceptedPendingDgi(),shouldRetry(),isRejected(),isFormatError()). - Autenticación WS-Security: Inyección automática de cabezales OASIS WS-Security
UsernameToken. - Preservación Automática CDATA: Encapsula el XML en bloques
<![CDATA[...]]>(CfeXmlVar) evitando doble codificación o escapes HTML. - URLs de Consulta PDF: Generación automática de enlaces públicos o autenticados para visualización e impresión del CFE.
Arquitectura de la Integración
graph LR
App[Sistema Emisor / ERP] -->|1. Genera CFE XML| CfePkg[upadrian/cfe]
CfePkg -->|2. Payload CFE| UcfePkg[upadrian/ucfe-client]
UcfePkg -->|3. SOAP Invoke ReqBody| Uruware[Plataforma Uruware UCFE]
Uruware -->|4. Firma digital y envío| DGI[DGI Uruguay]
Uruware -->|5. CodRta + XML Firmado| UcfePkg
UcfePkg -->|6. RespuestaDeUcfe DTO| App
Instalación
Instala el paquete vía Composer:
composer require upadrian/ucfe-client
Requisitos
- PHP 8.1 o superior
- Extensión
ext-soaphabilitada - Extensión
ext-opensslhabilitada - Extensión
ext-domyext-simplexmlhabilitadas - Opcional:
upadrian/cfepara construcción estructurada de XMLs de CFE - Opcional:
vlucas/phpdotenv(si utilizasUcfeConfig::fromEnv())
Configuración
Opción 1: Instanciación Directa
use upadrian\UcfeClient\Config\UcfeConfig;
use upadrian\UcfeClient\Config\UcfeEnvironment;
use upadrian\UcfeClient\UcfeClient;
$config = new UcfeConfig(
codComercio: 'MI_CODIGO_COMERCIO',
codTerminal: 'MI_TERMINAL',
usuario: 'mi_usuario_ws',
pass: 'mi_clave_ws',
environment: UcfeEnvironment::TESTING, // o UcfeEnvironment::PRODUCTION
timeout: 60
);
$client = new UcfeClient($config);
Opción 2: Carga desde Variables de Entorno (.env)
UCFE_COD_COMERCIO=mi_codigo_comercio
UCFE_COD_TERMINAL=mi_terminal
UCFE_USUARIO=mi_usuario_ws
UCFE_PASS=mi_clave_ws
UCFE_ENVIRONMENT=testing
UCFE_TIMEOUT=60
$config = UcfeConfig::fromEnv();
$client = new UcfeClient($config);
Uso Rápido: Flujo Recomendado (310)
use upadrian\UcfeClient\Config\UcfeConfig;
use upadrian\UcfeClient\Support\IdempotencyHelper;
use upadrian\UcfeClient\UcfeClient;
$client = new UcfeClient(UcfeConfig::fromEnv());
// 1. Generar identificadores únicos para idempotencia
$uuid = IdempotencyHelper::generateUuid();
$idReq = IdempotencyHelper::generateIdReq();
// 2. Enviar CFE sin firmar (delegando numeración y firma en UCFE)
$response = $client->enviarCfeSinFirmar(
cfeXml: $xmlCfeOObjeto,
tipoCfe: 101, // 101 = e-Ticket, 111 = e-Factura, etc.
uuid: $uuid,
idReq: $idReq
);
// 3. Evaluar respuesta con métodos semánticos
if ($response->isSuccess()) {
// CodRta 00: Aprobado y confirmado por DGI
$resp = $response->getResp();
if ($resp !== null) {
echo "Serie y Número: " . $resp->getSerie() . "-" . $resp->getNumeroCfe() . PHP_EOL;
echo "CAE: " . $resp->getIdCae() . PHP_EOL;
echo "URL PDF: " . $client->getPdfUrlFromRespBody($response) . PHP_EOL;
// Obtener XML firmado
$xmlFirmado = $resp->getXmlCfeFirmado();
}
} elseif ($response->isAcceptedPendingDgi()) {
// CodRta 11: Aceptado por UCFE pero pendiente de DGI
// NO reenviar el comprobante; consultar más tarde con Operación 360
echo "Comprobante en proceso. Consultar luego con UUID: " . $uuid . PHP_EOL;
} elseif ($response->shouldRetry()) {
// CodRta 03, 89 o 96: Error transitorio
// Reintentar con el MISMO $uuid y MISMO $idReq
echo "Error temporal. Reintentar con mismo UUID e IdReq." . PHP_EOL;
} elseif ($response->isRejected()) {
// CodRta 01 o 05: Rechazado
$resp = $response->getResp();
echo "Rechazado: " . ($resp?->getMensajeRta() ?? 'N/A') . PHP_EOL;
}
Operaciones Disponibles en UcfeClient
| Operación | Método en UcfeClient | TipoMensaje | Descripción |
|---|---|---|---|
| Prueba de Eco | ping() / probarEco() | 820 | Verifica conectividad y credenciales con UCFE. |
| Envío CFE sin Firmar | enviarCfeSinFirmar() / sendUnsignedCfe() | 310 | Recomendado. UCFE numera, firma y envía a DGI en línea. |
| Envío Lote sin Firmar | enviarLoteSinFirmar() / sendUnsignedBatch() | 340 | Envía múltiples CFEs para firma y procesamiento asíncrono en lote. |
| Validar CFE | validarCfe() / validateCfe() | 350 | Valida la estructura del XML antes de firmar. |
| Consultar Estado | consultarEstado() / consultStatus() | 360 | Consulta estado por UUID o (Tipo, Serie, Número). Polling tras CodRta 11. |
| Anular CFE | anularCfe() / cancelCfe() | 320 | Anula un CFE en bandeja UCFE antes de su transmisión a DGI. |
| Consultar CAE | consultarCae() / consultCae() | 230 | Consulta información y vencimientos de rangos CAE. |
| Forzar Ensobrado | forzarEnsobradoLote() / forceBatchDispatch() | 500 | Fuerza el envío inmediato a DGI de comprobantes en lote. |
| Envío CFE Firmado | enviarCfeFirmado() / sendSignedCfe() | 300 | Envía un CFE ya firmado criptográficamente por el emisor. |
| Envío Lote Firmado | enviarLoteFirmado() / sendSignedBatch() | 330 | Envía lote de CFEs ya firmados por el emisor. |
| Rango de Numeración | consultarRangoNumeracion() | 220 | Solicita asignación de rangos numéricos. |
| Reporte de Estado | reportarEstado() | 800 | Notifica periódicamente el estado del punto de emisión. |
Interoperabilidad con upadrian/cfe
El cliente acepta cualquier modelo de comprobante de upadrian/cfe o cualquier objeto que implemente \Stringable, toXml(), asXml(), \DOMDocument o \SimpleXMLElement:
// Modelo de upadrian/cfe u objeto con método toXml() / __toString()
$factura = new MiFacturaElectronica(...);
// Pasaje directo: UcfeClient extrae y valida el XML automáticamente
$response = $client->enviarCfeSinFirmar(
cfeXml: $factura,
tipoCfe: 111
);
Generación de URLs para Impresión de PDF
Permite generar enlaces directos para que los clientes o el sistema de facturación descarguen el PDF oficial:
// Desde una respuesta de UCFE (RespBody)
$pdfUrl = $client->getPdfUrlFromRespBody($response);
// O desde los datos internos de RespuestaDeUcfe
$resp = $response->getResp();
if ($resp !== null) {
$pdfUrl = $client->getPdfUrlFromRespuesta($resp);
}
// O directamente con los parámetros del comprobante
$pdfUrl = $client->getPdfUrl(
tipoCfe: 111,
serie: 'A',
numeroCfe: 12345,
rutEmisor: '219999990018',
codigoSeguridad: 'a1b2c3d4'
);
Ejecución de Tests
composer test
O directamente con PHPUnit:
vendor/bin/phpunit
Licencia
Este proyecto está licenciado bajo la Licencia MIT. Consulta el archivo LICENSE para más información.