Skip to content

SSD-Smart-Software-Development-SRL/ecf-dgii-php

Repository files navigation

ECF DGII PHP SDK

Packagist Version Packagist Downloads License

PHP SDK para integrar la facturacion electronica (e-CF) en Republica Dominicana a traves de ECF SSD.

Packagist Version Packagist Downloads CI License


Que es ECF SSD

ECF SSD es una plataforma que simplifica la emision de comprobantes fiscales electronicos (e-CF) en Republica Dominicana. En vez de que cada empresa implemente todo el proceso de comunicacion con la DGII (firmado XML, autenticacion por semilla, manejo de certificados, reintentos, almacenamiento, etc.), ECF SSD lo hace por ti.

Tu solo envias el comprobante en JSON a traves de este SDK, y el servicio se encarga de:

  • Firmar el comprobante electronicamente (XML signing con tu certificado digital)
  • Autenticar con la DGII (semilla -> token)
  • Enviar el e-CF a la DGII
  • Validar emisor-receptor
  • Almacenar los comprobantes de forma segura
  • Reintentar automaticamente en caso de fallos
  • Gestionar tus certificados digitales

Usas WooCommerce? Tenemos un plugin listo para facturacion electronica sin escribir codigo.

Buscas otro lenguaje? Tambien disponible en .NET, TypeScript, React, Python, Java, Kotlin, Swift y C++. Ver todos los SDKs y plugins.

Instalacion

composer require ecfx/ecf-dgii-php

Requisitos

  • PHP 8.1+
  • ext-curl
  • ext-json
  • ext-mbstring

Como Empezar

Paso 1: Registrate en ECF SSD

Visita https://ecf.ssd.com.do y crea tu cuenta.

Paso 2: Obten tu API Key

  1. Una vez logueado, selecciona el ambiente Test en el selector de ambientes (esquina superior derecha)
  2. Ve a Settings > API Key > Generate API Key
  3. Copia tu API Key -- la usaras como bearer token para autenticarte con el SDK

Paso 3: Contacta a ECF SSD para la Integracion

Contacta al equipo de ECF SSD (contacto@ssd.com.do) para iniciar el proceso de integracion y certificacion de tu sistema (no de las empresas de tus clientes -- la certificacion es para tu plataforma). El equipo te guiara a traves del proceso usando el ambiente de certificacion (Cert).

Paso 4: Usa Produccion

Una vez que tu sistema este integrado y certificado, podras usar el ambiente de produccion (Prod) para emitir comprobantes fiscales electronicos reales.

Vienes de otro proveedor? ECF SSD soporta migracion desde otros proveedores de facturacion electronica. Contacta al equipo para coordinar la transicion.

Configuracion

use Ecfx\EcfDgii\Configuration;

$config = Configuration::getDefaultConfiguration()
    ->setHost('https://api.test.ecfx.ssd.com.do')
    ->setAccessToken('your-api-key-here');

Ambientes

Ambiente URL Uso
Test https://api.test.ecfx.ssd.com.do Desarrollo y pruebas internas
Cert https://api.cert.ecfx.ssd.com.do Proceso de certificacion con la DGII
Prod https://api.prod.ecfx.ssd.com.do Produccion

Autenticacion

El SDK utiliza JWT Bearer Token. Puedes configurar el token de dos formas:

  1. Directamente en el codigo: pasando el API Key con setAccessToken()
  2. Variable de entorno: ECF_DGII_TOKEN

Ejemplo: Factura de Consumo (ECF 32)

Cada tipo de ECF tiene su propio conjunto de clases con el prefijo del tipo (ej. Ecf32ECF, Ecf32Encabezado, Ecf32Item para Factura de Consumo).

<?php

require_once 'vendor/autoload.php';

