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.
En este artículo
- El flujo básico: de pago en Stripe a registro VeriFactu
- Qué eventos de Stripe necesitas escuchar
- Configurar el webhook en Stripe
- Verificar la firma del webhook
- Extraer los datos del evento
- Almacenar datos fiscales del cliente
- Qué debe incluir realmente un registro VeriFactu
- Construir el payload real hacia VerifactuAPI
- Gestionar errores y reintentos
- Cómo elegir el tipo de factura (TipoFactura)
- Casos especiales: suscripciones y reembolsos
- Testing en sandbox
- Monitorización y logs
El flujo básico: de pago en Stripe a registro VeriFactu
El proceso tiene tres pasos:
- Stripe procesa un pago (cargo único, suscripción, invoice).
- Tu backend recibe un webhook de Stripe confirmando el evento.
- 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:
invoice.paid: se dispara cuando un invoice de Stripe (facturación por suscripción o facturación manual) se paga con éxito. Es tu punto de entrada principal en modelos de suscripción.checkout.session.completed: se dispara cuando una sesión de Stripe Checkout se completa. Es la opción más práctica en cobros únicos o compras puntuales, porque te da en un solo objeto el cliente, el importe y las líneas compradas.charge.succeeded: se activa cuando un cargo tiene éxito. Útil si trabajas directamente con Charges sin invoice ni Checkout de por medio.payment_intent.succeeded: confirma que un PaymentIntent se completó. Tiene sentido si construyes tu propio flujo de pago sin invoices ni Checkout, pero ten en cuenta que da menos contexto de negocio (líneas, conceptos) que los otros tres.
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:
- Identificación del cliente: nombre/razón social y NIF.
- Detalles de la operación: importe total, base imponible, cuota de IVA, concepto.
- Fecha de la operación.
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:
- Identificación completa del emisor y del destinatario (nombre/razón social, NIF).
- Descripción de la operación, base imponible, tipo de IVA y cuota.
- Un identificador único y correlativo.
- Un hash (huella) que encadena cada registro con el anterior, garantizando que no se puede alterar ni eliminar sin dejar rastro.
- Un código QR de verificación, que debe figurar en la representación de la factura y que permite a la AEAT o al receptor comprobar su validez.
- La indicación de que se trata de una factura generada por un sistema VeriFactu.
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:
- Guarda el evento en base de datos antes de procesarlo.
- Marca el estado del procesamiento (pendiente, enviado, error).
- 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:
- F1 Factura completa: para ventas B2B y a clientes que se identifican fiscalmente (nombre, NIF).
- F2 Factura simplificada: para ventas a consumidores finales que no piden identificación fiscal. La normativa general de facturación en España permite usarla en operaciones de hasta 400 € (o hasta 3.000 € en ciertos sectores minoristas), siempre que el cliente no necesite deducirse el IVA.
- F3 Factura sustitutiva: cuando emites una factura completa en sustitución de una simplificada ya emitida, normalmente porque el cliente pide después sus datos fiscales.
- R1 a R5 Facturas rectificativas, según el motivo: error fundado o devolución (R1), concurso de acreedores (R2), crédito incobrable (R3), otras causas (R4), o rectificación de una factura simplificada (R5).
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:
- Usa las claves de test de Stripe (empiezan por sk_test_ y pk_test_).
- Configura un webhook de test apuntando a un endpoint local (usa ngrok o similar para exponerlo).
- Dispara eventos de prueba desde el dashboard de Stripe (Developers > Webhooks > Send test webhook).
- 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:
- Webhook recibido de Stripe.
- Datos extraídos del evento.
- Payload enviado a VerifactuAPI.
- Respuesta recibida (aceptado, error de validación, etc.).
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.


