Aclaración de calendario: VeriFactu todavía no es obligatorio. La entrada en vigor está aplazada al 1 de enero de 2027 para sociedades y al 1 de julio de 2027 para autónomos y pymes. Por lo tanto hoy, facturar con Stripe (o con cualquier otro sistema) continúa siendo legal siempre que cumpla los requisitos generales de facturación en España (datos del emisor y receptor, NIF, desglose de IVA, numeración correlativa...). Lo que exige VeriFactu son unos requisitos adicionales que entrarán en vigor en esas fechas.

Dicho esto, Stripe por sí solo no está preparado para cumplir con los requisitos de la Ley Antifraude, gestiona el pago, pero no genera registros de facturación verificables. Así que si estás cobrando a través de Stripe y quieres adelantarte a VeriFactu (o ya te toca por tu fecha de obligación), tendrás que conectar ambos sistemas: capturar los eventos de pago desde Stripe y generar los registros de facturación conforme a normativa.

Este artículo explica cómo estructurar esa integración desde un punto de vista técnico: qué eventos escuchar, qué datos necesitas mapear y cómo enviar la información a un sistema VeriFactu compatible, usando la API real de VerifactuAPI como referencia.

El flujo básico: de pago en Stripe a registro VeriFactu

El proceso tiene tres pasos:

  1. Stripe procesa un pago (cargo único, suscripción, invoice).
  2. Tu backend recibe un webhook de Stripe confirmando el evento.
  3. Tu sistema genera el registro VeriFactu con los datos del pago y lo envía a la AEAT.

La clave está en el paso 2: decidir qué eventos de Stripe te interesan y cómo extraer los datos necesarios para construir el registro de facturación.

Qué eventos de Stripe necesitas escuchar

Stripe dispara muchísimos tipos de eventos. Para facturación, estos son los que de verdad te interesan:

Si vendes suscripciones, invoice.paid es tu punto de entrada. Si haces cobros únicos con Checkout, checkout.session.completed suele ser más cómodo que trabajar con charges o payment intents sueltos.

Configurar el webhook en Stripe

Accede al dashboard de Stripe, ve a Developers > Webhooks y añade un endpoint. El endpoint es una URL de tu backend que recibirá notificaciones JSON cada vez que ocurra un evento.

Ejemplo de endpoint:

https://tuapp.com/webhooks/stripe

Selecciona los eventos que quieres escuchar (por ejemplo, invoice.paid). Stripe te dará un webhook secret que usarás para verificar que las peticiones vienen realmente de Stripe.

Verificar la firma del webhook

Stripe firma cada petición con un header Stripe-Signature. Debes validarla antes de procesar el evento. Esto evita que alguien envíe eventos falsos a tu endpoint.

Ejemplo en Node.js con Express:

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    console.error('Webhook signature verification failed:', err.message);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // Evento verificado, procesar
  handleEvent(event);
  res.json({ received: true });
});

La función constructEvent lanza una excepción si la firma no coincide. Si pasa, el evento es auténtico.

Extraer los datos del evento

Una vez verificado el evento, necesitas extraer la información que VeriFactu requiere:

Ejemplo simplificado de un evento invoice.paid (los campos exactos pueden variar según la versión de la API de Stripe que uses, así que conviene revisar siempre la referencia oficial antes de dar algo por hecho):

{
  "id": "evt_...",
  "type": "invoice.paid",
  "data": {
    "object": {
      "id": "in_...",
      "customer": "cus_...",
      "total": 12100,
      "currency": "eur",
      "lines": {
        "data": [
          {
            "description": "Suscripción Pro - Mensual",
            "amount": 10000,
            "quantity": 1
          }
        ]
      },
      "created": 1678886400
    }
  }
}

Los campos customer, total y lines son tu punto de partida. El desglose de impuestos ya no vive en un campo suelto tipo tax: en las versiones actuales de la API de Stripe se consulta a través del array total_taxes del invoice (o mediante Stripe Tax si lo tienes activado). Y, en cualquier caso, Stripe no almacena por defecto el NIF del cliente ni su dirección fiscal completa, eso tendrás que guardarlo tú.

Almacenar datos fiscales del cliente

Stripe permite usar metadata en objetos Customer. Puedes guardar ahí el NIF y otros datos fiscales que necesites para VeriFactu.

Ejemplo al crear o actualizar un cliente:

const customer = await stripe.customers.create({
  email: 'cliente@example.com',
  name: 'Empresa Ejemplo SL',
  metadata: {
    nif: 'B12345678',
    direccion: 'Calle Falsa 123, 28001 Madrid',
    cp: '28001',
    pais: 'ES'
  }
});

Cuando recibas el webhook, recupera el cliente:

const customerId = event.data.object.customer;
const customer = await stripe.customers.retrieve(customerId);
const nif = customer.metadata.nif;
const direccion = customer.metadata.direccion;

Ahora tienes los datos necesarios para construir el registro VeriFactu.

Qué debe incluir realmente un registro VeriFactu

Antes de construir el payload conviene tener claro qué exige de verdad la normativa (Real Decreto 1007/2023 y su desarrollo). Un registro de facturación VeriFactu debe incluir:

El hash y el QR son las piezas críticas: no se generan "a mano", los calcula nuestra API VeriFactu manteniendo la cadena de registros.

Una aclaración que suele generar confusión: el registro de facturación de VeriFactu no incluye un campo de "medio de pago" como sí ocurre en otros sistemas (por ejemplo, el SII). Si tu negocio necesita registrar cómo pagó el cliente (tarjeta, SEPA, etc.), es información de gestión interna que puedes seguir guardando en Stripe o en tu propia base de datos, pero no forma parte de lo que se envía a la AEAT en el registro VeriFactu.