use GuzzleHttp\Client;
use Ecfx\EcfDgii\Configuration;
use Ecfx\EcfDgii\Api\EcfApi;
use Ecfx\EcfDgii\ApiException;
use Ecfx\EcfDgii\Model\Ecf32ECF;
use Ecfx\EcfDgii\Model\Ecf32Encabezado;
use Ecfx\EcfDgii\Model\Ecf32IdDoc;
use Ecfx\EcfDgii\Model\Ecf32Emisor;
use Ecfx\EcfDgii\Model\Ecf32Comprador;
use Ecfx\EcfDgii\Model\Ecf32Totales;
use Ecfx\EcfDgii\Model\Ecf32Item;
use Ecfx\EcfDgii\Model\Ecf32FormaDePago;
use Ecfx\EcfDgii\Model\Ecf32VersionType;
use Ecfx\EcfDgii\Model\Ecf32TipoPagoType;
use Ecfx\EcfDgii\Model\Ecf32FormaPagoType;
use Ecfx\EcfDgii\Model\Ecf32IndicadorFacturacionType;
use Ecfx\EcfDgii\Model\Ecf32IndicadorBienoServicioType;
use Ecfx\EcfDgii\Model\Ecf32TipoIngresosValidationType;
use Ecfx\EcfDgii\Model\TipoeCFType;
use Ecfx\EcfDgii\Model\IndicadorMontoGravadoType;
use Ecfx\EcfDgii\Model\UnidadMedidaType;

// 1. Configurar
$config = Configuration::getDefaultConfiguration()
    ->setHost('https://api.test.ecfx.ssd.com.do')
    ->setAccessToken(getenv('ECF_DGII_TOKEN'));

// 2. Crear el cliente API
$ecfApi = new EcfApi(new Client(), $config);

// 3. Construir el ECF 32 (Factura de Consumo)
$ecf = new Ecf32ECF([
    'encabezado' => new Ecf32Encabezado([
        'version' => Ecf32VersionType::VERSION1_0,
        'id_doc' => new Ecf32IdDoc([
            'tipoe_cf' => TipoeCFType::FACTURA_DE_CONSUMO_ELECTRONICA,
            'encf' => 'E320000000019',
            'indicador_monto_gravado' => IndicadorMontoGravadoType::CON_ITBIS_INCLUIDO,
            'tipo_ingresos' => Ecf32TipoIngresosValidationType::_01,
            'tipo_pago' => Ecf32TipoPagoType::CONTADO,
            'tabla_formas_pago' => [
                new Ecf32FormaDePago([
                    'forma_pago' => Ecf32FormaPagoType::EFECTIVO,
                    'monto_pago' => 590,
                ]),
            ],
        ]),
        'emisor' => new Ecf32Emisor([
            'rnc_emisor' => 'TODO_YOUR_RNC',           // TODO: Reemplaza con tu RNC
            'razon_social_emisor' => 'TODO_YOUR_RAZON_SOCIAL', // TODO: Reemplaza con tu razon social
            'direccion_emisor' => 'TODO_YOUR_ADDRESS',  // TODO: Reemplaza con tu direccion
            'numero_factura_interna' => '21',
            'fecha_emision' => new \DateTime('2026-03-13'),
        ]),
        'comprador' => new Ecf32Comprador(),
        'totales' => new Ecf32Totales([
            'monto_gravado_total' => 500,
            'monto_gravado_i1' => 500,
            'itbi_s1' => 18,
            'total_itbis' => 90,
            'total_itbis1' => 90,
            'monto_total' => 590,
            'monto_periodo' => 590,
        ]),
    ]),
    'detalles_items' => [
        new Ecf32Item([
            'numero_linea' => 1,
            'indicador_facturacion' => Ecf32IndicadorFacturacionType::ITBIS1_18_PERCENT,
            'nombre_item' => 'Servicio Digital',
            'indicador_bieno_servicio' => Ecf32IndicadorBienoServicioType::SERVICIO,
            'cantidad_item' => 1,
            'unidad_medida' => UnidadMedidaType::UNIDAD,
            'precio_unitario_item' => 500,
            'monto_item' => 500,
        ]),
    ],
]);

