Skip to content

🚀 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>nul

Ejemplo 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