Instalación
Requiere .NET 8 o .NET 10. Es un solo paquete: emisor, receptor, firma y herramientas vienen juntos.
dotnet add package DgiiEcf
Todo lo que necesitas para pasar de dotnet add package a tu primer e-CF aceptado en TesteCF.
Requiere .NET 8 o .NET 10. Es un solo paquete: emisor, receptor, firma y herramientas vienen juntos.
dotnet add package DgiiEcf
Agrega la sección DgiiEcf a tu appsettings.json:
{
"DgiiEcf": {
"Environment": "Test",
"StatusApiKey": null,
"Certificate": {
"CertificatePath": "certs/empresa.p12",
"CertificatePassword": "<desde un secret store>"
}
}
}| Environment | Ambiente DGII |
|---|---|
Test | TesteCF |
Certification | CerteCF |
Production | eCF |
El certificado también se puede entregar en base64 con CertificateBase64, algo útil en contenedores y secret managers. Nunca guardes la contraseña en el repositorio.
Con el contenedor de dependencias de ASP.NET Core o de un Worker:
builder.Services.AddDgiiEcf(builder.Configuration); // o por código services.AddDgiiEcf(options => { options.Environment = DgiiEnvironment.Certification; options.CertificateBase64 = secrets["dgii-p12"]; options.CertificatePassword = secrets["dgii-p12-password"]; }); // o partiendo de un X509Certificate2 que ya tengas abierto services.AddDgiiEcf(certificate, options => options.Environment = DgiiEnvironment.Production);
Si no usas un contenedor de DI:
using var dgii = DgiiEcfClient.Create(options => { /* ... */ }); var client = dgii.Client; // IDgiiEcfClient var receiver = dgii.Receiver; // IEcfReceiver
Todas las operaciones devuelven Result<T>. Un error esperado nunca lanza excepción: revisa IsSuccess o Error.
Error es un DgiiApiError con Status, Messages (los mensajes de la DGII) y RawBody.Error.Message trae el texto de la DGII, por ejemplo "e-NCF duplicado".Inyecta IDgiiEcfClient, firma y envía. El token se obtiene y se renueva solo, y si la DGII responde 401 se reintenta una vez con un token nuevo. Por defecto el archivo se nombra {RNCEmisor}{eNCF}.xml.
public sealed class FacturacionService(IDgiiEcfClient dgii) { public async Task<string?> EnviarAsync(Ecf factura, CancellationToken ct) { var firmado = dgii.SignDocument(factura); // o dgii.Sign(xml, "ECF") if (firmado.IsFailure) return null; var envio = await dgii.SendElectronicDocumentAsync(firmado.Value, cancellationToken: ct); if (envio.IsFailure) return null; var estado = await dgii.GetTrackStatusAsync(envio.Value.TrackId!, ct); return estado.IsSuccess ? estado.Value.Estado : null; } }
Otras operaciones del emisor: SendCommercialApprovalAsync, VoidEncfAsync, InquiryStatusAsync, GetTrackIdsAsync, GetCustomerDirectoryAsync y GetSummaryInvoiceInquiryAsync (solo producción). Las de estatus de servicios (GetServicesStatusAsync, GetMaintenanceWindowsAsync, VerifyServiceStatusAsync) requieren StatusApiKey.
Para un e-CF 32 menor de RD$250,000 se envía el resumen (RFCE). Guarda el e-CF 32 firmado: es el documento completo.
var ecf32 = dgii.SignDocument(facturaConsumo).Value; var rfce = dgii.CreateSignedRfce(ecf32).Value; // resumen firmado + código de seguridad var respuesta = await dgii.SendSummaryAsync(rfce.SignedXml); var qr = EcfTools.FcQrCodeUrl(rnc, encf, montoTotal, rfce.SecurityCode, DgiiEnvironment.Test);
Para el estándar emisor-receptor expones tres endpoints y IEcfReceiver hace el trabajo pesado:
| Endpoint | Método |
|---|---|
GET /fe/autenticacion/api/semilla | GenerateSeed() |
POST /fe/autenticacion/api/validacioncertificado | ValidateSignedSeed() → JWT |
POST /fe/recepcion/api/ecf | ValidateToken() + BuildSignedReceiptAcknowledgement() |
app.MapGet("/fe/autenticacion/api/semilla", (IEcfReceiver r) => Results.Content(r.GenerateSeed(), "application/xml")); app.MapPost("/fe/autenticacion/api/validacioncertificado", async (HttpRequest req, IEcfReceiver r) => { var body = await new StreamReader(req.Body).ReadToEndAsync(); var archivo = r.ParseReceivedDocument(body, req.ContentType!); var token = archivo.IsSuccess ? r.ValidateSignedSeed(archivo.Value.XmlContent) : null; return token is { IsSuccess: true } ? Results.Ok(token.Value) : Results.Unauthorized(); });
El acuse de recibo rechaza por su cuenta los tipos 32, 41, 43, 45, 46 y 47 (código 1) y los documentos cuyo RNC comprador no es el tuyo (código 4).
EcfTools es una clase estática: no necesita certificado ni conexión HTTP.
| Método | Qué hace |
|---|---|
GetSecurityCode(signedXml) | Primeros 6 caracteres del SignatureValue |
ConvertEcf32ToRfce(signedEcf32) | Genera el RFCE sin firmar |
EcfQrCodeUrl(...) / FcQrCodeUrl(...) | URL del QR con el encoding de encodeURIComponent |
Serialize(doc) / Deserialize<T>(xml) | Documentos tipados ⇄ XML |
JsonToXml / XmlToJson / NormalizeJsonCasing | Compatibilidad con los payloads JSON del paquete Node |
SetXmlValue, ExtractXmlFromBody, CurrentFormattedDateTime | Utilidades de XML y fecha |
Para verificar la firma de un documento recibido, usa IValidateDocumentSignatureHandler desde el contenedor de DI.
Es un port de dgii-ecf v1.8.5, con estas mejoras:
IAccessTokenStore por un store distribuido.ImpuestoAdicional y rechaza documentos que no son tipo 32 o que llegan a RD$250,000.637.20 sigue siendo 637.20).Proyecto independiente desarrollado y mantenido por fcastro. No está afiliado, respaldado ni mantenido por la Dirección General de Impuestos Internos (DGII).