// 4. Enviar el ECF
try {
    $result = $ecfApi->recepcionEcf32($ecf);

    echo "ECF enviado!\n";
    echo "Message ID: " . $result->getMessageId() . "\n";
} catch (ApiException $e) {
    echo "Error: " . $e->getMessage() . "\n";
    echo "Response: " . $e->getResponseBody() . "\n";
}

Usando EcfService (auto-routing + polling)

El EcfService enruta automaticamente al endpoint correcto segun el tipo de ECF y hace polling hasta que la DGII termine de procesar:

use Ecfx\EcfDgii\EcfService;
use Ecfx\EcfDgii\EcfProcessingException;
use Ecfx\EcfDgii\EcfPollingTimeoutException;

$service = new EcfService($ecfApi, pollingMaxAttempts: 30, pollingIntervalSeconds: 2);

try {
    $result = $service->sendEcf($ecf);

    echo "ECF aceptado!\n";
    echo "Status: " . $result->getEstatus() . "\n";
    echo "ENCF: " . $result->getEncf() . "\n";
} catch (EcfProcessingException $e) {
    echo "ECF rechazado: " . $e->getMessage() . "\n";
} catch (EcfPollingTimeoutException $e) {
    echo "Polling timeout: " . $e->getMessage() . "\n";
}

Como Funciona sendEcf

Tu sistema                          ECF SSD                         DGII
   |                                   |                              |
   |-- sendEcf(ecf) ----------------->|                              |
   |                                   |-- firma XML ----------------|
   |                                   |-- autenticacion (semilla) ->|
   |                                   |-- envio e-CF -------------->|
   |                                   |<-- trackId ----------------|
   |                                   |-- polling estado ---------->|
   |                                   |<-- resultado final --------|
   |<-- EcfResponse ------------------|                              |
   |                                   |                              |
  1. Enrutamiento automatico: Determina el endpoint correcto (/ecf/31, /ecf/32, etc.) basandose en el tipoeCF
  2. Envio: POST al endpoint correspondiente
  3. Polling: Consulta periodicamente el estado hasta que la DGII responda (Finished o Error)
  4. Resultado: Retorna el EcfResponse con el estado final, codigo de seguridad, URL de impresion, etc.

Respuesta (EcfResponse)

Al completarse el envio, recibes un EcfResponse con los datos necesarios para cumplir con los requisitos de impresion de la DGII:

Campo Descripcion
ImpresionUrl URL para generar el codigo QR requerido por la DGII en el comprobante impreso
CodSec Codigo de seguridad -- debe aparecer en el comprobante impreso
FechaFirma Fecha y hora de la firma digital del comprobante
Estatus Estado DGII: Aceptado, AceptadoCondicional, Rechazado
Progress Estado del procesamiento: Queued, Sending, Polling, Finished, Error
Encf Numero de comprobante fiscal electronico (eNCF)
Mensaje Mensaje de respuesta de la DGII
Errors Detalle de errores (si los hay)
MontoTotal Monto total del comprobante

QR e Impresion

La DGII requiere que todo comprobante impreso incluya un codigo QR. El campo ImpresionUrl contiene la URL que debe codificarse como QR. Adicionalmente, el CodSec (codigo de seguridad) y la FechaFirma deben aparecer impresos en el comprobante.

$result = $service->sendEcf($ecf);

$urlQr = $result->getImpresionUrl();         // codificar como QR
$codigoSeguridad = $result->getCodSec();     // imprimir en el comprobante
$fechaFirma = $result->getFechaFirma();      // fecha de firma digital

Tipos de Comprobantes (ECF)

Cada tipo de ECF tiene su propio conjunto de clases. Usa las clases que correspondan a tu tipo de documento:

