🚀 API: Comienzo Rápido (Quickstarts)
El siguiente catálogo agrupa los ejemplos de integración unificada oficial extraídos nativamente de nuestra página web. Selecciona tu lenguaje de programación para ver la implementación del Patrón O(1) Polling.
Ejemplo en C# .NET
csharp
/*
============================================================================
GUÍA DE INTEGRACIÓN RÁPIDA: VERI*FACTU MICRO-SERVICE VÍA C# (.NET)
============================================================================
Estimado Desarrollador .NET:
Este archivo demuestra el "Patrón de Sondeo (Polling) O(1)" en C# puro,
utilizando HttpClient y JSON.NET de manera nativa sin librerías externas.
INSTRUCCIONES DE USO:
1. Pega este código en tu aplicación de consola (Console App) y arráncala.
2. Usamos POST hacia /v1/ingesta con la factura en formato JSON para que el
Micro-Servicio firme matemáticamente la factura por debajo.
3. El bucle \`while/await Task.Delay\` consulta instantáneamente a /v1/check_status
para esperar confirmación real (CSV) sin sobrecargar tu servidor.
EJECUCIÓN RÁPIDA DE PRUEBA (DESDE POWERSHELL):
Abre una terminal en esta misma carpeta y lanza estos comandos en bloque:
dotnet new console -n PruebaCliente -f net8.0
Copy-Item rest_csharp_demo.cs PruebaCliente\Program.cs -Force
cd PruebaCliente
dotnet run
============================================================================
*/
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace VeriFactuDemo
{
class Program
{
private static readonly HttpClient client = new HttpClient();
// Configuración del MicroServer
// Ej Local: "http://127.0.0.1:8000" | Red Local: "http://192.168.1.100:8000" | Nginx Remoto: "https://api.tu-empresa.com"
private const string BASE_URL = "http://127.0.0.1:8000";
static async Task Main(string[] args)
{
Console.WriteLine("===============================================");
Console.WriteLine("1. ENVIANDO INGESTA A MICRO-SERVICIO (C#)");
Console.WriteLine("===============================================");
Random rnd = new Random();
int numRandom = rnd.Next(100000, 999999);
string serie = "CSH";
string emisor = "B12345678"; // Sustituir por NIF real autorizado en la BD
var payload = new
{
cabecera = new
{
emisor = emisor,
numfactura = numRandom.ToString(),
serie = serie,
rectificativa = "N",
tipofacturarectificativa = "",
fecharectificada = "",
facturarectificada = "",
rectificativasustitucion = "",
facturaf3 = "N",
numseriesustituidaf3 = "",
fechafactsustituidaf3 = "",
descripcionoperacion = "Factura vía C# Nativo",
eliminacion = "N",
tipodoc = "01",
pais = "ES",
nif = "B98765432",
nombre = "EMPRESA DEMO S.L.",
fecha = DateTime.Now.ToString("dd/MM/yyyy"),
base1 = "100.00", piva1 = "21.00", iva1 = "21.00",
base2 = "", piva2 = "", iva2 = "",
base3 = "", piva3 = "", iva3 = "",
base4 = "", piva4 = "", iva4 = "",
@base = "100.00",
totaliva = "21.00",
total = "121.00"
},
detalle = new
{
@base = new[] { "100.00", "", "", "" },
piva = new[] { "21.00", "", "", "" },
iva = new[] { "21.00", "", "", "" },
precargo = new[] { "", "", "", "" },
recargo = new[] { "", "", "", "" }
},
metadata = new
{
insertar_en_db = true
}
};
Console.WriteLine($"Factura: {serie}-{numRandom}");
var jsonPayload = JsonSerializer.Serialize(payload);
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
try
{
// 1. Enviar Ingesta
var responseIngesta = await client.PostAsync($"{BASE_URL}/v1/ingesta", content);
string resBody = await responseIngesta.Content.ReadAsStringAsync();
if (responseIngesta.IsSuccessStatusCode)
{
Console.WriteLine(" > INGESTA OK (Recibida en la BD local). Esperando a la AEAT...\n");
}
else
{
Console.WriteLine($" > ERROR DE INGESTA (HTTP {(int)responseIngesta.StatusCode}):");
Console.WriteLine(resBody);
return;;
}
}
catch (Exception ex)
{
Console.WriteLine($" > Falla crítica de red: {ex.Message}");
return;;
}
// 2. Polling O(1)
Console.WriteLine("===============================================");
Console.WriteLine("2. ESPERANDO RESPUESTA DE LA AEAT (Polling)");
Console.WriteLine("===============================================");
JsonNode finalPendiente = null;
int maxIntentos = 15;
for (int i = 1; i <= maxIntentos; i++)
{
await Task.Delay(1000); // Sleep asíncrono no bloqueante
Console.WriteLine($" [Intento {i}/{maxIntentos}] Comprobando /v1/check_status...");
try
{
string urlCheck = $"{BASE_URL}/v1/check_status?emisor={emisor}&serie={serie}&num={numRandom}";
var responseCheck = await client.GetAsync(urlCheck);
if (responseCheck.IsSuccessStatusCode)
{
var jsonString = await responseCheck.Content.ReadAsStringAsync();
var data = JsonNode.Parse(jsonString);
var statusTxt = data?["status"]?.ToString();
if (!string.IsNullOrEmpty(statusTxt) && statusTxt != "pending")
{
finalPendiente = data;
Console.WriteLine(" ! Respuesta Definitiva Detectada !\n");
break;;
}
}
else
{
Console.WriteLine($" Error consultando check_status. HTTP: {(int)responseCheck.StatusCode}");
}
}
catch (Exception ex)
{
Console.WriteLine($" Excepción durante polling: {ex.Message}");
}
}
// 3. Resultado Final
Console.WriteLine("===============================================");
Console.WriteLine("3. RESULTADO FINAL (RECUPERADO DE AEAT)");
Console.WriteLine("===============================================");
if (finalPendiente != null)
{
Console.WriteLine($" ESTADO AEAT : {finalPendiente?["status"]?.ToString()} (Raw: {finalPendiente?["raw_status"]?.ToString()})");
Console.WriteLine($" MENSAJE AEAT : {finalPendiente?["mensaje"]?.ToString()}");
var csv = finalPendiente?["csv"]?.ToString();
if (!string.IsNullOrEmpty(csv))
{
Console.WriteLine($" C.S.V. : {csv}");
}
}
else
{
Console.WriteLine(" TIMEOUT (15s): La AEAT está tardando o hay cola.");
}
}
}
}Ejemplo en Node
javascript
/**
* ============================================================================
* GUÍA DE INTEGRACIÓN RÁPIDA: VERI*FACTU MICRO-SERVICE VÍA NODE.JS
* ============================================================================
*
* Estimado Desarrollador:
* Este script es una demostración 100% nativa (libre de librerías externas)
* que te enseña la forma más limpia, eficiente y no-bloqueante de certificar
* facturas ante la Agencia Tributaria usando nuestro Micro-Servicio.
*
* FLUJO RECOMENDADO O(1):
* 1. POST a /v1/ingesta: Envías tu JSON en claro. El Micro-Servicio local
* se encarga de toda la criptografía RSA y el envío SOAP en background.
* 2. POLLING ASÍNCRONO a /v1/check_status: Entra en un bucle asíncrono
* preguntando por ESA factura específica (\`serie\` y \`num\`). Al ser una
* búsqueda exacta (O(1)), no sobrecarga tu BD.
* 3. RECEPCIÓN CSV: En aprox 5-8 segundos, recogerás el Estado y el Código
* Seguro de Verificación (CSV) oficial de la AEAT para adjuntarlo a tu ERP.
*
* Ejecución: node rest_node_demo.js
* ============================================================================
*/
const http = require('http');
// Ej Local: '127.0.0.1' | Red Local: '192.168.1.100' | Nginx Remoto: 'api.tu-empresa.com'
const MICRO_SERVER_HOST = '127.0.0.1';
// Si usas Nginx con HTTPS remoto, el puerto suele ser 443
const MICRO_SERVER_PORT = 8000;
// Replicando la lógica de 'proxyRequest' de tu server.js pero para uso directo
function peticionHttp(method, path, data = null) {
return new Promise((resolve, reject) => {
const options = {
hostname: MICRO_SERVER_HOST,
port: MICRO_SERVER_PORT,
path: path,
method: method,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
};
let postData = '';
if (data && (method === 'POST' || method === 'PUT')) {
postData = JSON.stringify(data);
options.headers['Content-Length'] = Buffer.byteLength(postData);
}
const req = http.request(options, (res) => {
let body = '';
res.setEncoding('utf8');
res.on('data', (chunk) => body += chunk);
res.on('end', () => {
let json = {};
try {
json = JSON.parse(body);
} catch (e) {
json = { raw: body };
}
// Normalizar respuesta
const isOk = res.statusCode >= 200 && res.statusCode < 300 && json.status !== 'error';
resolve({
code: res.statusCode,
ok: isOk,
body: json
});
});
});
req.on('error', (e) => reject(e));
if (postData) {
req.write(postData);
}
req.end();
});
}
async function runDemo() {
const ahora_str = new Date().toLocaleDateString('es-ES', { day: '2-digit', month: '2-digit', year: 'numeric' });
const num_random = Math.floor(Math.random() * 900000) + 100000;
const payload = {
cabecera: {
emisor: "B12345678", // Sustituir por el NIF real autorizado en tu BD
numfactura: String(num_random),
serie: "NODE",
rectificativa: "N",
tipofacturarectificativa: "",
fecharectificada: "",
facturarectificada: "",
rectificativasustitucion: "",
facturaf3: "N",
numseriesustituidaf3: "",
fechafactsustituidaf3: "",
descripcionoperacion: "Factura vía NodeJS nativo",
eliminacion: "N",
tipodoc: "01",
pais: "ES",
nif: "B98765432",
nombre: "EMPRESA DEMO S.L.",
fecha: ahora_str,
base1: "100.00", piva1: "21.00", iva1: "21.00",
base2: "", piva2: "", iva2: "",
base3: "", piva3: "", iva3: "",
base4: "", piva4: "", iva4: "",
base: "100.00",
totaliva: "21.00",
total: "121.00"
},
detalle: {
base: ["100.00", "", "", ""],
piva: ["21.00", "", "", ""],
iva: ["21.00", "", "", ""],
precargo: ["", "", "", ""],
recargo: ["", "", "", ""]
},
metadata: {
insertar_en_db: true
}
};
console.log("===============================================");
console.log("1. ENVIANDO INGESTA (NODE.JS HTTP)");
console.log("===============================================");
console.log(\`Factura: \${payload.cabecera.serie}-\${payload.cabecera.numfactura}\`);
try {
const resIngesta = await peticionHttp('POST', '/v1/ingesta', payload);
if (resIngesta.ok) {
console.log(" > INGESTA OK (Recibida en la BD local). Esperando a la AEAT...
");
} else {
console.error(\` > ERROR DE INGESTA (HTTP \${resIngesta.code}):\`, JSON.stringify(resIngesta.body, null, 2));
process.exit(1);
}
console.log("===============================================");
console.log("2. ESPERANDO RESPUESTA DE LA AEAT (Polling)");
console.log("===============================================");
let finalPendiente = null;
const maxIntentos = 15;
const emisor = payload.cabecera.emisor;
const serie = payload.cabecera.serie;
const num = payload.cabecera.numfactura;
for (let i = 1; i <= maxIntentos; i++) {
await new Promise(r => setTimeout(r, 1000)); // sleep(1) async
console.log(\` [Intento \${i}/\${maxIntentos}] Comprobando /v1/check_status...\`);
const queryParams = new URLSearchParams({ emisor, serie, num }).toString();
const resCheck = await peticionHttp('GET', \`/v1/check_status?\${queryParams}\`);
if (resCheck.code === 200) {
if (resCheck.body && resCheck.body.status && resCheck.body.status !== 'pending') {
finalPendiente = resCheck.body;
console.log(" ! Respuesta Definitiva Detectada !
");
break;;
}
} else {
console.error(\` Error consultando checkStatus (HTTP \${resCheck.code})\`, resCheck.body);
}
}
// RESULTADO FINAL
console.log("===============================================");
console.log("3. RESULTADO FINAL (RECUPERADO DE AEAT)");
console.log("===============================================");
if (finalPendiente) {
console.log(\` ESTADO AEAT : \${finalPendiente.status} (Raw: \${finalPendiente.raw_status || ''})\`);
console.log(\` MENSAJE AEAT : \${finalPendiente.mensaje || ''}\`);
if (finalPendiente.csv) {
console.log(\` C.S.V. : \${finalPendiente.csv}\`);
}
} else {
console.log(" TIMEOUT (15s): La AEAT está tardando o hay cola.");
}
} catch (error) {
console.error("Error catastrofico en la peticion:", error);
}
}
// Ejecutar
runDemo();Ejemplo en Python
python
"""
============================================================================
GUÍA DE INTEGRACIÓN RÁPIDA: VERI*FACTU MICRO-SERVICE VÍA PYTHON
============================================================================
Estimado Desarrollador:
Este script es una demostración en Python puro que ilustra el
"Patrón de Sondeo (Polling) O(1)".
INSTRUCCIONES DE USO:
1. Define el diccionario de tu factura (payload) y lánzalo vía POST a /v1/ingesta.
2. Inmediatamente entra en un bucle ligero usando \`time.sleep(1)\` para buscar la
respuesta en /v1/check_status asegurando consumo mínimo de DB.
3. El Micro-Servicio te dará el Código Seguro de Verificación oficial (CSV).
Ejecución: python rest_python_demo.py
============================================================================
"""
import time
import requests
import json
from datetime import datetime
import random
# Configuración del MicroServer
# Ej Local: "http://127.0.0.1:8000" | Red Local: "http://192.168.1.100:8000" | Nginx: "https://api.tu-empresa.com"
BASE_URL = "http://127.0.0.1:8000"
HEADERS = {
"Content-Type": "application/json",
"Accept": "application/json"
}
def run_demo():
print("===============================================")
print("1. ENVIANDO INGESTA A MICRO-SERVICIO (PYTHON)")
print("===============================================")
ahora_str = datetime.now().strftime("%d/%m/%Y")
num_random = random.randint(100000, 999999)
payload = {
"cabecera": {
"emisor": "B12345678", # Sustituir por NIF real autorizado en la BD
"numfactura": str(num_random),
"serie": "PYT",
"rectificativa": "N",
"tipofacturarectificativa": "",
"fecharectificada": "",
"facturarectificada": "",
"rectificativasustitucion": "",
"facturaf3": "N",
"numseriesustituidaf3": "",
"fechafactsustituidaf3": "",
"descripcionoperacion": "Factura vía Python DSK",
"eliminacion": "N",
"tipodoc": "01",
"pais": "ES",
"nif": "B98765432",
"nombre": "EMPRESA DEMO S.L.",
"fecha": ahora_str,
"base1": "100.00", "piva1": "21.00", "iva1": "21.00",
"base2": "", "piva2": "", "iva2": "",
"base3": "", "piva3": "", "iva3": "",
"base4": "", "piva4": "", "iva4": "",
"base": "100.00",
"totaliva": "21.00",
"total": "121.00"
},
"detalle": {
"base": ["100.00", "", "", ""],
"piva": ["21.00", "", "", ""],
"iva": ["21.00", "", "", ""],
"precargo": ["", "", "", ""],
"recargo": ["", "", "", ""]
},
"metadata": {
"insertar_en_db": True
}
}
emisor = payload["cabecera"]["emisor"]
serie = payload["cabecera"]["serie"]
num = payload["cabecera"]["numfactura"]
print(f"Factura: {serie}-{num}")
try::
# 1. Enviar Ingesta
res_ingesta = requests.post(f"{BASE_URL}/v1/ingesta", json=payload, headers=HEADERS, timeout=10)
if res_ingesta.ok:
print(" > INGESTA OK (Recibida en la BD local). Esperando a la AEAT...
")
else::
print(f" > ERROR DE INGESTA (HTTP {res_ingesta.status_code}):")
print(json.dumps(res_ingesta.json(), indent=2, ensure_ascii=False))
return
except requests.exceptions.RequestException as e:
print(f" > Falla crítica de red al contactar servidor: {e}")
return
# 2. Polling O(1)
print("===============================================")
print("2. ESPERANDO RESPUESTA DE LA AEAT (Polling)")
print("===============================================")
final_pendiente = None
max_intentos = 15
for i in range(1, max_intentos + 1):
time.sleep(1)
print(f" [Intento {i}/{max_intentos}] Comprobando /v1/check_status...")
params = {"emisor": emisor, "serie": serie, "num": num}
try::
res_check = requests.get(f"{BASE_URL}/v1/check_status", params=params, headers=HEADERS)
if res_check.ok:
data = res_check.json()
if data.get("status") and data.get("status") != "pending":
final_pendiente = data
print(" ! Respuesta Definitiva Detectada !
")
break
else::
print(f" Error consultando check_status. HTTP: {res_check.status_code}")
except Exception as e:
print(f" Excepción durante polling: {e}")
# 3. Resultado Final
print("===============================================")
print("3. RESULTADO FINAL (RECUPERADO DE AEAT)")
print("===============================================")
if final_pendiente:
print(f" ESTADO AEAT : {final_pendiente.get('status')} (Raw: {final_pendiente.get('raw_status', '')})")
print(f" MENSAJE AEAT : {final_pendiente.get('mensaje', '')}")
csv = final_pendiente.get('csv')
if csv:
print(f" C.S.V. : {csv}")
else::
print(" TIMEOUT (15s): La AEAT está tardando o hay cola.")
if __name__ == "__main__":
run_demo()Ejemplo en Delphi 10
pascal
program ConsoleDemoSDK;
{$APPTYPE CONSOLE}
{$R *.res}
{
============================================================================
GUÍA DE INTEGRACIÓN RÁPIDA: VERI*FACTU VIA DELPHI SDK (TVFEngine)
============================================================================
Este proyecto de consola demuestra el uso de nuestra librería oficial
\`uVFEngine.pas\` incluida en tu DSK.
En lugar de programar el polling manualmente, la función \`IngestaYConfirmacion\`
capsula todas las peticiones POST, GET y temporizadores en una sola línea.
============================================================================
}
uses
System.SysUtils,
System.Classes,
uConfig in 'uConfig.pas',
uVFEngine in 'uVFEngine.pas',
uHttpVF in 'uHttpVF.pas',
uVFPayload in 'uVFPayload.pas';
var
Cfg: TVfDemoConfig;
Engine: TVFEngine;
PayloadStr, Emisor, NumStr, Serie, FechaStr: string;
Res: TVFIngestaAckResult;
NumRandom: Integer;
begin
try
Writeln('===============================================');
Writeln(' DEMOSTRACION DE VERIFACTU SDK (CONSOLA)');
Writeln('===============================================');
// 1. Inicializar parametros del SDK
Cfg.ApiBaseUrl := 'http://127.0.0.1:8000';
Cfg.TimeoutMs := 15000;
Emisor := 'B12345678'; // NIF falso / Sustituir
Cfg.NifEmisor := Emisor;
Randomize;
NumRandom := Random(900000) + 100000;
NumStr := IntToStr(NumRandom);
Serie := 'DEL';
FechaStr := FormatDateTime('dd/mm/yyyy', Now);
// Payload de factura anonimizado
PayloadStr :=
'{' +
' "cabecera": {' +
' "emisor": "' + Emisor + '",' +
' "numfactura": "' + NumStr + '",' +
' "serie": "' + Serie + '",' +
' "rectificativa": "N",' +
' "tipofacturarectificativa": "",' +
' "fecharectificada": "",' +
' "facturarectificada": "",' +
' "rectificativasustitucion": "",' +
' "facturaf3": "N",' +
' "numseriesustituidaf3": "",' +
' "fechafactsustituidaf3": "",' +
' "descripcionoperacion": "Factura via Delphi SDK",' +
' "eliminacion": "N",' +
' "tipodoc": "01",' +
' "pais": "ES",' +
' "nif": "B98765432",' +
' "nombre": "EMPRESA DEMO S.L.",' +
' "fecha": "' + FechaStr + '",' +
' "base1": "100.00", "piva1": "21.00", "iva1": "21.00", "base": "100.00", "totaliva": "21.00", "total": "121.00"' +
' },' +
' "detalle": {' +
' "base": ["100.00", "", "", ""], "piva": ["21.00", "", "", ""], "iva": ["21.00", "", "", ""]' +
' },' +
' "metadata": { "insertar_en_db": true }' +
'}';
Writeln(' [*] Factura Preparada: ', Serie, '-', NumStr);
// 2. Invocador Mágico
Engine := TVFEngine.Create(Cfg);
try
Writeln(' [*] Lanzando Ingesta y Esperando a la AEAT automaticamente...');
// El metodo IngestaYConfirmacion nos hace el Polling O(1) + el Acuse de Recibo juntos.
Res := Engine.IngestaYConfirmacion(PayloadStr, Cfg.TimeoutMs, 1000, 50, True);
Writeln('');
if Res.Timeout then
Writeln(' [!] Tiempo de espera agotado de 15s. AEAT lenta. Queda en cola.')
else if Res.EncontradoEnPendientes then
begin
Writeln('===============================================');
Writeln('3. RESULTADO RECUPERADO EXITOSAMENTE');
Writeln('===============================================');
Writeln(' - ESTADO AEAT : ', Res.Pendiente.Status);
if Res.Pendiente.CSV <> '' then
Writeln(' - C.S.V. : ', Res.Pendiente.CSV);
Writeln(' - HUELLA : ', Res.Pendiente.Huella);
if Res.AckHecho then
Writeln(' - ACK : OK (Marcado como recogido)');
end
else
begin
Writeln(' [!] Error Culpable: ', Res.ErrorMsg);
if Res.Ingesta.ErrorBodyJson <> '' then
Writeln(Res.Ingesta.ErrorBodyJson);
end;
finally
Engine.Free;
end;
Writeln('');
Writeln('Proceso finalizado. Pulse Enter para salir.');
Readln;
except
on E: Exception do
Writeln('Excepcion Fatal: ', E.Message);
end;
end.Ejemplo en Delphi 7
pascal
program ConsoleDemoSDK_D7;
{$APPTYPE CONSOLE}
{$R *.res}
{
============================================================================
GUÍA DE INTEGRACIÓN RÁPIDA: VERI*FACTU VIA DELPHI 7 SDK (TVFEngine)
============================================================================
Este proyecto de consola demuestra la potencia de nuestra librería oficial
\`uVFEngine.pas\` compilada nativamente para el histórico Delphi 7.
============================================================================
}
uses
SysUtils,
Classes,
uConfig in 'uConfig.pas',
uVFEngine in 'uVFEngine.pas',
uVFHttp in 'uVFHttp.pas',
uVFJson in 'uVFJson.pas';
var
Cfg: TVfDemoConfig;
Engine: TVFEngine;
PayloadStr, Emisor, NumStr, Serie, FechaStr: string;
Res: TVFIngestaAckResult;
NumRandom: Integer;
begin
try
Writeln('===============================================');
Writeln(' DEMOSTRACION DE VERIFACTU SDK (DELPHI 7)');
Writeln('===============================================');
// 1. Inicializar parametros del SDK
Cfg.ApiBaseUrl := 'http://127.0.0.1:8000';
Cfg.TimeoutMs := 15000;
Cfg.Token := '';
Emisor := 'B12345678'; // NIF falso / Sustituir
Cfg.NifEmisor := Emisor;
Randomize;
NumRandom := Random(900000) + 100000;
NumStr := IntToStr(NumRandom);
Serie := 'DEL7';
FechaStr := FormatDateTime('dd/mm/yyyy', Now);
// Payload de factura anonimizado compatible ansi string
PayloadStr :=
'{' +
' "cabecera": {' +
' "emisor": "' + Emisor + '",' +
' "numfactura": "' + NumStr + '",' +
' "serie": "' + Serie + '",' +
' "rectificativa": "N",' +
' "tipofacturarectificativa": "",' +
' "fecharectificada": "",' +
' "facturarectificada": "",' +
' "rectificativasustitucion": "",' +
' "facturaf3": "N",' +
' "numseriesustituidaf3": "",' +
' "fechafactsustituidaf3": "",' +
' "descripcionoperacion": "Factura via Delphi 7 SDK DSK",' +
' "eliminacion": "N",' +
' "tipodoc": "01",' +
' "pais": "ES",' +
' "nif": "B98765432",' +
' "nombre": "EMPRESA DEMO S.L.",' +
' "fecha": "' + FechaStr + '",' +
' "base1": "100.00", "piva1": "21.00", "iva1": "21.00", "base": "100.00", "totaliva": "21.00", "total": "121.00"' +
' },' +
' "detalle": {' +
' "base": ["100.00", "", "", ""], "piva": ["21.00", "", "", ""], "iva": ["21.00", "", "", ""]' +
' },' +
' "metadata": { "insertar_en_db": true }' +
'}';
Writeln(' [*] Factura Preparada: ', Serie, '-', NumStr);
// 2. Invocador Magico
Engine := TVFEngine.Create(Cfg);
try
Writeln(' [*] Lanzando Ingesta y Esperando a la AEAT automaticamente...');
Res := Engine.IngestaYConfirmacion(PayloadStr, Cfg.TimeoutMs, 1000, 50, True);
Writeln('');
if Res.Timeout then
Writeln(' [!] Tiempo de espera agotado de 15s. AEAT lenta. Queda en cola.')
else if Res.EncontradoEnPendientes then
begin
Writeln('===============================================');
Writeln('3. RESULTADO RECUPERADO EXITOSAMENTE');
Writeln('===============================================');
Writeln(' - ESTADO AEAT : ', Res.Pendiente.Status);
if Res.Pendiente.CSV <> '' then
Writeln(' - C.S.V. : ', Res.Pendiente.CSV);
Writeln(' - HUELLA : ', Res.Pendiente.Huella);
if Res.AckHecho then
Writeln(' - ACK : OK (Marcado como recogido)');
end
else
begin
Writeln(' [!] Error Culpable: ', Res.ErrorMsg);
if Res.Ingesta.ErrorBodyJson <> '' then
Writeln(Res.Ingesta.ErrorBodyJson);
end;
finally
Engine.Free;
end;
Writeln('');
Writeln('Proceso finalizado. Pulse Enter para salir.');
Readln;
except
on E: Exception do
Writeln('Excepcion Fatal: ', E.Message);
end;
end.Ejemplo en cURL (Linux)
bash
#!/bin/bash
# ============================================================================
# GUÍA DE INTEGRACIÓN RÁPIDA: VERI*FACTU VIA BASH (Linux CURL Nativo)
# ============================================================================
# Ideal para integraciones con servidores Web (PHP legado), daemons de
# sincronización, entornos Docker crudos o crontabs.
#
# 1. Utiliza Heredoc heredado para generar el JSON sin problemas de escape.
# 2. POST /v1/ingesta
# 3. Bucle FOR con CURL GET /v1/check_status usando un fallback inteligente:
# Si existe 'jq' instalado extrae el dato real, si no, usa 'grep'.
# ============================================================================
echo "==============================================="
echo "1. ENVIANDO INGESTA A MICRO-SERVICIO (CURL LNX)"
echo "==============================================="
# Mínimo de variables
EMISOR="B12345678" # Sustituir por NIF real autorizado en la BD
SERIE="LNX"
NUM=\${RANDOM}
BASEURL="http://127.0.0.1:8000"
# Creación limpia de archivo temporal multilínea sin escapes raros gracias a Heredoc
cat << EOF > payload_tmp.json
{
"cabecera": {
"emisor": "\${EMISOR}",
"numfactura": "\${NUM}",
"serie": "\${SERIE}",
"rectificativa": "N",
"tipofacturarectificativa": "",
"fecharectificada": "",
"facturarectificada": "",
"rectificativasustitucion": "",
"facturaf3": "N",
"numseriesustituidaf3": "",
"fechafactsustituidaf3": "",
"descripcionoperacion": "Prueba BASH LNX",
"eliminacion": "N",
"tipodoc": "01",
"pais": "ES",
"nif": "B98765432",
"nombre": "EMPRESA DEMO S.L.",
"fecha": "18/04/2026",
"base1": "100.00", "piva1": "21.00", "iva1": "21.00", "totaliva": "21.00", "base": "100.00", "total": "121.00"
},
"detalle": {
"base": ["100.00","","",""], "piva": ["21.00","","",""], "iva": ["21.00","","",""]
},
"metadata": {
"insertar_en_db": true
}
}
EOF
echo "Factura: \${SERIE}-\${NUM}"
# Petición a Ingesta (-s significa "slient", oculta barras de progreso de curl)
curl -s -X POST "\${BASEURL}/v1/ingesta" \
-H "Content-Type: application/json" \
-d @payload_tmp.json > response_ingesta.json
# Verificamos rápidamente si devolvió algún error mediante un grep case-insensitive (-i)
if grep -qi "error" response_ingesta.json; then
echo " > ERROR DE INGESTA. Respuesta:"
cat response_ingesta.json
rm -f payload_tmp.json response_ingesta.json
exit 1
else
echo " > INGESTA OK (Lanzada a cola local). Esperando a la AEAT..."
fi
echo ""
echo "==============================================="
echo "2. ESPERANDO RESPUESTA DE LA AEAT (Polling)"
echo "==============================================="
ENCONTRADO=0
# Bucle con sleep nativo
for i in {1..15}; do
sleep 1
echo " [Intento $i/15] Comprobando /check_status..."
curl -s -X GET "\${BASEURL}/v1/check_status?emisor=\${EMISOR}&serie=\${SERIE}&num=\${NUM}" > response_check.json
# Comprobamos si la palabra "pending" ya NO aparece en el JSON de respuesta
if ! grep -q '"status":"pending"' response_check.json; then
# Asegurarnos de que no sea un 404/502 comprobando que la palabra "status" sigue existiendo
if grep -q '"status":' response_check.json; then
ENCONTRADO=1
echo " ! Respuesta Definitiva Detectada !"
break
fi
fi
done
echo ""
echo "==============================================="
echo "3. RESULTADO FINAL EXTRAIDO"
echo "==============================================="
if [ "$ENCONTRADO" -eq 1 ]; then
# La elegancia de Linux: si el servidor tiene jq instalado lo usamos.
if command -v jq > /dev/null; then
echo "Extracción JSON pura (via JQ):"
jq -r '{ "Estado AEAT": .status, "Mensaje": .mensaje, "C.S.V.": .csv, "Huella": .huella }' response_check.json
else
echo "Extracción regex cruda (via GREP -E):"
echo ""
grep -E '"status":|"mensaje":|"csv":|"huella":' response_check.json
fi
else
echo "TIMEOUT: La AEAT está tardando (o el proceso ha fallado), sigue en la cola."
fi
echo ""
echo "[Limpiando temporales...]"
rm -f payload_tmp.json response_ingesta.json response_check.json
echo "Hecho."Ejemplo en cURL (Windows)
bat
@echo off
setlocal enabledelayedexpansion
:: ============================================================================
:: GUIA DE INTEGRACION RAPIDA: VERIFACTU VIA CMD (Windows CURL Nativo)
:: ============================================================================
echo ===============================================
echo 1. ENVIANDO INGESTA A MICRO-SERVICIO (CURL WIN)
echo ===============================================
set EMISOR=B12345678
set SERIE=BAT
set NUM=%RANDOM%
set BASEURL=http://127.0.0.1:8000
:: Escribimos de forma plana al fichero
echo { "cabecera": { "emisor": "%EMISOR%", "numfactura": "%NUM%", "serie": "%SERIE%", "rectificativa": "N", "tipodoc": "01", "pais": "ES", "nif": "B98765432", "nombre": "EMPRESA DEMO S.L.", "fecha": "18/04/2026", "base": "100.00", "totaliva": "21.00", "total": "121.00", "base1": "100.00", "piva1": "21.00", "iva1": "21.00"}, "detalle": { "base": ["100.00","","",""], "piva": ["21.00","","",""], "iva": ["21.00","","",""] }, "metadata": { "insertar_en_db": true } } > payload_tmp.json
echo Factura: %SERIE%-%NUM%
curl.exe -s -X POST "%BASEURL%/v1/ingesta" -H "Content-Type: application/json" -d @payload_tmp.json > response_ingesta.json
findstr /i "error" response_ingesta.json >nul
if !errorlevel! equ 0 (
echo ^> ERROR DE INGESTA. Respuesta bruta:
type response_ingesta.json
goto :limpieza
) else (
echo ^> INGESTA OK ^(Lanzada a cola^). Esperando a la AEAT...
)
echo.
echo ===============================================
echo 2. ESPERANDO RESPUESTA DE LA AEAT (Polling)
echo ===============================================
set "ENCONTRADO=0"
for /L %%i in (1,1,15) do (
ping 127.0.0.1 -n 2 >nul
echo [Intento %%i/15] Comprobando /check_status...
curl.exe -s -X GET "%BASEURL%/v1/check_status?emisor=%EMISOR%&serie=%SERIE%&num=%NUM%" > response_check.json
findstr /i ""pending"" response_check.json >nul
if !errorlevel! neq 0 (
findstr /i "status" response_check.json >nul
if !errorlevel! equ 0 (
set ENCONTRADO=1
echo ! Respuesta Definitiva Detectada !
goto :resultado
)
)
)
:resultado
echo.
echo ===============================================
echo 3. RESULTADO FINAL EXTRAIDO
echo ===============================================
if "%ENCONTRADO%"=="1" (
echo [Respuesta Cruda JSON]
type response_check.json
echo.
) else (
echo TIMEOUT: La AEAT esta tardando.
)
:limpieza
echo.
del payload_tmp.json 2>nul
del response_ingesta.json 2>nul
del response_check.json 2>nulEjemplo en Excel (VBA)
vb
Attribute VB_Name = "VeriFactu_Demo_VBA"
' ============================================================================
' GUÍA DE INTEGRACIÓN RÁPIDA: VERI*FACTU MICRO-SERVICE VÍA MICROSOFT EXCEL (VBA)
' ============================================================================
'
' Estimado Autónomo o Desarrollador VBA:
' Muchas pymes generan sus facturas directamente desde plantillas de Excel.
' Con este script, un simple botón de "Enviar a AEAT" en tu hoja de cálculo
' desencadenará el proceso fiscal de VeriFactu automatizadamente sin salir de Office.
'
' INSTRUCCIONES DE USO:
' 1. Abre tu Excel, pulsa ALT + F11 para abrir el editor VBA.
' 2. Inserta un nuevo "Módulo" y pega este código.
' 3. El motor O(1) funciona igual: Hace POST enviando los datos de las celdas,
' espera unos segundos y recupera el C.S.V para imprimirlo en tu factura.
' ============================================================================
Sub EnviarFactura_VeriFactu()
Dim http As Object
' Usamos la librería nativa de Windows (ServerXMLHTTP) que soporta requests en background
Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
Dim base_url As String
base_url = "http://127.0.0.1:8000"
' ========================================================================
' 1. CONSTRUCCIÓN DEL PAYLOAD DESDE LAS CELDAS
' ========================================================================
' Aquí extraeríamos los datos reales de las celdas, por ejemplo:
' Dim numFactura As String: numFactura = ActiveSheet.Range("B3").Value
'
' Para la demo, usamos variables y lo ensamblamos como String (para no depender
' de librerías JSON externas de terceros en Excel nativo).
Dim emisor As String: emisor = "B12345678"
Dim serie As String: serie = "EXC"
Dim num As String: num = CStr(Int((999999 - 100000 + 1) * Rnd + 100000))
Dim fecha As String: fecha = Format(Now, "dd/mm/yyyy")
Dim payload As String
payload = "{" & _
"""cabecera"": {" & _
"""emisor"": """ & emisor & """, " & _
"""numfactura"": """ & num & """, " & _
"""serie"": """ & serie & """, " & _
"""rectificativa"": ""N"", " & _
"""tipofacturarectificativa"": """", " & _
"""fecharectificada"": """", " & _
"""facturarectificada"": """", " & _
"""rectificativasustitucion"": """", " & _
"""facturaf3"": ""N"", " & _
"""numseriesustituidaf3"": """", " & _
"""fechafactsustituidaf3"": """", " & _
"""descripcionoperacion"": ""Factura via VBA Excel SDK"", " & _
"""eliminacion"": ""N"", " & _
"""tipodoc"": ""01"", " & _
"""pais"": ""ES"", " & _
"""nif"": ""B98765432"", " & _
"""nombre"": ""EMPRESA DEMO S.L."", " & _
"""fecha"": """ & fecha & """, " & _
"""base1"": ""100.00"", ""piva1"": ""21.00"", ""iva1"": ""21.00"", " & _
"""base2"": """", ""piva2"": """", ""iva2"": """", " & _
"""base3"": """", ""piva3"": """", ""iva3"": """", " & _
"""base4"": """", ""piva4"": """", ""iva4"": """", " & _
"""base"": ""100.00"", " & _
"""totaliva"": ""21.00"", " & _
"""total"": ""121.00""" & _
"}, " & _
"""detalle"": {" & _
"""base"": [""100.00"", """", """", """"], " & _
"""piva"": [""21.00"", """", """", """"], " & _
"""iva"": [""21.00"", """", """", """"], " & _
"""precargo"": ["""", """", """", """"], " & _
"""recargo"": ["""", """", """", """"]" & _
"}, " & _
"""metadata"": { ""insertar_en_db"": true }" & _
"}"
' ========================================================================
' 2. ENVIAR INGESTA AL MICROSERVER LOCAL
' ========================================================================
http.Open "POST", base_url & "/v1/ingesta", False
http.setRequestHeader "Content-Type", "application/json"
On Error GoTo ManejoErrorInternet
http.Send payload
On Error GoTo 0
If http.Status < 200 Or http.Status >= 300 Then
MsgBox "✖ Error de MicroServer (HTTP " & http.Status & "): " & vbCrLf & http.responseText, vbCritical, "VeriFactu Error"
Exit Sub
End If
' ========================================================================
' 3. POLLING (SONDEO) O(1) - ESPERANDO A LA AGENCIA TRIBUTARIA
' ========================================================================
Application.StatusBar = "Enviando factura a AEAT... Por favor, espere."
Dim check_url As String
check_url = base_url & "/v1/check_status?emisor=" & emisor & "&serie=" & serie & "&num=" & num
Dim i As Integer
Dim statusTerminado As Boolean
statusTerminado = False
Dim respuestaFinal As String
For i = 1 To 15
' Esperar 1 segundo para no saturar
Application.Wait (Now + TimeValue("0:00:01"))
http.Open "GET", check_url, False
http.Send
If http.Status = 200 Then
respuestaFinal = http.responseText
' La forma rudimentaria y nativa de parsear JSON en VBA sin dependencias es InStr
If InStr(1, respuestaFinal, """status"":""pending""") = 0 Then
statusTerminado = True
Exit For
End If
End If
Next i
Application.StatusBar = False
' ========================================================================
' 4. PROCESAR RESPUESTA AEAT E INYECTARLA EN LA FACTURA
' ========================================================================
If statusTerminado Then
' Ha llegado la respuesta definitiva
' Pequeño truco sucio de manipulación de cadenas para extraer el CSV
Dim csvExtraido As String
csvExtraido = ExtraerValorJSON(respuestaFinal, "csv")
If csvExtraido <> "" Then
' Magia: ¡Inyectamos el código fiscal en tu plantilla de Excel automáticamente!
' ActiveSheet.Range("H15").Value = "C.S.V. VeriFactu:" & csvExtraido
MsgBox "✔ Factura " & serie & "-" & num & " enviada con éxito a la AEAT." & vbCrLf & vbCrLf & _
"C.S.V. Recibido: " & csvExtraido, vbInformation, "VeriFactu Aceptado"
Else
MsgBox "Rechazado o sin CSV. Ver respuesta completa: " & vbCrLf & respuestaFinal, vbExclamation, "Atención"
End If
Else
MsgBox "⏳ Tiempo de espera agotado de 15 segundos. La Agencia Tributaria va lenta." & vbCrLf & _
"Tranquilo, tu factura está encapsulada a salvo en el Motor", vbExclamation, "Cola AEAT"
End If
Exit Sub
ManejoErrorInternet:
MsgBox "No se pudo contactar con el MicroServer. Asegúrate de que VeriFactu está encendido.", vbCritical
End Sub
' Función auxiliar para extraer datos básicos limpiamente
Function ExtraerValorJSON(jsonString As String, clave As String) As String
Dim posInicio As Long, posFin As Long
Dim buscarClave As String
buscarClave = """" & clave & """:"""
posInicio = InStr(1, jsonString, buscarClave)
If posInicio > 0 Then
posInicio = posInicio + Len(buscarClave)
posFin = InStr(posInicio, jsonString, """")
If posFin > posInicio Then
ExtraerValorJSON = Mid(jsonString, posInicio, posFin - posInicio)
Exit Function
End If
End If
ExtraerValorJSON = ""
End Function