Search by

upadrian / ucfe-client

upadrian

PHP client for UCFE (Uruguay Comprobante Fiscal Electrónico / e-Factura) SOAP CfeService

Package info

gitlab.com/upadrian/ucfe

Issues

pkg:composer/upadrian/ucfe-client

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

dev-master 2026-09-17 00:09 UTC

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.

PHP Version License: MIT

📚 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 modelos upadrian/cfe.
    • examples/04_consultar_estado_360.php: Polling de estado y manejo de CodRta 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 de upadrian/cfe, \Stringable, \DOMDocument, \SimpleXMLElement o cadenas XML crudas.
  • Idempotencia Integrada: Generador de UUIDv4 y secuencias numéricas IdReq con 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-soap habilitada
  • Extensión ext-openssl habilitada
  • Extensión ext-dom y ext-simplexml habilitadas
  • Opcional: upadrian/cfe para construcción estructurada de XMLs de CFE
  • Opcional: vlucas/phpdotenv (si utilizas UcfeConfig::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ónMétodo en UcfeClientTipoMensajeDescripción
Prueba de Ecoping() / probarEco()820Verifica conectividad y credenciales con UCFE.
Envío CFE sin FirmarenviarCfeSinFirmar() / sendUnsignedCfe()310Recomendado. UCFE numera, firma y envía a DGI en línea.
Envío Lote sin FirmarenviarLoteSinFirmar() / sendUnsignedBatch()340Envía múltiples CFEs para firma y procesamiento asíncrono en lote.
Validar CFEvalidarCfe() / validateCfe()350Valida la estructura del XML antes de firmar.
Consultar EstadoconsultarEstado() / consultStatus()360Consulta estado por UUID o (Tipo, Serie, Número). Polling tras CodRta 11.
Anular CFEanularCfe() / cancelCfe()320Anula un CFE en bandeja UCFE antes de su transmisión a DGI.
Consultar CAEconsultarCae() / consultCae()230Consulta información y vencimientos de rangos CAE.
Forzar EnsobradoforzarEnsobradoLote() / forceBatchDispatch()500Fuerza el envío inmediato a DGI de comprobantes en lote.
Envío CFE FirmadoenviarCfeFirmado() / sendSignedCfe()300Envía un CFE ya firmado criptográficamente por el emisor.
Envío Lote FirmadoenviarLoteFirmado() / sendSignedBatch()330Envía lote de CFEs ya firmados por el emisor.
Rango de NumeraciónconsultarRangoNumeracion()220Solicita asignación de rangos numéricos.
Reporte de EstadoreportarEstado()800Notifica 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.