Tipo Prefijo eNCF Descripcion Endpoint
31 Ecf31* E31 Factura de Credito Fiscal recepcionEcf31()
32 Ecf32* E32 Factura de Consumo recepcionEcf32()
33 Ecf33* E33 Nota de Debito recepcionEcf33()
34 Ecf34* E34 Nota de Credito recepcionEcf34()
41 Ecf41* E41 Compras recepcionEcf41()
43 Ecf43* E43 Gastos Menores recepcionEcf43()
44 Ecf44* E44 Regimenes Especiales recepcionEcf44()
45 Ecf45* E45 Gubernamental recepcionEcf45()
46 Ecf46* E46 Exportaciones recepcionEcf46()
47 Ecf47* E47 Pagos al Exterior recepcionEcf47()

Por ejemplo, para enviar una Factura de Credito Fiscal usa Ecf31ECF, Ecf31Encabezado, Ecf31Item, etc. y llama $ecfApi->recepcionEcf31($ecf).

Funcionalidades del API

Ademas de enviar comprobantes, el SDK expone todos los endpoints del API:

Clase Descripcion
EcfService Envio de ECF con auto-routing y polling
EcfApi Envio de ECF (tipos 31-47), consultas, busquedas, anulaciones
CompanyApi CRUD de empresas y gestion de certificados
DgiiApi Consultas DGII (directorio, estado, timbre, track IDs, etc.)
RecepcionApi Seguimiento de solicitudes de recepcion
ApiKeyApi Gestion de API keys

Funcionalidades adicionales

Funcionalidad Descripcion
Empresas Crear, consultar y eliminar empresas registradas
Certificados Subir y consultar certificados digitales
Consulta de e-CF Buscar comprobantes por RNC, eNCF, fecha, monto, etc.
Aprobacion Comercial Enviar aprobacion/rechazo comercial (ACECF)
Anulacion de Rangos Solicitar anulacion de secuencias de eNCF
Consultas DGII Directorio, estado, resultado, timbre, RFCE, track IDs
Estatus de Servicios Verificar disponibilidad de servicios DGII
API Keys Crear API keys para acceso programatico

Uso Directo de los Clientes API

Para control total, usa las clases API generadas directamente:

use GuzzleHttp\Client;
use Ecfx\EcfDgii\Configuration;
use Ecfx\EcfDgii\Api\CompanyApi;
use Ecfx\EcfDgii\Api\EcfApi;
use Ecfx\EcfDgii\Api\DgiiApi;
use Ecfx\EcfDgii\Api\RecepcionApi;
use Ecfx\EcfDgii\Api\ApiKeyApi;

$config = Configuration::getDefaultConfiguration()
    ->setHost('https://api.prod.ecfx.ssd.com.do')
    ->setAccessToken('your-api-key');

$client = new Client();

// Operaciones de empresas
$companyApi = new CompanyApi($client, $config);
$companies = $companyApi->getCompanies();
$company = $companyApi->getCompanyByRnc('123456789');

// Operaciones de ECF
$ecfApi = new EcfApi($client, $config);
$response = $ecfApi->recepcionEcf31($ecf);           // Factura de credito fiscal
$response = $ecfApi->recepcionEcf32($ecf);           // Factura de consumo
$results = $ecfApi->searchEcfs('123456789');           // Buscar ECFs por RNC
$ecfById = $ecfApi->getEcfById('123456789', $msgId);  // Obtener ECF por message ID
$ecfApi->aprobacionComercial('123456789', $encf, $acecfRequest); // Aprobacion comercial

// Consultas DGII
$dgiiApi = new DgiiApi($client, $config);
$directorio = $dgiiApi->consultaDirectorioListado('123456789');
$estado = $dgiiApi->consultaEstado('123456789', $rncEmisor, $ncf, $rncComprador, $codSeg);
$timbre = $dgiiApi->consultaTimbre('123456789', $rncEmisor, $encf, $monto, $codSeg);
$servicios = $dgiiApi->estatusServiciosObtenerEstatus('123456789');