Construir el payload real hacia VerifactuAPI

Si usas VerifactuAPI, el endpoint para dar de alta un registro de facturación es:

POST https://app.verifactuapi.es/api/alta-registro-facturacion
Authorization: Bearer {tu_token}
Content-Type: application/json

Los campos siguen la nomenclatura del esquema oficial de la AEAT, no nombres inventados. Un payload típico, construido a partir del evento de Stripe:

{
  "IDEmisorFactura": "B87654321",
  "NumSerieFactura": "2026-000145",
  "FechaExpedicionFactura": "15-03-2026",
  "TipoFactura": "F1",
  "DescripcionOperacion": "Suscripción Pro - Mensual",
  "Destinatarios": [
    {
      "NombreRazon": "Empresa Ejemplo SL",
      "NIF": "B12345678"
    }
  ],
  "Desglose": [
    {
      "Impuesto": "01",
      "ClaveRegimen": "01",
      "TipoImpositivo": 21,
      "BaseImponibleOImporteNoSujeto": 100.00,
      "CuotaRepercutida": 21.00
    }
  ],
  "CuotaTotal": 21.00,
  "ImporteTotal": 121.00,
  "RefExterna": "in_1MtHbELkdIwHu7ixl4OzzPMv"
}

Es buena práctica mandar en RefExterna el id del invoice o charge de Stripe: te permite luego cruzar cada registro VeriFactu con su pago de origen, algo imprescindible cuando llegue un reembolso.

Ejemplo de llamada con axios:

const axios = require('axios');

async function crearFacturaVeriFactu(datosFactura) {
  const response = await axios.post(
    'https://app.verifactuapi.es/api/alta-registro-facturacion',
    datosFactura,
    {
      headers: {
        'Authorization': `Bearer ${process.env.VERIFACTU_API_KEY}`,
        'Content-Type': 'application/json'
      }
    }
  );
  return response.data;
}

VerifactuAPI expone también un endpoint gemelo, /api/alta-registro-facturacion/validar, que valida la estructura del payload sin darlo de alta de verdad — útil para probar tu mapeo de datos sin generar registros reales.

Gestionar errores y reintentos

Los webhooks pueden fallar por timeouts, errores de red o caídas de tu servidor. Stripe reintenta automáticamente el envío si tu endpoint devuelve un código de error (distinto de 2xx).

Pero si tu sistema VeriFactu está caído o rechaza el registro por un dato incorrecto, debes gestionar el reintento tú mismo. Recomendaciones:

  1. Guarda el evento en base de datos antes de procesarlo.
  2. Marca el estado del procesamiento (pendiente, enviado, error).
  3. Implementa un worker que revise eventos pendientes y reintente el envío.

Ejemplo de tabla en base de datos:

id stripe_event_id customer_id status retry_count created_at
1 evt_abc123 cus_xyz789 sent 0 2026-03-15
2 evt_def456 cus_uvw012 error 3 2026-03-15

Así no pierdes datos aunque haya errores temporales.

Cómo elegir el tipo de factura (TipoFactura)

VeriFactu distingue varios tipos de factura, y Stripe no te dice directamente cuál te toca. Los más habituales:

Para la mayoría de negocios que facturan a través de Stripe con clientes identificados (SaaS B2B, suscripciones con NIF), F1 es el caso por defecto.

Casos especiales: suscripciones y reembolsos

Suscripciones recurrentes.

Cada renovación de suscripción genera un nuevo invoice.paid. Cada invoice debe generar su propio registro VeriFactu. El encadenamiento (el hash) lo genera la API VeriFactu de forma cronológica según el orden en que le llegan los registros, así que basta con que tú generes el registro cada vez que Stripe confirme el pago.

Reembolsos.

Cuando emites un reembolso en Stripe, el evento a escuchar es charge.refunded (o, en integraciones más recientes, refund.created) el evento invoice.payment_refunded no existe en Stripe, así que no lo busques en tu integración. Ten en cuenta además que, si el reembolso es sobre un invoice, Stripe suele generarlo internamente a través de una nota de crédito.

Sea cual sea el evento, debes generar una factura rectificativa en VeriFactu (R1 normalmente), indicando el motivo y referenciando la factura original. Stripe no gestiona esa referencia por ti: por eso conviene guardar desde el principio la correspondencia entre el id del charge o invoice de Stripe y el NumSerieFactura del registro VeriFactu el campo RefExterna del payload es justo para esto.

Testing en sandbox

Antes de operar en producción, prueba el flujo completo en entorno de test:

  1. Usa las claves de test de Stripe (empiezan por sk_test_ y pk_test_).
  2. Configura un webhook de test apuntando a un endpoint local (usa ngrok o similar para exponerlo).
  3. Dispara eventos de prueba desde el dashboard de Stripe (Developers > Webhooks > Send test webhook).
  4. Verifica que tu sistema recibe el evento, extrae los datos correctamente y envía el registro a VerifactuAPI.

VerifactuAPI de Invocash ofrece un entorno de pruebas contra el sandbox de la AEAT completamente gratuito e ilimitado, sin necesidad de certificado digital ni tarjeta de crédito: puedes registrar tu NIF de pruebas y validar toda la integración antes de pasar a producción.

Monitorización y logs

Implementa logs detallados en cada paso:

Esto facilita el debugging cuando algo falla. Herramientas como Sentry o Logtail te permiten centralizar logs y alertar si hay errores recurrentes.

Si ya usas Stripe y necesitas implementar VeriFactu, puedes probar VerifactuAPI sin coste y totalmente ilimitado.