Saltar a contenido

Certificación como emisor electrónico — estado técnico

Qué exige el Proceso de Certificación para ser Emisor Electrónico de la DGII, y qué tenemos. Este documento existe para que cuando el equipo del proyecto pida "lo técnico", la respuesta esté lista y sea comprobable.

El proceso tiene tres etapas: Solicitud (formulario en la OFV + postulación firmada), Set de Pruebas (datos, simulación y comunicación) y Certificación. Los requisitos previos —RNC, clave OFV, alta de NCF, certificado digital del Usuario Administrador e-CF, estar al día— son administrativos y los lleva el equipo del proyecto, no el software.

Las tres URL que hay que declarar

El formulario de postulación pide tres direcciones de servicios nuestros, y el paso 12 las vuelve a pedir para el ambiente productivo. Las tres existen:

Campo del formulario Ruta Qué hace
URL Autenticación GET /fe/autenticacion/api/semilla
POST /fe/autenticacion/api/validacioncertificado
Entrega una semilla, la recibe firmada y devuelve un token
URL Recepción POST /fe/recepcion/api/ecf Recibe e-CF de terceros y responde el ARECF firmado
URL Aprobación POST /fe/aprobacioncomercial/api/ecf Recibe aprobaciones/rechazos comerciales (200 / 400)

Requisitos del estándar para las tres: SSL, puertos tradicionales, accesibles desde internet y no sensibles a mayúsculas/minúsculas. El host es lo único que cambia entre contribuyentes.

Otros campos del formulario (Tipo de Registro, Tipo de Software, Nombre del Software, Versión de Software, Datos del Proveedor) son declarativos: los completa el equipo del proyecto.

El servicio de autenticación

Es el espejo del que consumimos contra la DGII, con los papeles invertidos:

  1. El contribuyente pide GET /fe/autenticacion/api/semilla → devolvemos un SemillaModel con un valor único.
  2. Lo firma con su certificado y lo envía a POST /fe/autenticacion/api/validacioncertificado (multipart, parte xml).
  3. Comprobamos, en este orden: estructura contra semilla.xsd → firma digital → que la semilla la emitiéramos nosotros, siga viva y no se haya usado → RNC del titular del certificado.
  4. Devolvemos { token, expira, expedido }.

Los nombres de recurso son los del estándar del RECEPTOR, no los del servicio de la DGII. La DGII expone /autenticacion/api/autenticacion/semilla y validarsemilla; a los contribuyentes se les exige /fe/autenticacion/api/semilla y validacioncertificado, porque "la única diferencia entre los servicios de los contribuyentes sea la dirección de host" (Descripción Técnica Servicios Emisores Electrónicos, Creación de Servicios). Confundirlos deja el servicio inalcanzable para quien siga la norma.

Detalles que importan:

  • Anti-replay. La semilla se registra al emitirse y se consume al usarse (Cache::pull), con una ventana de 5 minutos. Sin esto, una semilla firmada capturada de una sesión anterior serviría para siempre, y una semilla inventada por el llamante también.
  • El token no se guarda en claro: se indexa por sha256, así que quien lea el almacén de caché no obtiene tokens utilizables. Vive una hora (DGII_RECEIVER_TOKEN_TTL).
  • Una semilla sin firmar la rechaza el propio esquema: en semilla.xsd el xs:any final —el hueco de la <Signature>— es obligatorio.
  • El token queda asociado al titular del certificado, no a una company nuestra: quien se autentica es un contribuyente externo.

Por qué el token es opcional en recepción y aprobación

DGII_RECEIVER_REQUIRE_TOKEN está en false por defecto, y es una decisión deliberada: en las pruebas de comunicación (pasos 9 a 11) es la DGII quien nos envía los comprobantes y las aprobaciones, y exigir un token que no llegue haría fallar el paso. La legitimidad de fondo no depende del token: la firma digital del documento se verifica siempre, y el RNC comprador tiene que corresponder a uno de nuestros emisores.

Lo que sí es innegociable: un token presente pero inválido se rechaza con 401. Si el llamante decide identificarse, esa identidad tiene que sostenerse.

El set de pruebas, paso a paso

Paso Qué pide la DGII Estado
1 Postulación en XML, firmada Administrativo
2 Pruebas de datos e-CF: generar los XML desde un Excel que ellos dan y enviarlos a recepción Listo — los 10 tipos
3 Pruebas de datos de aprobaciones/rechazos comerciales desde otro Excel Listo
4 Pruebas de simulación: e-CF con datos reales de la operación Listo
5 Enviar las representaciones impresas en PDF (máx. 10 MB) Listo — RI conforme a §18
6 Validación de la RI por la DGII Revisión manual de ellos
7 Actualizar las URL de prueba Las tres existen
8 Descargar el certificado raíz y declararse listo Listo — falta instalar el fichero
9 Recepción de e-CF enviados por la DGII → devolver acuses Listo — ARECF firmado
10-11 Recepción de aprobaciones comerciales → responder OK / Error Listo — 200 / 400
12 URL de los servicios en producción Las tres existen
13 Declaración jurada firmada Administrativo

Dos avisos sobre el set de pruebas

Un rechazo obliga a reiniciar. La nota al pie del paso 2 es explícita: "en el caso de que un e-CF generado resulte con estado Rechazado, deberá reiniciar la generación del set de datos de prueba". Eso cambia el cálculo: conviene agotar la auditoría del núcleo antes de la certificación, porque un campo mal emitido no cuesta un reintento, cuesta el set completo.

Hay que poder expresar lo que venga en su Excel. El set exige generar los XML "con los mismos campos y en el mismo orden" del archivo que ellos entregan. Si trae bloques que nuestro JSON canónico todavía no expresa —los campos de ISC de alcoholes y tabaco (CantidadReferencia, GradosAlcohol, PrecioUnitarioReferencia, Subcantidad) son los candidatos conocidos— hay que implementarlos antes, no durante.

Riesgos abiertos

  • Cadena de confianza: implementada, pendiente de instalar el fichero. Ver cadena-de-confianza.md. El código ya comprueba vigencia y cadena; lo que falta es un fichero, no desarrollo: descargar el raíz del portal (paso 8), reunir las intermedias de las CAs acreditadas por INDOTEL, apuntar DGII_TRUST_AUTHORITIES y activar DGII_TRUST_REQUIRE_CHAIN antes de producción.
  • Sesión ante la DGII. Ya no es un riesgo: las llamadas autenticadas renuevan la sesión y reintentan una vez ante un 401 (DgiiClient::authenticated()), así que una sesión caída a mitad de la operación no cuesta un documento sin transmitir.
  • Nada probado en vivo. Todo lo anterior está verificado contra los esquemas oficiales y con certificados autofirmados. El .p12 real es lo que convierte esto en una afirmación comprobada.

Antes de dar el paso 8 por bueno

El paso 8 es "descargar el certificado raíz y declararse listo", y es fácil de dar por hecho: se baja un fichero y se sigue. Pero un raíz descargado y no instalado —o instalado donde el proceso de PHP no lo lee— no da error en ningún sitio: simplemente no se comprueba nada. Y al contrario, activar DGII_TRUST_REQUIRE_CHAIN con las raíces incompletas rechaza comprobantes legítimos, uno por uno.

Por eso hay un comando de diagnóstico:

docker compose exec app php artisan dgii:trust-check

Lista las raíces que el proceso está leyendo de verdad (con su vencimiento), si hay intermedias, y comprueba que nuestros propios certificados de firma encadenen hasta alguna de ellas. Si nuestro certificado no encadena, el de un tercero tampoco lo hará.