// Seguimiento de recepcion
$recepcionApi = new RecepcionApi($client, $config);
$requests = $recepcionApi->searchEcfReceptionRequests();

Manejo de Errores

Excepcion Cuando
EcfProcessingException La DGII rechaza el ECF durante el procesamiento
EcfPollingTimeoutException El polling excede el maximo de intentos sin resultado final
ApiException Errores HTTP/red al comunicarse con el API
use Ecfx\EcfDgii\ApiException;
use Ecfx\EcfDgii\EcfProcessingException;
use Ecfx\EcfDgii\EcfPollingTimeoutException;

try {
    $result = $service->sendEcf($ecf);
} catch (EcfProcessingException $e) {
    // ECF rechazado por la DGII
    $response = $e->getEcfResponse();
    if ($response) {
        echo "Errores: " . $response->getErrors() . "\n";
    }
} catch (EcfPollingTimeoutException $e) {
    // El polling no obtuvo resultado a tiempo
    echo "Timeout: " . $e->getMessage() . "\n";
} catch (ApiException $e) {
    // Error HTTP (4xx, 5xx, red)
    echo "HTTP Error: " . $e->getCode() . "\n";
    echo "Body: " . $e->getResponseBody() . "\n";
}

Variables de Entorno

Variable Descripcion
ECF_DGII_TOKEN API key para autenticacion (desde ecf.ssd.com.do Settings > API Key)

Ejemplos

Regenerar desde OpenAPI Spec

openapi-generator generate \
  -i path/to/openapi/v1.json \
  -g php \
  -c openapi-generator-config.json \
  -o . \
  --skip-validate-spec

Contribuir

  1. Fork el repositorio
  2. Crea tu feature branch (git checkout -b feature/mi-feature)
  3. Haz commit de tus cambios (git commit -m 'Agregar mi feature')
  4. Push al branch (git push origin feature/mi-feature)
  5. Abre un Pull Request

Los tests corren automaticamente via GitHub Actions en cada push y PR.

Documentacion del API

La documentacion completa del API de ECF SSD esta disponible en:

https://ecf.ssd.com.do/documentacion/api-ecf-ssd

Incluye referencia de todos los endpoints, schemas, codigos de error y ejemplos de request/response.

Arquitectura Backend / Frontend

sequenceDiagram
    participant C as Cliente (Browser/App)
    participant BE as Backend
    participant ECF as ECF API

    C->>BE: POST /invoice (datos de factura)
    Note over BE: Valida, guarda y convierte a formato ECF

    BE->>ECF: POST /ecf/{tipo} (enviar ECF)
    ECF-->>BE: { messageId }
    BE-->>C: { messageId }

    Note over C: No espera — puede continuar

    alt Token en cache
        C->>C: Usar token existente
    else Sin token o expirado
        C->>BE: GET /ecf-token
        BE->>ECF: POST /apikey (solo lectura, scoped a RNC)
        ECF-->>BE: { apiKey }
        BE-->>C: { apiKey }
        C->>C: Almacenar token en cache
    end

    loop Polling hasta completar
        C->>ECF: GET /ecf/{rnc}/{encf} (token solo lectura)
        ECF-->>C: { progress, codSec, ... }
    end
Loading

Flujo detallado

  1. El cliente (browser/app) envía los datos de la factura al backend (POST /invoice, /order, /sale)
  2. El backend valida, guarda y convierte la factura interna al formato ECF
  3. El backend envía el ECF a la API de ECF SSD (POST /ecf/{tipo}) y recibe un messageId
  4. El backend retorna el messageId al cliente — el cliente no espera, puede continuar
  5. Cuando el cliente necesita consultar el estado del ECF, usa EcfFrontendClient que internamente:
    • Verifica si hay un token de solo lectura en cache
    • Si no existe o expiró: llama a getToken() (que el consumidor provee — típicamente un fetch('/ecf-token') a su backend), luego llama a cacheToken(token) para almacenarlo
    • Si la API retorna 401: automáticamente llama a getToken() de nuevo, actualiza el cache, y reintenta
  6. El cliente hace polling directamente contra la API de ECF SSD (GET /ecf/{rnc}/{encf}) hasta que progress sea Finished

