Manual de integración con hardware fiscal
Este manual describe cómo Zentto ERP se integra con las impresoras fiscales homologadas por el
SENIAT, a través del componente Zentto Fiscal Agent (repositorio
zentto-fiscal-agent). Documenta, por cada fabricante, las bibliotecas (DLL)
consumidas, la referencia completa de funciones de cada biblioteca, la matriz de operaciones
soportadas, los comandos/tramas enviados al equipo, ejemplos de secuencia completa de cada
operación y los endpoints expuestos, en cumplimiento de los requisitos de documentación técnica
de la homologación. Las fuentes primarias son la documentación de integración provista por los
fabricantes: el manual de la DLL BemaFI y el Protocolo de Comunicación Directo (Bematech), el
Manual de integración DLL 2.2 y el protocolo SENIAT 0141 v5.4 (Desarrollos PnP), la
documentación de la biblioteca TfhkaNet (The Factory HKA) y la documentación del protocolo
Rigazsa (base 0141 con extensiones).
1. Arquitectura general
El Zentto Fiscal Agent es una aplicación .NET 9 (x64) que corre como
Servicio de Windows (nombre ZenttoHardwareHub, display
"Zentto Fiscal Agent") en la estación donde está conectada la impresora fiscal. Expone una API
HTTP local en http://localhost:7654 consumida por el ERP (navegador o nube vía relay).
Zentto ERP (navegador / api.zentto.net)
│ HTTPS / WebSocket (relay)
▼
Zentto Fiscal Agent (Servicio de Windows, localhost:7654)
│ DLL del fabricante ó tramas seriales directas (RS-232 / USB-CDC)
▼
Impresora fiscal (Bematech · The Factory HKA · PnP · Rigazsa)
- Marcas fiscales implementadas: Bematech, The Factory HKA, PnP y Rigazsa. Adicionalmente ESC/POS genérico (impresión NO fiscal: comandas, tickets).
- Doble transporte por marca: cada request puede elegir
modo: dll (biblioteca del fabricante) o trama (protocolo serial directo, según la documentación de protocolo del fabricante). Rigazsa opera solo por tramas (no existe DLL del fabricante). - Contrato común: todos los fabricantes responden el modelo
FiscalResultado — success, numeroDocumento, coo, serialImpresora, fechaEmision, transporte, documentoInterno (correlación con el documento del ERP) y pasos[] (traza paso a paso de la operación, devuelta en cada respuesta HTTP). - Regla SENIAT (bidireccionalidad): antes de abrir cualquier documento se verifica el estado del equipo (pre-flight). Si la impresora no responde, no tiene papel o tiene la memoria fiscal llena, el agente responde HTTP 422 y el ERP no registra el documento. Ante un fallo a mitad de emisión se anula el documento colgado (rollback) y se reintenta con espera (3 intentos × 4 s) cuando la falla es físicamente recuperable (papel/tapa).
- Documento colgado: si el pre-flight detecta una transacción fiscal abierta de un intento previo (corte de energía, papel a mitad), el agente la anula antes de abrir el documento nuevo. El COO ya consumido no afecta acumuladores ni correlativo fiscal porque el documento no llegó a totalizar.
- Anti-inyección: todo texto recibido del ERP (nombres, descripciones, direcciones) se sanea eliminando bytes de control (< 0x20 y 0x7F) antes de incorporarlo a la trama, de modo que un dato de negocio no pueda inyectar STX/ETX/EOT en el cable.
- Trazabilidad: (a) cada operación devuelve su traza
pasos[] (OK Item[...], WARN ..., ERR ...); (b) todo request HTTP y excepción se registra en la observabilidad central (Kafka); (c) la DLL de Bematech mantiene su propio log de fabricante (Log=1, Path=C:\Log en BemaFI32.ini).
1.1 Endpoints comunes (todas las marcas)
| Método | Ruta | Descripción |
GET | / | Estado del hub (versión, modo) |
GET | /api/fiscal/health | Salud del agente: versión, marcas activas (hka, bematech, pnp, rigaza) y transporte efectivo por marca (trama|dll, configurable en caliente desde la bandeja) |
GET | /api/fiscal/metodos | Autodescubrimiento: lista de endpoints fiscales disponibles |
GET | /api/fiscal/status?marca=&puerto=&conexion= | Estado del equipo con datos reales (modelo, serial fiscal, firmware, sensores de papel/tapa, memoria fiscal) |
POST | /api/fiscal/reporte/x · /api/fiscal/reporte/z | Reportes X/Z genéricos (ruteo por marca del body) |
GET | /api/fiscal/reporte/mensual?anio=&mes= · /api/fiscal/memoria | Reporte mensual y lectura de memoria fiscal genéricos |
POST | /api/fiscal/documento-no-fiscal | Documento no fiscal genérico |
POST | /api/escpos · /api/print | Impresión NO fiscal (ESC/POS: comandas, tickets) |
GET | /api/perifericos/balanza?puerto= | Lectura de balanza serial |
Además, cada marca expone su propia familia de 12 endpoints (documentados en la
sección de cada fabricante): factura, nota-credito, nota-debito,
documento-no-fiscal, reporte/x, reporte/z, gaveta,
anular, reimpresion, reporte/rango, info y
puertos. Toda emisión que la impresora rechace o no responda devuelve
HTTP 422 con el detalle, para que el ERP no registre el documento.
2. Instalación como Servicio de Windows y multi-impresora
Instalación
- Instalador (Inno Setup): requiere administrador; crea el servicio con
sc.exe create ZenttoHardwareHub binPath= "...Zentto.LocalFiscalAgent.exe --urls http://localhost:7654" start= auto, configura reinicio automático ante fallo (5 s / 10 s / 30 s) e instala la app de bandeja Zentto.FiscalAgent.Tray.exe para configurar sin abrir consola. - Script PowerShell (
INSTALAR_SERVICIO.ps1): publica self-contained, crea el servicio (New-Service, inicio automático) y registra tareas programadas ZenttoAgentStart/Stop/Restart (cuenta SYSTEM) para controlar el servicio desde la web sin UAC. - Desinstalación:
DESINSTALAR_SERVICIO.ps1.
Procedimiento paso a paso (resumen operativo)
- Abrir PowerShell como Administrador en la carpeta del agente.
- Ejecutar
powershell -ExecutionPolicy Bypass -File .\INSTALAR_SERVICIO.ps1 — publica el ejecutable (carpeta limpia .service-publish) y crea el servicio. - Verificar el servicio:
Get-Service ZenttoHardwareHub. - Verificar la API:
http://localhost:7654/ y http://localhost:7654/api/status?marca=PNP&puerto=COM1&conexion=emulador. - Alternativa manual:
dotnet publish -c Release -r win-x64 → sc.exe create ZenttoHardwareHub ... start= auto → sc.exe failure ZenttoHardwareHub reset= 86400 actions= restart/5000/restart/5000/restart/5000 → Start-Service ZenttoHardwareHub. - Operación diaria:
Restart-Service / Stop-Service ZenttoHardwareHub; ver logs en vivo con dotnet run --urls http://localhost:7654 (modo consola). Ante conflicto de puerto, verificar con netstat -ano | findstr :7654.
Configuración
appsettings.json: secciones Bematech, Hka, Pnp con Puerto (default AUTO — autodetección sondeando puertos COM), BaudRate 9600, TimeoutMs y transporte por defecto. - Override en caliente sin admin:
%ProgramData%\Zentto\FiscalAgent\agent.settings.json (hot-reload), escrito por la app de bandeja — permite cambiar el transporte (DLL/trama) sin reiniciar.
Multi-impresora / multi-marca
- Un mismo agente atiende varias marcas simultáneamente (endpoints separados por marca) y varios puertos COM: cada request del ERP indica
marca, puerto y modo. - El mapeo caja → impresora lo administra el ERP (Configuración de Cajas); el agente es stateless respecto al equipo.
- Identidad por caja para impresión remota:
FiscalRelay:CashRegisterId + CompanyId + AgentToken — una instancia de agente representa una caja ante la nube.
Relay de impresión remota (WebSocket)
Para dispositivos que no están en la misma máquina que la impresora (apps móviles, tablets), el
agente mantiene un WebSocket persistente con wss://api.zentto.net/ws/fiscal-relay:
se registra con {type:"register", companyId, cashRegisterId, agentToken}, recibe
{type:"command", id, path, method, payload}, lo ejecuta contra el propio agente local
y responde {type:"response", id, status, data}. Heartbeat ping/pong cada 20 s y
reconexión con backoff exponencial (2 s → 60 s).
3. Bematech (MP-4000 TH FI) — validada con hardware
3.1 Biblioteca del fabricante
- DLL:
BemaFI64.dll (x64) / BemaFI32.dll (x86), versión verificada 5.4.1.47 (kit DLL_BemaFI_5_4_1_46). Carga por P/Invoke (DllImport, stdcall, ANSI) con resolver por bitness (Bematech/BematechNative.cs). La DLL exporta del orden de 100 funciones Bematech_FI_* (incluyendo variantes *Serial y *MFD); la sección 3.3 documenta la referencia por categoría. - Archivo de apoyo:
BemaFI32.ini en C:\Windows\System32 (el agente lo copia al iniciar): Puerta=USB (o COMx), BaudRate=9600, Tentativas=200, WakMilisegundos=40, TimeoutSegundos=20, ModeloImp=BEMATECH, PAIS=VENEZUELA. - Modelos soportados por la biblioteca: MP-20 FI II, MP-40 FI II, MP-2000 FI TH, MP-4000 TH FI, MP-6000 FI TH, MP-25 FI, MP-50 FI, Yanco Y-8000 / Y-8500.
- Validación: ruta DLL confirmada contra impresora real MP-4000 TH FI (firmware 01.00.22/23). El framing serial directo está implementado y marcado para validación final contra emulador.
3.2 Matriz de operaciones
| Operación | Soporte | Función DLL | Opcode trama | Observaciones |
| Factura / cupón fiscal | DLL y trama | AbreComprobanteDeVentaEx → VendeArticulo×N → IniciaCierreCupon → EfectuaFormaPago×N → FinalizarCierreCupon | ESC 0 · ESC 62 71 · ESC 32 · ESC 90 · ESC 34 | Alícuotas VE: 16,00 / 8,00 / 31,00 / II (exento) |
| Factura con IGTF 3% | Solo DLL | IniciaCierreCuponIGTF(montoBs) | — | Sin comando serial equivalente; en modo híbrido la transacción completa se enruta por la DLL. Requiere DLL ≥ 5.4.1.40 y firmware 01.00.22/23 |
| Nota de crédito | DLL y trama | AbreNotaDeCredito + DevolucionArticulo×N | ESC 89 · ESC 62 71 51 | Referencia obligatoria: serial, RIF, fecha/hora y COO de la factura afectada |
| Nota de débito | Endpoint | POST /nota-debito (mismo contrato que NC) | Documento inverso con referencia a la factura |
| Documento no fiscal | DLL y trama | AbreRelatorioGerencial / UsaRelatorioGerencial / CierraRelatorioGerencial | ESC 20 / ESC 67 / ESC 21 | Texto libre (informe gerencial) |
| Reporte X | DLL y trama | LecturaX (variante LecturaXSerial devuelve la data al PC) | ESC 6 | |
| Reporte Z (reducción) | DLL y trama | ReduccionZ(fecha, hora) | ESC 5 | Cierre diario |
| Memoria fiscal por fechas | DLL y trama | LecturaMemoriaFiscalFecha(DDMMAA, DDMMAA) | ESC 8 (rango 6+6) | La variante serial diferencia fecha/Z por longitud del rango |
| Memoria fiscal por nº Z | DLL y trama | LecturaMemoriaFiscalReduccion(nnnn, nnnn) (variante ...SerialReduccion al PC) | ESC 8 (rango 4+4) | |
| Reimpresión | DLL (trama solo último doc) | ImpresionCintaDetalle(tipo, ini, fin, usuario) — tipo "0" total, "1" fecha, "2" COO | ESC 75 (copia del último comprobante) | La reimpresión por COO específico se enruta por DLL |
| Anular documento / renglón | DLL y trama | AnulaCupon / AnulaArticuloAnterior | ESC 14 / ESC 13 | Usada además como rollback ante fallos a mitad |
| Gaveta de dinero | DLL | AccionaGaveta | — | ModoGaveta en el INI |
| Estado del equipo | DLL y trama | VerificaImpresoraPrendida / RetornoImpresora(ACK, ST1, ST2) | ESC 19 | Pre-flight de la regla SENIAT |
| Info / serial / COO | DLL y trama | NumeroSerie / NumeroComprobanteFiscal / NumeroCupon / SubTotal | ESC 35 40 serial · ESC 35 41 firmware · ESC 30 COO | |
| Alícuotas | DLL | RetornoAlicuotas (consulta) / ProgramaAlicuota (alta) | — | Programación requiere Modo Intervención Técnica |
| Fiscalización | DLL | ProgramaCliche / AnadirRif / BorraRifs / ProgramaAlicuota | — | Requieren llave MIT (Modo Intervención Técnica) |
| QR / código de barras | DLL | CodigoBarrasPDF417MFD / FinalizaCierreCuponCodigoBarrasMFD | — | Cierre del cupón imprimiendo PDF417/QR |
3.3 Referencia de funciones de la DLL BemaFI (por categoría)
Catálogo de las funciones de la biblioteca BemaFI relevantes para la integración fiscal VE,
según el manual de la DLL del fabricante y los bindings de integración (firmas verificadas
contra BemaFI64.dll 5.4.1.47). Todas retornan int
(1 = OK; ver códigos al final). Prefijo Bematech_FI_ omitido por brevedad.
Puerto y estado
| Función | Parámetros | Propósito |
AbrePuertaSerial() | — | Abre el puerto configurado en BemaFI32.ini |
CierraPuertaSerial() | — | Cierra el puerto |
VerificaImpresoraPrendida() | — | Verifica que el equipo responde (pre-flight) |
RetornoImpresora(ref ACK, ref ST1, ref ST2) | 3 × ref int | Estado detallado ACK/ST1/ST2 (decodificación bit a bit) |
Fiscalización y configuración (Modo Intervención Técnica)
| Función | Parámetros | Propósito |
ProgramaCliche(cliche) | texto ≤ 558, líneas con \n | Encabezado del propietario (razón social, dirección) |
AnadirRif(rif) | ≤ 18 chars (export Bematech_FI_Anadir_Rif) | Programa un RIF de comercializador; requiere equipo sin movimiento (tras Z) |
BorraRifs() | — | Borra los RIFs programados |
ProgramaAlicuota(alicuota, tipo) | string, int | Da de alta una alícuota de impuesto |
Cupón fiscal (factura)
| Función | Parámetros | Propósito |
AbreComprobanteDeVenta(rif, nombre) | 2 strings | Abre cupón fiscal (forma corta) |
AbreComprobanteDeVentaEx(rif, nombre, direccion) | 3 strings — orden verificado contra el equipo: RIF, Nombre, Dirección | Abre cupón fiscal con datos completos del cliente |
AbreCupon(nombreRif) | 1 string | Apertura simplificada |
VendeArticulo(codigo, descripcion, alicuota, tipoCantidad, cantidad, decimales, valorUnitario, tipoDescuento, descuento) | 8 strings + 1 int — alícuota "16,00"/"8,00"/"31,00"/"II" o índice "01".."16"; tipoCantidad 'I' entera / 'F' fraccionaria; tipoDescuento '%' o '$' | Registra un renglón de venta |
AnulaArticuloAnterior() | — | Anula el último renglón vendido |
AnulaCupon() | — | Anula el cupón fiscal abierto (rollback) |
Cierre y formas de pago
| Función | Parámetros | Propósito |
IniciaCierreCupon(accion, tipo, valor) | "A" recargo / "D" descuento · "%" o "$" · valor | Inicia el cierre con descuento/recargo global |
IniciaCierreCuponIGTF(montoBs) | monto pagado en divisa/cripto expresado en Bs (formato "2300,00"); "0" = sin IGTF | Inicia el cierre aplicando IGTF 3% — la impresora calcula el 3% internamente. Requiere DLL ≥ 5.4.1.40 |
EfectuaFormaPago(forma, valor) | descripción del medio (ej. "Debito", "Divisa") + monto | Registra cada forma de pago del cupón |
FinalizarCierreCupon(mensaje) | mensaje promocional | Cierra el cupón e imprime el totalizador |
FinalizaCierreCuponCodigoBarrasMFD(mensaje, tipoCodigo, codigo, altura, largura, posCaracteres, fuente, margen, correccion, columnas) | 3 strings + 7 int | Cierra el cupón imprimiendo un código (ej. "PDF417" con URL/QR) |
Nota de crédito / devolución
| Función | Parámetros | Propósito |
AbreNotaDeCredito(nombre, numeroSerie, rif, dia, mes, ano, hora, minuto, segundo, coo, msjPromocional) | 11 strings — identifica la factura afectada por serial + fecha/hora + COO | Abre la nota de crédito referenciada |
DevolucionArticulo(codigo, descripcion, alicuota, tipoCantidad, cantidad, decimales, valorUnit, tipoDescuento, valorDesc) | misma firma que VendeArticulo | Renglón devuelto (una llamada por tasa/artículo) |
Documento no fiscal (informe gerencial)
| Función | Parámetros | Propósito |
AbreRelatorioGerencial() | — | Abre el documento no fiscal |
UsaRelatorioGerencial(texto) | texto | Imprime líneas de texto libre |
CierraRelatorioGerencial() | — | Cierra el documento no fiscal |
InformeGerencial(texto) / CierraInformeGerencial() | texto / — | Variante corta abre-e-imprime del informe gerencial |
Informes fiscales, memoria fiscal y reimpresión
| Función | Parámetros | Propósito |
LecturaX() | — | Imprime la lectura X |
LecturaXSerial() | — | Lectura X devuelta por el puerto serial (al PC) |
ReduccionZ(fecha, hora) | strings (vacíos = fecha/hora del equipo) | Reducción Z (cierre diario) |
LecturaMemoriaFiscalFecha(fechaIni, fechaFin) | DDMMAA + DDMMAA | Reporte de memoria fiscal por rango de fechas |
LecturaMemoriaFiscalReduccion(zIni, zFin) | nº Z ≤ 4 dígitos | Reporte de memoria fiscal por rango de reducciones Z |
LecturaMemoriaFiscalSerialReduccion(zIni, zFin) | nº Z | Ídem, con salida por el puerto serial (al PC) |
ImpresionCintaDetalle(tipo, ini, fin, usuario) | tipo "0" total / "1" por fecha DDMMAA / "2" por COO (≤ 6 díg.); usuario normalmente "1" | Reimpresión de documentos desde la cinta de detalle (MFD) |
Consultas (out-string, marshaling VBByRefStr) y periféricos
| Función | Salida | Propósito |
NumeroSerie(ref s) | serial del equipo | Identificación fiscal de la máquina |
NumeroComprobanteFiscal(ref s) | nº del último comprobante fiscal | Correlativo fiscal |
NumeroCupon(ref s) | COO (Contador de Orden de Operación) | Número del último documento |
RetornoAlicuotas(ref s) | alícuotas programadas | Consulta de tasas |
SubTotal(ref s) | subtotal del cupón abierto | Consulta durante la emisión |
AccionaGaveta() | — | Apertura de gaveta de dinero |
AccionaGuillotinaMFD(modo) | — | Corte de papel (guillotina) |
CodigoBarrasPDF417MFD(nivelCorreccion, altura, longitud, numColumnas, codigo) | — | Imprime código PDF417/QR (ej. URL de verificación) |
Códigos de retorno: 1=OK, -6=apagada/desconectada, -27=estado distinto de (6,0,0) en ACK/ST1/ST2. ST1/ST2 se decodifican bit a bit: ST1.7 fin de papel, ST1.6 poco papel, ST1.4 impresora en error (tapa/mecánico, recuperable), ST1.1 comprobante abierto (documento colgado → anular), ST2.6 memoria fiscal llena (fatal).
3.4 Tramas seriales directas (modo híbrido)
Según el manual de Protocolo de Comunicación Directo del fabricante. Puerto
9600 8-N-1, timeout 20 s (con reintentos de transporte 200 × 40 ms, según la configuración de la
biblioteca del fabricante). Framing:
STX(0x02) | LEN(2, LE) | DATA | CSUM(2, LE) | ETX(0x03);
DATA = ESC(0x1B) opcode (FS 0x1C + campo)*, texto ISO-8859-1; respuesta
ACK/NAK + ST1 + ST2.
| Comando | Opcode |
| Abrir factura | ESC 0 (Nombre, RIF, Dirección) |
| Vender artículo / devolución | ESC 62 71 / ESC 62 71 51 |
| Anular artículo / anular factura | ESC 13 / ESC 14 |
| Iniciar cierre / forma de pago / terminar cierre | ESC 32 / ESC 90 / ESC 34 |
| Nota de crédito | ESC 89 |
| Documento no fiscal (abre/texto/cierra) | ESC 20 / ESC 67 / ESC 21 |
| Lectura X / Reducción Z | ESC 6 / ESC 5 |
| Consultas: serial / firmware / estado / COO | ESC 35 40 / ESC 35 41 / ESC 19 / ESC 30 |
| Memoria fiscal por rango / copia último documento | ESC 8 / ESC 75 |
Formatos de campo: coma decimal (0,25), alícuota ×100 a 4 dígitos
(16% → 1600), descuento 1000 = 10%. En las formas de pago,
el índice 20 identifica el medio en divisa cuando aplica IGTF.
3.5 Ejemplos de secuencia completa
Factura con IGTF 3% (modo DLL — camino de certificación)
1. Bematech_FI_AbrePuertaSerial() → 1
2. Bematech_FI_VerificaImpresoraPrendida() → 1 (pre-flight SENIAT)
3. Bematech_FI_AnulaCupon() (seguridad: limpia doc. colgado)
4. Bematech_FI_AbreComprobanteDeVentaEx("V-12345678-9", "CLIENTE DEMO", "Caracas")
5. Bematech_FI_VendeArticulo("P001", "Producto gravado", "16,00", "I", "1", 2, "10000", "%", "0000")
... (una llamada por renglón; alícuotas 16,00 / 8,00 / 31,00 / II)
6. Bematech_FI_IniciaCierreCuponIGTF("116,00") (monto pagado en divisa, en Bs)
7. Bematech_FI_EfectuaFormaPago("Divisa USD", "11600")
8. Bematech_FI_FinalizarCierreCupon("Gracias por su compra")
9. Bematech_FI_NumeroCupon(ref coo) · Bematech_FI_NumeroSerie(ref serial)
10. Bematech_FI_CierraPuertaSerial()
La impresora imprime el renglón IGTF 3% en el totalizador del cupón. Sin IGTF, el paso 6 es IniciaCierreCupon("D", "%", "0").
Nota de crédito con referencia (modo DLL)
1. AbrePuertaSerial → VerificaImpresoraPrendida (pre-flight)
2. Bematech_FI_AbreNotaDeCredito("CLIENTE DEMO", "AB012345678", "V-12345678-9",
"05", "07", "2026", "14", "30", "00", "001234", "Devolucion")
// serial + RIF + fecha/hora + COO de la factura AFECTADA
3. Bematech_FI_DevolucionArticulo("P001", "Producto gravado", "16,00", "I", "1", 2, "10000", "%", "0000")
... (un renglón por cada tasa devuelta)
4. IniciaCierreCupon → EfectuaFormaPago → FinalizarCierreCupon
5. NumeroCupon / NumeroComprobanteFiscal (correlativo de la NC emitida)
Factura sin IGTF (modo serial directo — flujo real)
Pre-flight ESC 19 → anular colgado ESC 14 → abrir factura
ESC 0 → N × vender artículo ESC 62 71 → iniciar cierre
ESC 32 (descuento/recargo) → N × forma de pago ESC 90 → terminar
cierre ESC 34 → leer COO (ESC 30) y serial (ESC 35 40).
Reporte Z
1. AbrePuertaSerial → VerificaImpresoraPrendida
2. Bematech_FI_ReduccionZ("", "") // vacío = fecha/hora del propio equipo
(serial directo: ESC 5)
3. CierraPuertaSerial
3.6 Certificación con hardware real (dossier Bematech)
La integración se validó contra una MP-4000 TH FI física (firmware
01.00.22 / 01.00.23, adecuación IGTF) respondiendo los 5 elementos requeridos por
Bematech / Soluciones POS Venezuela:
- Sistema integrado: Zentto ERP — módulo Zentto Fiscal Agent. Empresa ZENTTO GLOBAL TECHNOLOGY, C.A. — RIF J-50849797-0.
- Versión de la DLL: BemaFI64.dll v5.4.1.47 (la adecuación IGTF exige ≥ 5.4.1.40 — cumplido). Verificable en vivo:
GET /api/fiscal/bematech/info → campo VersionDll. - IGTF 3% demostrativo: factura con pago en divisa vía
IniciaCierreCuponIGTF; la impresora imprime el renglón IGTF en el totalizador. - Factura y NC con todas las tasas: un ítem por cada alícuota VE (16,00 / 8,00 / 31,00 / II) y nota de crédito referenciando la factura original.
- Ítem con la palabra "total": descripción "Servicio total mensual" — verifica que no interfiere con el parsing del totalizador.
Los 5 escenarios se ejecutan en secuencia con una sola llamada:
POST /api/fiscal/bematech/certificacion (body {"modo":"dll", "rifCliente":"J-50849797-0"}),
que devuelve el resultado y los pasos de cada escenario; los tickets impresos constituyen la evidencia gráfica.
3.7 Endpoints
| Método | Ruta (/api/fiscal/bematech/...) | Descripción |
GET | info?modo=&puerto= | Sistema, versión DLL, modelo, serial, firmware, estado |
GET | puertos | Lista COM disponibles + autodetectado |
GET | alicuotas | Alícuotas programadas en el equipo |
POST | factura | Emitir factura / cupón fiscal (con o sin IGTF) |
POST | nota-credito · nota-debito | Notas referenciando la factura afectada |
POST | documento-no-fiscal | Informe gerencial (texto libre) |
POST | reporte/x · reporte/z | Lectura X · Reducción Z |
POST | reporte/rango | Memoria fiscal por rango (fechas o números Z) |
POST | reimpresion | Reimpresión desde la cinta de detalle (por COO/fecha) |
POST | gaveta · anular | Gaveta de dinero · anulación del documento abierto |
POST | fiscalizar | Cliché + RIF + alícuotas (alta del equipo, MIT) |
POST | certificacion | Ejecuta los 5 escenarios de certificación |
Además GET /api/fiscal/status?marca=bematech devuelve modelo/serial/firmware reales.
Modo por defecto: Hibrido (serial; transacciones con IGTF se enrutan por DLL), configurable.
4. The Factory HKA (Tfhka)
4.1 Biblioteca del fabricante
- DLL:
TfhkaNet.dll — biblioteca .NET administrada (AnyCPU), consumida por referencia directa (clase TfhkaNet.IF.VE.Tfhka). Sin P/Invoke. - Encoding: ISO-8859-15 (Latin-9), registrado al iniciar el agente.
- Transporte por defecto:
trama (mismo protocolo serial que utiliza la propia biblioteca del fabricante, según su documentación de protocolo); el modo dll queda disponible por request/config (ej. certificación con hardware + DLL). - Apertura del puerto (OpenFpCtrl): 9600 8-E-1 (paridad par), Handshake None, WriteTimeout 4000 ms; sondeo de líneas de control RTS/CTS → DSR/DTR → sin control.
4.2 Matriz de operaciones
| Operación | Soporte | Comando / método | Observaciones |
| Factura fiscal | DLL y trama | iR*/iS*/i01 + ítem por tasa + 3 + 101 | Los comandos ASCII son idénticos en ambos transportes (la DLL los envía con SendCmd) |
| Factura con IGTF 3% | DLL y trama | cierre 199 en lugar de 101 | Total + IGTF con pagos en divisa |
| Nota de crédito | DLL y trama | iF*/iD*/iI* + ítems d<tasa> + 3 + 101 | Referencia obligatoria: COO, fecha y serial de la factura afectada |
| Nota de débito | Endpoint | mismo flujo de devolución | El firmware HKA VE no define ND nativa; se emite como documento inverso |
| Documento no fiscal | DLL y trama | 800<texto> (línea) · 810 (cierre) | Máx 40 chars por línea |
| Reporte X | DLL y trama | DLL: PrintXReport() · trama: I0X | U0X sube la data sin imprimir |
| Reporte Z | DLL y trama | DLL: PrintZReport() · trama: I0Z | U0Z sube la data sin imprimir |
| Memoria fiscal por fechas | DLL y trama | PrintZReport(DateTime, DateTime) → I2A + ddMMyy + ddMMyy | U2A = misma consulta con salida electrónica |
| Memoria fiscal por nº Z | DLL y trama | PrintZReport(int, int) → I3A + nnnnnn + nnnnnn | U3A = salida electrónica |
| Reimpresión | DLL y trama | R<letra><ini:7><fin:7> vía SendCmd | La DLL no expone método dedicado: el comando R del protocolo se envía por su API pública |
| Anulación | DLL y trama | 7 | Anula el documento en curso; usada como rollback |
| Gaveta | DLL y trama | 0 (apertura) · CheckDrawer() (estado) | |
| Estado | DLL y trama | CheckFPrinter() / GetPrinterStatus() · trama: ENQ | Pre-flight SENIAT |
| Info / serial | DLL y trama | GetS1PrinterData() · trama: consulta S1 | Último nº de factura/NC, contador Z, RIF, serial, fecha/hora |
| Alícuotas | DLL y trama | prefijo de tasa del ítem: espacio=exento, !=general, "=reducida, #=adicional | La tasa se selecciona por canasta en cada renglón |
| Fiscalización / medios de pago | No expuesta | — | La programación de la tabla de medios y el pago parcial multi-forma requieren el manual oficial de protocolos TFHKA VE; el agente cierra por total (una forma por ticket) |
4.3 Referencia de métodos de la biblioteca TfhkaNet (clase VE.Tfhka)
| Método | Propósito |
OpenFpCtrl(puerto) / OpenFpCtrl(puerto, baud) | Abre el puerto COM (9600 8-E-1); sondea líneas de control |
CloseFpCtrl() | Cierra el puerto |
CheckFPrinter() | Pre-flight: envía ENQ y verifica que la impresora responde |
CheckDrawer() | Estado de la gaveta (bit 0x08 de la respuesta al ENQ) |
GetPrinterStatus() → PrinterStatus | Códigos y descripciones de estado (ST) y error (ER) |
SendCmd(string) | Envío de un comando ASCII (núcleo de la emisión; también usado para comandos sin método dedicado, como la familia R de reimpresión) |
GetS1PrinterData() → S1PrinterData | Datos S1: último nº de factura y NC, contador Z, RIF, nº de máquina, fecha/hora del equipo |
PrintXReport() | Imprime lectura X (I0X) |
PrintZReport() | Imprime reducción Z (I0Z) |
PrintZReport(DateTime desde, DateTime hasta) | Reporte de memoria fiscal por rango de fechas (I2A); la variante string espera yyyyMMdd y la biblioteca la convierte a ddMMyy |
PrintZReport(int desde, int hasta) | Reporte de memoria fiscal por rango de números Z (I3A, PadLeft 6) |
GetXReport() / GetZReport(...) | Variantes que SUBEN la data al PC sin imprimir (U0X / U0Z / U2A / U3A) |
4.4 Comandos ASCII enviados (idénticos en modo DLL y trama)
| Operación | Comando |
| Datos del cliente | iR*<rif> · iS*<nombre> (máx 38) · i01<dirección> (máx 40) |
| Renglón de factura | <prefijo-tasa><precio:10><cantidad:8><descripción> — prefijo: espacio=exento, !=16%, "=8%, #=adicional; precio ×100 (10 dígitos), cantidad ×1000 (8 dígitos), descripción ≤ 127. Ej.: !000000155000002000Producto A |
| Renglón de NC / referencia | d<tasa><precio:10><cantidad:8><desc> (tasa dígito 0..3) · iF*<coo> · iD*<dd/MM/yyyy> · iI*<serial> |
| Subtotal / totalizar | 3 · 101 (total, efectivo) · 199 (total + IGTF 3% con pagos en divisa). El sufijo de 1XX es el medio de pago programado en la impresora |
| Documento no fiscal | 800<texto> (línea) · 810 (cierre) |
| Gaveta / anular / X / Z | 0 · 7 · I0X · I0Z |
| Reimpresión por número | R<letra><ini:7><fin:7> — letra F facturas, C notas de crédito, D notas de débito, Z reportes Z. Ej.: RF00000120000001 |
| Reimpresión por fecha | Rf/Rc/Rd/Rz + fechas ddMMyy (relleno a 7) |
| Memoria fiscal por rango | I2A<ddMMyy><ddMMyy> (fechas) · I3A<n:6><n:6> (números Z) · U2A/U3A (salida electrónica) |
| Consultas de estado | Familia S y WM — ver 4.5 |
4.5 Consultas de datos (familia S / WM)
Los comandos de consulta devuelven una trama de datos multi-paquete (campos separados por
LF). El agente los expone con
GET /api/fiscal/hka/consulta?tipo=<comando>, devolviendo Data cruda
y Campos[] parseados. La biblioteca del fabricante expone un método
GetSxPrinterData por cada consulta; por tramas, un solo passthrough cubre todas.
| Comando | Contenido |
S1 | Datos de operación (parseados por el agente): nº de cajero, ventas del día, última factura y cantidad del día, última ND y cantidad, última NC y cantidad, documentos no fiscales y cantidad, contador de reportes de auditoría, contador de cierres Z, RIF, serial de la máquina, fecha DDMMYY y hora HHMMSS del equipo (variante larga ≥ 16 campos; existe variante corta de 12–14 campos con otro orden) |
S2 · S2E · S3 · S4 · S5 · S6 · S7 · S8P | Consultas de datos adicionales del equipo (documento en curso, tasas, medios, totales, identificación) — expuestas como passthrough; el detalle de campos es el del manual de protocolos TFHKA VE |
SG · SM · SR · SV | Consultas de datos adicionales (ídem, passthrough) |
WM12 / WM13 / WM14 / WM15 | Configuración de red del equipo: IP / CW / URL / RED |
4.6 Trama de bajo nivel (modo sin DLL)
Según la documentación de protocolo del fabricante (formato idéntico al que envía la propia
biblioteca TfhkaNet). Puerto 9600 8-EVEN-1, Latin-9. Bytes de control:
STX=0x02 ETX=0x03 EOT=0x04 ENQ=0x05 ACK=0x06 NAK=0x15 ETB=0x17.
- Comando:
STX | comando | ETX | LRC con LRC = XOR(bytes del comando) XOR ETX. Respuesta: 1 byte ACK (aceptado) o NAK (rechazado → reintento con intervalo). Sin número de secuencia rotativo (a diferencia de PnP/Rigazsa). - Estado: se envía
ENQ (un solo byte) → respuesta de 5 bytes STX | ST | ER | ETX | LRC (verificación LRC == ST^ER^0x03; si falla → error 144). - Subida de datos (S1, reportes U0X/U0Z...): paquetes
STX | datos | ETB/ETX | LRC (| EOT); el host responde ACK para pedir el siguiente bloque (ETB = hay más, ETX = último).
Decodificación de estado (byte ST, por máscara de bits)
| Máscara | Código | Significado |
0x40 | 01 | Modo no fiscal, en espera |
0x41 / 0x42 | 02 / 03 | Modo no fiscal, en transacción fiscal / no fiscal |
0x60 | 04 | Modo fiscal, en espera |
0x61 / 0x62 | 05 / 06 | Modo fiscal, en transacción fiscal / no fiscal |
0x70/0x71/0x72 | 07–09 | Memoria fiscal casi llena (en espera / transacción fiscal / no fiscal) |
0x68/0x69/0x6A | 10–12 | Memoria fiscal LLENA (variantes) |
Los estados 02/03/05/06 ("en transacción") permiten detectar un documento colgado; el bit 0x04 del ST indica Buffer Full (código 112).
Decodificación de error (byte ER)
| Máscara / valor | Código | Significado | Recuperable |
0x40 | 00 | Sin error | sí |
0x41 / 0x42 / 0x43 | 01 / 02 / 03 | Fin de papel / error mecánico con papel / ambos (tapa) | sí — el agente reintenta el mismo comando (3 × 4 s) dando tiempo al operador |
0x50 / 0x54 / 0x58 / 0x5C | 80 / 84 / 88 / 92 | Comando o valor inválido / tasa inválida / sin directivas / comando inválido | no |
0x60 / 0x64 / 0x6C | 96 / 100 / 108 | Error fiscal / error de memoria fiscal / memoria fiscal llena | no |
| — | 71 | No se detecta papel | — |
| — | 112 / 128 / 137 / 144 / 145 / 153 | Buffer lleno / error de comunicación / sin respuesta / error LRC / error interno API / error abriendo archivo | no |
4.7 Ejemplos de secuencia completa
Factura con IGTF 3% (2 ítems, gravado + exento)
ENQ → pre-flight (ST/ER sin error; si hay doc. colgado → "7")
iR*J-50849797-0 → RIF del cliente
iS*CLIENTE DEMO → razón social
i01Av. Principal, Caracas → dirección
!000000155000002000Producto A → ítem 16% (precio 15,50 ×100, cantidad 2,000 ×1000)
000000080000001000Producto exento → ítem exento (prefijo espacio)
3 → subtotal
199 → totaliza + IGTF 3% (pagos en divisa)
S1 → lee nº de factura, serial y fecha/hora del equipo
Sin IGTF, el cierre es 101. Cada comando viaja como STX·cmd·ETX·LRC y se confirma con ACK.
Nota de crédito con referencia
ENQ → pre-flight
iR*J-50849797-0 · iS*CLIENTE DEMO → cliente
iF*001234 → nº (COO) de la factura afectada
iD*05/07/2026 → fecha de la factura afectada
iI*Z1B1234567 → serial de la máquina que la emitió
d1000000155000002000Producto A → ítem devuelto (dígito de tasa 1 = general)
3 → subtotal
101 → totaliza la NC
S1 → lee LastCreditNoteNumber + serial
Reporte Z y memoria fiscal
I0Z → reducción Z del día
I2A010726080726 → MF por fechas (01/07/26 → 08/07/26)
I3A000010000015 → MF por números Z (10 → 15)
RF00012340001234 → reimpresión de la factura nº 1234
4.8 Endpoints
| Método | Ruta (/api/fiscal/hka/...) | Descripción |
GET | info?puerto=&modo= | Estado + serial + RIF + últimos documentos + contador Z + fecha/hora del equipo |
GET | puertos | COM disponibles + configurado + detectado |
GET | consulta?tipo=S1&puerto= | Passthrough de consultas S1..S8P/SG/SM/SR/SV/WM12-15 |
POST | factura · nota-credito · nota-debito | Emisión de documentos fiscales |
POST | documento-no-fiscal | DNF (líneas 800 + cierre 810) |
POST | reporte/x · reporte/z · reporte/rango | X · Z · memoria fiscal por rango (fechas o nº Z) |
POST | reimpresion | Comando R por tipo (factura/NC/ND/Z); sin número usa el último del tipo (S1) |
POST | gaveta · anular | Gaveta · anulación del documento en curso |
5. PnP (Desarrollos PnP, protocolo SENIAT 0141 v5.4)
5.1 Biblioteca del fabricante
- Fabricante: DESARROLLOS PNP, C.A. (RIF J-29366870-0). Modelos: PF-950A, PF-675A, PF-220A/D, PF-300A, PFT88A.
- DLL:
pnpdll64.dll (x64) / pnpdll.dll (x86) vía P/Invoke (stdcall, ANSI) — 44 funciones exportadas PF* según el Manual de integración DLL 2.2 del fabricante. Todas retornan char*: OK · ER (error; el detalle se lee con PFultimo()) · NP (puerto no abierto) · TO (timeout). - Naturaleza: la DLL es un wrapper de alto nivel sobre el protocolo serial 0141 v5.4 (abre el COM a 9600 8-N-1 y arma las tramas internamente); por eso el modo
trama del agente produce exactamente las mismas tramas de cable. - Transporte por defecto:
trama (protocolo 0141 v5.4 directo). Limitación de la DLL: no expone la memoria auditora, por lo que la reimpresión de 2ª vía exige el modo trama. - Nota de despliegue: la DLL muestra un aviso de caducidad cada ~2 años (mecanismo del fabricante para forzar la actualización SENIAT); al correr el agente como servicio sin escritorio interactivo el aviso no bloquea la operación.
5.2 Matriz de operaciones
| Operación | Soporte | Función DLL | Opcode trama | Observaciones |
| Factura fiscal | DLL y trama | PFabrefiscal → PFrenglon×N → PFtotal | 0x40 → 0x42×N → 0x43 → 0x45 T | IVA en formato EEDD: 1600 / 0800 / 0000 |
| Factura con IGTF 3% | DLL y trama | PFTfiscal("Pago divisa") + PFComando("E|U|<monto>") | 0x45 U + monto divisa (o B parcial+IGTF) | Campo 2 del 0x45 = base del 3% (pago en divisa); la respuesta trae el IGTF agregado (campo 4) |
| Desglose de pagos (parcial) | Trama | — | 0x45 A/B (parcial) + 0x41 texto por pago + 0x45 T | El cierre parcial imprime el total y habilita textos de pago; requiere 2º cierre |
| Nota de crédito | DLL y trama | PFDevolucion(razón, rif, comp, maqui, fecha, hora) | 0x40 calif D | comp=nº factura afectada, maqui=serial del equipo emisor, fecha DDMMAA + hora HHMM |
| Nota de débito | DLL y trama | mismo flujo de devolución | 0x40 calif B | |
| Documento no fiscal | DLL y trama | PFAbreNF / PFLineaNF / PFCierraNF | 0x48 / 0x49 / 0x4A | |
| Reporte X | DLL y trama | PFrepx() | 0x39 calif X | |
| Reporte Z | DLL y trama | PFrepz() | 0x39 calif Z | El reporte Z incluye los acumulados IGTF |
| Memoria fiscal por fechas | DLL y trama | PFrepMemNF(desf, hasf, mod) | 0x3A | Fechas yymmdd; mod M=resumen, C=salida electrónica al PC (auditoría sin imprimir) |
| Memoria fiscal por nº Z | DLL y trama | PFRepMemoriaNumero(desn, hasn, mod) | 0x3B | |
| Reimpresión (2ª vía) | Trama | — (la DLL no expone la memoria auditora; recupera el Z electrónico con calif C) | 0x3D auditor por número (ini = fin = nº) | Sin número, el agente resuelve el último del tipo leyendo el status 0x38 |
| Anulación | DLL y trama | PFCancelaDoc("C","0") | 0x14 | Rollback del documento abierto |
| Gaveta | DLL y trama | PFGaveta() | 0x7B | |
| Estado | DLL y trama | PFestatus(p) — N general, U IGTF fac/nc, W tasas | 0x38 + calificador | Pre-flight SENIAT |
| Info / serial / reloj | DLL y trama | PFSerial() / PFLeereloj() / PFversion() | 0x80 (serial + RIF) | |
| Alícuotas | DLL | PFcambiatasa(t1, t2, t3) (formato XXDD; requiere Z previo) · consulta PFestatus("W") | 0x38 W | General / reducida / aumentada |
| Fiscalización | — | La fiscalización del equipo PnP la realiza el fabricante/técnico autorizado | El agente solo consume el equipo ya fiscalizado |
5.3 Referencia de las funciones exportadas PF* de la DLL
Catálogo según el Manual de integración DLL 2.2 del fabricante (la DLL exporta 44 funciones
PF*; se documentan a continuación las funciones con uso definido en el manual,
agrupadas por categoría). Firma P/Invoke:
[DllImport("pnpdll64.dll", CallingConvention=StdCall, CharSet=Ansi)] static extern IntPtr PFxxx(string ...);
el char* devuelto se lee con Marshal.PtrToStringAnsi.
Puerto y emisión
| Función | Parámetros | Propósito |
PFabrepuerto(num) | nº del COM (ej. "1") | Abre el puerto (internamente 9600 8-N-1) |
PFcierrapuerto() | — | Cierra el puerto |
PFabrefiscal(razon, rif) | razón social + RIF del cliente | Abre factura fiscal (0x40) |
PFrenglon(desc, cant, monto, iva) | descripción (20 chars PF-220 / 40 PF-300) · cantidad nnnn.nnn · precio unit. sin impuesto nnnnnn.nn · IVA EEDD (0000/0800/1200/1600) | Renglón de venta (0x42) |
PFTfiscal(txt) | texto | Línea de texto fiscal (ej. "Pago divisa" antes del cierre IGTF) |
PFtotal() | — | Cierre total (0x45 T): totaliza y asigna correlativo fiscal |
PFtoteconomico() | — | Cierre económico (total sin desglose extendido) |
PFparcial() | — | Cierre parcial (0x45 A): imprime total, permite texto de pago, requiere 2º cierre |
PFComando(cmd) | "CMD|CALIF|MONTO" — CMD = letra ASCII del código hex (E=0x45, 9=reporte); montos sin punto, 2 decimales implícitos | Comando crudo del protocolo — permite integrar todo el equipo (ej. E|U|25000 = cierre total + IGTF, base 250,00) |
PFCancelaDoc(mod, mon) | ej. ("C","0") | Anula el documento abierto (0x14) |
Documento no fiscal
| Función | Propósito |
PFAbreNF() / PFLineaNF(txt) / PFCierraNF() | Abre / imprime línea / cierra el documento no fiscal (0x48/0x49/0x4A) |
Nota de crédito, reportes y memoria fiscal
| Función | Parámetros | Propósito |
PFDevolucion(razon, rif, comp, maqui, fecha, hora) | cliente + nº factura + serial del equipo emisor + DDMMAA + HHMM | Nota de crédito / devolución (0x40 calif D) |
PFrepx() / PFrepz() | — | Reporte X / reducción Z (0x39) |
PFrepMemNF(desf, hasf, mod) | fechas yymmdd + modo M/C | Memoria fiscal por rango de fechas (0x3A) |
PFRepMemoriaNumero(desn, hasn, mod) | números Z + modo M/C | Memoria fiscal por rango de números Z (0x3B) |
PFestatus(p) | calificador N/U/W/... | Estado del equipo (0x38); U devuelve acumulados IGTF fac/nc |
PFultimo() | — | Detalle del último error tras un retorno ER |
PFSerial() / PFLeereloj() / PFversion() | — | Serial del equipo / reloj interno / versión |
Configuración y periféricos
| Función | Propósito |
PFcambiatasa(t1, t2, t3) | Programa las tasas general/reducida/aumentada (formato XXDD; requiere Z previo) |
PFcambiofecha(f, h) | Fecha y hora del equipo |
PFCambtipoContrib(t) / PFTipoImp(m) | Tipo de contribuyente / tipo de impresión |
PFreset() | Reset del equipo |
PFGaveta() / PFCortar() | Gaveta de dinero / corte de papel |
PFBarra(b) | Código de barras |
PFDisplay950(t) | Display del PF-950 |
PFLogoClick() | Logo |
PFSlipON() / PFSLIPOFF() | Modo slip (validadora) on/off |
PFvalida675(...) / PFCheque2(...) / PFendoso(...) / PFVoltea() | Validación de documentos, impresión de cheques y endoso (PF-675) |
5.4 Tramas seriales (protocolo 0141 v5.4)
Puerto 9600 8N1, DTR/RTS activos, ISO-8859-1. Maestro/esclavo (el host siempre inicia). Trama:
STX(0x02) | Sec(0x20–0x7F) | Comando(0x30–0xAF) | (FS 0x1C + campo)* | ETX(0x03) | BCC(4 hex),
donde BCC = suma simple de los bytes STX..ETX inclusive, expresada en 4 caracteres hexadecimales,
y Sec es un secuencial cíclico que debe diferir del anterior (la respuesta repite el mismo).
El byte 0x12 indica "equipo procesando" (extiende el timeout +800 ms por aparición).
Respuesta: STX | Sec | Comando | EstadoImpresora(4 hex) | EstadoFiscal(4 hex) | datos | ETX | BCC.
Éxito = ausencia del literal ERROR+nº en la respuesta. RESET por software: secuencia
cruda 0x07 0x08 ... 0x17 (sin framing).
| Comando | Opcode |
Abrir factura (calif D=NC, B=ND) | 0x40 |
Vender ítem (calif M suma, m anula) | 0x42 |
| Subtotal | 0x43 (Rigazsa: 0x44) |
| Cierre | 0x45 con A parcial · B parcial+IGTF · T total · U total+IGTF 3% |
| Texto de pago / texto libre en factura | 0x41 |
| DNF: abre / texto / cierra | 0x48 / 0x49 / 0x4A |
| Reporte X / Z | 0x39 |
Memoria fiscal por fecha / por nº Z (calif M resumen, C salida al PC) | 0x3A / 0x3B |
| Memoria auditora por fecha / por número (reimpresión 2ª vía) | 0x3C / 0x3D |
Status (calif N, U=IGTF fac/nc, W=tasas) | 0x38 |
| Anular documento abierto | 0x14 |
| Gaveta | 0x7B |
| Serial + RIF del equipo | 0x80 |
Estados (16 bits, en la respuesta de cada comando)
| Estado fiscal (bit) | Condición |
| 0 / 1 | Error memoria fiscal (bit0+bit7 = MF llena) / error memoria de trabajo |
| 3 / 4 / 5 | Comando no reconocido / campo inválido / comando no válido para el estado |
| 6 | Desbordamiento de totales |
| 7 / 8 | Memoria fiscal llena / casi llena |
| 11 | Necesario cierre Z |
| 12 / 13 | Factura fiscal abierta / documento no fiscal abierto (documento colgado) |
| 15 | OR lógico de errores (chequeo rápido) |
| Estado impresora (bit) | Condición |
| 2 / 3 | Falla de impresora / fuera de línea |
| 14 | Sin papel |
| 15 | OR lógico de errores |
5.5 IGTF 3% (cierre 0x45)
El IGTF no es una función aparte: se aplica en el cierre 0x45 con los calificadores
B (parcial + IGTF) y U (total + IGTF 3%). El campo 2 del cierre es el
monto pagado en divisa (base del 3%; si ≥ total, el 3% se calcula sobre el total)
y el campo 4 de la respuesta devuelve el IGTF agregado. Vía DLL: PFComando("E|B|1000")
(parcial+IGTF, base 10,00) o PFComando("E|U|25000") (total+IGTF, base 250,00).
Acumulados: PFestatus("U") → IGTF facturas / IGTF notas de crédito; el reporte Z también
los incluye. El soporte IGTF 3% consta en el changelog del protocolo del fabricante (2022-03-13).
5.6 Ejemplos de secuencia completa
Factura con IGTF y desglose de pagos (modo trama)
0x38 N → pre-flight (estados 4 hex; si bit12/13 → 0x14 anula colgado)
0x40 CLIENTE DEMO | J-50849797-0 → abre factura
0x42 Producto A | 2.000 | 15.50 | 1600 | M → ítem 16%
0x42 Producto B | 1.000 | 8.00 | 0000 | M → ítem exento
0x43 → subtotal
0x45 B | 11600 → cierre parcial + IGTF (base divisa 116,00)
0x41 "Divisa USD 116,00" | S → texto por cada pago
0x45 T → cierre final (asigna correlativo fiscal)
0x80 → lee serial + RIF del equipo
Sin desglose de pagos, el cierre es directo: 0x45 U + monto (con IGTF) o 0x45 T. El número fiscal se lee de la respuesta del cierre.
Factura con IGTF (modo DLL)
PFabrepuerto("1") → OK
PFabrefiscal("CLIENTE DEMO", "J-50849797-0")
PFrenglon("Producto A", "2.000", "15.50", "1600")
PFTfiscal("Pago divisa")
PFComando("E|U|11600") → cierre total + IGTF 3% (base 116,00, sin punto)
PFSerial() · PFcierrapuerto()
Nota de crédito con referencia (modo DLL)
PFabrepuerto("1")
PFDevolucion("CLIENTE DEMO", "J-50849797-0", "001234", "NST0000095", "050726", "1430")
// nº factura afectada + serial del equipo emisor + fecha DDMMAA + hora HHMM
PFrenglon("Producto A", "2.000", "15.50", "1600")
PFtotal() → cierra la NC
PFcierrapuerto()
Reporte Z, memoria fiscal y reimpresión
PFrepz() → reducción Z (trama: 0x39 Z)
PFrepMemNF("260701", "260708", "M") → MF por fechas (trama: 0x3A)
PFRepMemoriaNumero("10", "15", "M") → MF por números Z (trama: 0x3B)
PFRepMemoriaNumero("12", "12", "C") → recupera el Z nº 12 electrónico (sin imprimir)
0x3D 1234 | 1234 | F → reimpresión 2ª vía de la factura 1234 (solo trama)
5.7 Endpoints
| Método | Ruta (/api/fiscal/pnp/...) | Descripción |
GET | info?puerto=&modo= · puertos | Estado/serial · COM disponibles |
POST | factura · nota-credito · nota-debito | Emisión (con/sin IGTF; NC/ND referenciadas) |
POST | documento-no-fiscal · gaveta · anular | DNF · gaveta · anulación |
POST | reporte/x · reporte/z · reporte/rango | X · Z · memoria fiscal por rango |
POST | reimpresion | 2ª vía desde la memoria auditora (0x3D) |
Selector modo=dll|trama por request o config (Pnp:Transporte, hot-reload desde la bandeja).
6. Rigazsa
6.1 Protocolo (sin DLL del fabricante)
- Sin DLL — opera exclusivamente por tramas, como extensión del protocolo PnP 0141 (misma implementación del agente con perfil
EsRigaza), según la documentación del protocolo Rigazsa. - Transporte: RS-232 / USB-CDC a 9600 8N1, o red (UDP/TCP) mediante el puente WiFi integrado del equipo RIGAZSA-1.
- Diferencias sobre PnP base: subtotal con opcode
0x44 (en vez de 0x43); montos como enteros escalados (cantidad ×1000, precio ×100, tasa ×100) en lugar de formato con punto; secuencial restringido a 0x1F–0x7E; desglose de pagos con el comando 0x44 PAGO_FF (Descripción | Monto | T | tasa) antes del cierre; campo vacío se envía como NULO (0x7F). - Tasas IVA por defecto: General 16 %, Reducida 8 %, Adicional 31 %, IVA-4 12 %, Exento 0 %. En fiscalización se envían ×100 y terminan con la palabra
ERGA (Exento, Reducido, Gravable, Ampliado). - Validación de RIF de cliente: primera letra en E/G/J/P/V, resto numérico, máx. 12 caracteres.
- Estados: mismos bitmask de 16 bits que PnP (5.4), con bits adicionales del estado fiscal: bit 9 error RTC, bit 10 memoria auditora llena, bit 14 modo entrenamiento.
6.2 Matriz de operaciones
| Operación | Soporte | Comando | Observaciones |
| Factura fiscal | Trama | 0x40 → 0x42×N → 0x44 subtotal → cierre 0x45 | Montos enteros escalados |
| Factura con IGTF 3% | Trama | 0x45 U + monto divisa | Igual semántica que PnP |
| Desglose de pagos | Trama | 0x44 PAGO_FF por pago (Descripción|Monto|T|tasa) antes del cierre | Extensión Rigazsa |
| Nota de crédito / débito | Trama | 0x40 calif D / B + referencia (nº factura, serial, fecha, hora) | |
| Documento no fiscal | Trama | 0x48 / 0x49 / 0x4A | |
| Reporte X / Z | Trama | 0x39 (X / Z) | |
| Memoria fiscal por fechas | Trama | 0x3A — fechaIni aammdd | fechaFin aammdd | calif (D imprime, C no imprime) | |
| Memoria fiscal por nº Z | Trama | 0x3B — zIni | zFin | calif | |
| Reimpresión / auditoría por fecha | Trama | 0x3C — fechaIni | fechaFin | tipo (F factura, C NC, N no fiscal, T todos) | |
| Reimpresión / auditoría por número | Trama | 0x3D — numIni | numFin | tipo | Reimpresión de UN documento = ini = fin = nº. Si el rango no existe, el equipo responde error y el agente lo propaga con 422 |
| Anulación | Trama | 0x14 | ⚠️ En el protocolo Rigazsa este byte también corresponde a CONFIGURATIONWIFI (el contexto los distingue) |
| Gaveta | Trama | 0x7B | |
| Estado | Trama | 0x38 / 0x4D consulta de estado | |
| Info / serial | Trama | 0x80 — devuelve serial (ej. NST0000095) + RIF fiscalizado | El mismo byte consulta el estado de fiscalización |
| Alícuotas / fiscalización | Trama | 0x01 / 0x06 / 0x07 / 0x10 — ver 6.4 | Alta remota del equipo |
| Fecha / hora | Trama | 0x58 set (ddmmaa | hhmmss) · 0x59 get | Sincronía horaria (requisito SENIAT) |
6.3 Tabla completa de comandos
Base PnP (facturación / reportes / estado)
| Hex | Constante | Acción |
0x40 | ABRIR_FF | Abrir factura fiscal (calif D → nota de crédito) |
0x41 | TEXTO_FF | Texto libre en factura |
0x42 | ITEM_FF | Línea de ítem: desc, cant ×1000, precio ×100, tasa IVA ×100, calificador |
0x44 | SUB_FF / PAGO_FF / Devolucion_MF | Subtotal / desglose de pago / devolución (multiplexado por calificador) |
0x45 | CERRAR_FF | Cerrar factura (A/B/T/U) |
0x46 | BARCODE | Código de barras |
0x48/0x49/0x4A | DOC_NO_FF | Abrir / texto / cerrar documento no fiscal |
0x4B | CORTAR / AVANCE_PAPEL | Control de papel |
0x39 | Report (X/Z) | Reporte X / reducción Z |
0x3A / 0x3B | MEMORIA_FISCAL_FECHA / _NUMERO | Memoria fiscal por rango de fecha / número Z |
0x3C / 0x3D | AUDITOR_FECHA / _NUMERO | Memoria auditora por fecha / número (2ª vía) |
0x38 / 0x4D | STATUS_IF / CONSULTA_ESTADO | Estado de la impresora fiscal |
0x4C / 0x4E / 0x4F | CONSULTAR_DIRECCION / _ENCABEZADO / _PIE_PAGINA | Lectura de la configuración impresa |
0x80 | SERIAL_PRINTER | Serial + RIF de la máquina |
Extensiones Rigazsa — fiscalización remota
| Hex | Constante | Trama de datos |
0x01 | CONF_DATOS_FISCALIZACION | razón social + RIF (con máscara de formato: negrita/centrado) |
0x07 | CONF_LINEAS_ESTABLECIMIENTO | hasta 11 líneas de dirección |
0x06 | CONF_TASAS_IVA | contraseña + tasa1 ×100 + tasa2 + tasa3 + ERGA. La contraseña se deriva de la identidad del autorizador + fecha (no es estática) |
0x10 | CONF_GUARDAR_FISCALIZACION | confirma/persiste la fiscalización |
0x5D / 0x5E | CONF_ENCABEZADO / CONF_PIE_PAGINA | Encabezado / pie del ticket |
0x58 / 0x59 | SET/GET fecha-hora | ddmmaa | hhmmss |
0x09 | HORA_MILITAR | Modo 24 h |
Extensiones Rigazsa — conectividad y envío Z al SGEF
| Hex | Constante | Acción |
0x14 | CONFIGURATIONWIFI | SSID, clave, DHCP S/N, IP, máscara, gateway, DNS1, DNS2 (campo omitido → 0x7F) |
0x15 | CONFIGURATIONURLAPPREMOTE | URL a la que el equipo sube los reportes Z (SGEF del SENIAT) |
0x16 / 0x17 / 0x18 / 0x54 | Firmware | URL de firmware / versión de la impresora / versión interna / URL de descarga OTA |
0x08 | INTERVAL_TIME_SEND_REPORTSZ | Intervalo en minutos del envío automático de Z al SGEF (default 60) |
0x51 | ESTADO_ENVIOS_Z | Consulta el estado de los envíos Z al SGEF |
0x11 / 0x12 / 0x13 | MODO_PAGINACION / ENTER_MODE / ACCUMESSCONFIGXORZ | Configuración de impresión (nota: 0x12 es también el byte "procesando" en las respuestas) |
El equipo RIGAZSA-1 puede subir sus reportes Z de forma autónoma al SGEF
(Sistema de Gestión de Envíos Fiscales del SENIAT) sin depender del PC, usando los comandos
0x15 (URL destino) y 0x08 (intervalo).
6.4 Ejemplo de secuencia completa (factura con desglose de pagos)
0x38 N → pre-flight
0x40 CLIENTE DEMO | J-50849797-0 → abre factura
0x42 Producto A | 2000 | 1550 | 1600 | M → ítem (cant ×1000, precio ×100, tasa ×100)
0x44 → subtotal (opcode Rigazsa)
0x44 PAGO_FF: Efectivo Bs | 1500 | T | 0 → desglose de pago 1
0x44 PAGO_FF: Divisa USD | 11600 | T | 0 → desglose de pago 2
0x45 U | 11600 → cierre total + IGTF (base divisa)
0x80 → serial + RIF (respuesta: estados 4 hex + NSTxxxxxxx + RIF)
Ejemplo real de respuesta al 0x80:
STX 0x21 0x80 | "0000" | "0000" | "NST0000095" | "J-1234951-2" | ETX "0728"
(estado impresora, estado fiscal, serial, RIF, BCC).
6.5 Endpoints
| Método | Ruta (/api/fiscal/rigaza/...) | Descripción |
GET | info?puerto= · puertos | Estado/serial · COM disponibles |
POST | factura · nota-credito · nota-debito | Emisión (mismas operaciones que PnP con el perfil Rigazsa) |
POST | documento-no-fiscal · gaveta · anular | DNF · gaveta · anulación |
POST | reporte/x · reporte/z · reporte/rango | X · Z · memoria fiscal por rango |
POST | reimpresion | 2ª vía desde la memoria auditora (0x3D) |
7. Estado de validación por fabricante
| Marca | DLL | Tramas | Validación con hardware |
| Bematech | BemaFI64.dll 5.4.1.47 (con IGTF) | Opcodes ESC (framing en validación contra emulador) | Validada con MP-4000 TH FI real (firmware 01.00.22/23) |
| The Factory HKA | TfhkaNet.dll (referencia .NET) | STX/cmd/ETX/LRC | En producción |
| PnP | pnpdll64.dll (44 exports PF*) | 0141 v5.4 | Implementada según la especificación del fabricante; validación de campos de respuesta pendiente contra hardware físico |
| Rigazsa | No existe | Extensión de 0141 (subtotal 0x44, montos escalados) | Según la documentación del protocolo del fabricante; a confirmar contra hardware físico |
7.1 Casos de uso de validación (agente → emulador)
Antes de operar contra el ERP, el pipeline agente → impresora se valida con el emulador fiscal
(zentto-fiscal-emulator) sobre un par COM virtual (com0com, ej. COM10 ↔ COM11):
el agente envía tramas correctas (con sus adapters reales) y
erróneas (bytes crudos); el emulador debe aceptar las primeras (imprime el ticket)
y rechazar las segundas (error/NAK, sin impresión).
Zentto.LocalFiscalAgent.exe --casos-de-uso --puerto COM10 --marca pnp
# marcas: pnp | rigaza | hka
| Marca | Casos correctos (adapters reales) | Casos erróneos (deben rechazarse) |
| PnP / Rigazsa | Factura 3 ítems (16 % / exento / 31 %), nota de crédito, reporte X, reporte Z | BCC inválido, comando desconocido (0x99), vender sin factura abierta (0x42) |
| HKA | Factura, nota de crédito, reporte X, reporte Z | LRC inválido, comando desconocido |
Resultado esperado: 7 correctos, 0 fallidos (exit code 0). La aceptación/rechazo se
verifica por ausencia/presencia de ERROR (PnP/Rigazsa) o por ACK/NAK (HKA).
8. Referencias
Documentación de los fabricantes
- Bematech: manual de la DLL BemaFI (funciones
Bematech_FI_*, incluida la documentación de Bematech_FI_ImpresionCintaDetalle para reimpresión) y manual de Protocolo de Comunicación Directo (opcodes ESC); Comunicado POS Venezuela 22-mar-2022 (adecuación IGTF, BemaFI32 ≥ 5.4.1.40). - Desarrollos PnP: Manual de integración DLL 2.2 (44 exports
PF*, retornos OK/ER/NP/TO, PFComando) y protocolo SENIAT 0141 v5.4 (framing STX/Sec/FS/ETX/BCC, estados, IGTF en el cierre 0x45). - The Factory HKA: documentación de la biblioteca
TfhkaNet.dll (clase IF.VE.Tfhka, ejemplo oficial de integración) y Manual de Protocolos y Comandos TFHKA VE (familias R de reimpresión, consultas S/WM). - Rigazsa: documentación del protocolo Rigazsa (base 0141 con extensiones de fiscalización, conectividad y envío Z al SGEF).
Documentación del repositorio zentto-fiscal-agent
docs/CERTIFICACION_BEMATECH.md — dossier de certificación con hardware Bematech (los 5 elementos requeridos). CASOS_DE_USO.md y CasosDeUso.cs — CLI de validación agente→emulador sobre COM virtual (--casos-de-uso --puerto COMn --marca <marca>). PASO_A_PASO_SERVICIO.md, INSTALAR_SERVICIO.ps1, installer.iss — instalación del servicio. - Código de integración por marca:
Bematech/ (BematechNative, BematechFiscalService, BematechSerialProtocol), Hka/ (HkaFiscalService, HkaTramaService, HkaSerialProtocol), Pnp/ (PnpNative, PnpNativeService, PnpFiscalService, PnpProtocol — este último con el perfil Rigazsa).