Ejemplo: Backend (PHP)

use GuzzleHttp\Client;
use Ecfx\EcfDgii\Configuration;
use Ecfx\EcfDgii\Api\EcfApi;
use Ecfx\EcfDgii\Api\ApiKeyApi;
use Ecfx\EcfDgii\EcfService;

$config = Configuration::getDefaultConfiguration()
    ->setHost('https://api.prod.ecfx.ssd.com.do')
    ->setAccessToken(getenv('ECF_DGII_TOKEN'));

$client = new Client();
$ecfApi = new EcfApi($client, $config);
$service = new EcfService($ecfApi);

// Endpoint de facturacion — tu logica de negocio + envio a ECF SSD
// POST /api/v1/invoices
$invoice = validateAndSave($request);
$ecf = convertToEcf($invoice);
$result = $service->sendEcf($ecf);
// Retorna el messageId al cliente para que haga polling desde el frontend
return json_encode(['messageId' => $result->getMessageId(), 'encf' => $result->getEncf()]);

// Generar token de solo lectura para el cliente
// GET /api/v1/ecf-token
$apiKeyApi = new ApiKeyApi($client, $config);
$apiKey = $apiKeyApi->createApiKey($rnc);
return json_encode(['apiKey' => $apiKey->getToken()]);

Ejemplo: Frontend (TypeScript con EcfFrontendClient)

El cliente envía la factura al backend PHP, recibe el messageId y encf, y luego usa EcfFrontendClient para consultar el estado directamente contra la API de ECF SSD:

import { createFrontendClient } from '@ssddo/ecf-sdk';

const frontend = createFrontendClient({
  getToken: async () => {
    const res = await fetch('/api/v1/ecf-token');
    const { apiKey } = await res.json();
    return apiKey;
  },
  environment: 'prod',
  // cacheToken y getCachedToken usan localStorage por defecto
});

// 1. Enviar factura al backend PHP
const invoiceRes = await fetch('/api/v1/invoices', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(invoiceData),
});
const { messageId, encf } = await invoiceRes.json();

// 2. Consultar estado del ECF directamente contra ECF SSD (solo lectura)
const { data: ecf } = await frontend.queryEcf('131880681', encf);
console.log(ecf.progress);    // 'Finished'
console.log(ecf.codSec);      // Codigo de seguridad para imprimir
console.log(ecf.impresionUrl); // URL para generar QR

// 3. Buscar ECFs por RNC
const { data: ecfs } = await frontend.searchEcfs('131880681');

Plugin WooCommerce

Si usas WooCommerce, tenemos un plugin listo para facturacion electronica sin escribir codigo:

ECF SSD para WooCommerce

Soporte

Otros SDKs y Plugins

Este SDK es parte de una coleccion de SDKs oficiales para multiples plataformas:

Plataforma Paquete Instalacion
.NET NuGet dotnet add package SSDDO.ECF_DGII.SDK
TypeScript npm npm install ecf-dgii-client
React npm npm install ecf-dgii-react
Python PyPI pip install ecf-dgii-client
PHP Packagist composer require ecfx/ecf-dgii-php
Java Maven Central Ver java/README.md
Kotlin Maven Central Ver kotlin/README.md
iOS/Swift Swift Package Manager Ver ios/README.md
C++ vcpkg / Conan Ver C++/README.md
WooCommerce GitHub Plugin WordPress

Licencia

MIT


Hecho con platano power por SSD Smart Software Development SRL

About

PHP SDK for the ECF DGII electronic fiscal receipt API (Dominican Republic)

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages