Auto-facturación con Stripe
Emite facturas automáticamente desde los cobros de Stripe Connect — captura de NIF en el Checkout, umbral de factura simplificada, exigir NIF y qué cobros se derivan a revisión manual.
Cuando conectas Stripe mediante Stripe Connect, Factuarea puede emitir una
factura automáticamente por cada cobro exitoso: el cobro se convierte en una
factura con status: sent, se da de alta en VeriFactu y se le registra un
Payment. El flujo es idempotente de extremo a extremo, así que un webhook
reentregado nunca produce una factura duplicada.
Lo alimentan dos flujos:
- Flujo A — un cobro que paga una factura de Factuarea ya existente (una Checkout Session que Factuarea creó desde un enlace de pago). La factura ya existe; el cobro la marca como pagada.
- Flujo B — un cobro espontáneo sin factura previa (un Payment Link que el comercio creó en su propio Dashboard de Stripe, o cualquier otro cobro de Connect). Factuarea crea la factura a partir del cobro.
La configuración se lee y escribe a través de los endpoints v1 a nivel de empresa:
- Obtener la configuración de auto-facturación
(
stripe_autoinvoicing:read). - Actualizar la configuración de auto-facturación
(
stripe_autoinvoicing:write). - Listar cobros auto-facturados
(
stripe_autoinvoicing:read). - Listar rectificativas auto-facturadas
(
stripe_autoinvoicing:read).
Si gestionas varias tiendas con cuentas de Stripe distintas, cada cuenta tiene su propia serie y su propia configuración — consulta Varias tiendas.
La auto-facturación está limitada por el módulo de integración con Stripe de
tu plan y viene desactivada por defecto — actívala explícitamente con
enabled: true.
Qué expone la API de Stripe. Configuración de auto-facturación a nivel de empresa (una comodidad heredada del modelo de tienda única — la fuente de verdad real es la configuración por cuenta conectada, ver Varias tiendas), cuentas conectadas, cobros auto-facturados, rectificativas y payouts para la conciliación bancaria.
Varias tiendas
Un negocio puede operar varias "tiendas" o líneas (una tienda física + un curso
online) con cuentas de Stripe distintas (Stripe Connect) y querer numeración
de facturas independiente para cada una (TIENDA-2026-…, CURSOS-2026-…).
Factuarea modela cada cuenta de Stripe que conectas como una cuenta conectada:
cada Account Link que completas añade una cuenta — nunca sobrescribe la
anterior — y los cobros de cada cuenta se enrutan a la serie y la configuración
de esa cuenta.
Cada cuenta conectada lleva:
- una serie (
series_id) usada para las facturas auto-creadas a partir de sus cobros —nullsignifica que se usa la serie de facturas por defecto de la empresa; - su propia configuración de auto-facturación (
autoinvoicing_enabled,simplified_threshold_cents,require_nif,refunds_enabled,subscription_autoinvoicing_enabled) — toda regla fiscal de esta página aplica por cuenta.
Cuando llega un webhook, Factuarea resuelve la cuenta de Stripe (acct_xxx) a su
cuenta conectada y emite la factura en la serie de esa cuenta, con la política
fiscal de esa cuenta — de modo que dos tiendas producen facturas en dos series de
numeración separadas y correctas.
Las cuentas nuevas nacen seguras
Una cuenta recién conectada no hereda la configuración de otra cuenta: nace con los mismos valores por defecto seguros que un alta nueva — auto-facturación desactivada, umbral 400 €, "exigir NIF" desactivado, devoluciones activas, suscripciones desactivadas — y sin serie (cae en la serie por defecto de la empresa hasta que le asignes una). Configúrala explícitamente antes de que emita nada.
Endpoints v1 por cuenta
Gestiona las cuentas bajo el recurso connected-accounts (mismos scopes
stripe_autoinvoicing:read|write; la identidad es el id de la cuenta, un UUID v7):
- Listar cuentas conectadas
(
stripe_autoinvoicing:read). - Obtener una cuenta conectada
(
stripe_autoinvoicing:read). - Actualizar una cuenta conectada
— nombre,
series_id(envíanullpara limpiarla) y la configuración por cuenta (stripe_autoinvoicing:write). - Desconectar una cuenta conectada
(
stripe_autoinvoicing:write).
# Asignar la serie CURSOS y activar la auto-facturación en una tienda
curl -X PUT https://api.factuarea.com/v1/connected-accounts/0192f3a4-… \
-H "Authorization: Bearer fact_test_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 0192f3a4-…" \
-d '{
"series_id": "0192aaaa-…",
"autoinvoicing_enabled": true
}'Desconectar una cuenta conserva sus facturas ya emitidas y su histórico; los
webhooks posteriores se registran sin procesar. Referenciar el id de una
cuenta que pertenece a otra empresa devuelve 404 (connected_account_not_found)
— el aislamiento multi-tenant nunca filtra la existencia.
El endpoint a nivel de empresa mientras tienes una tienda
Los endpoints de configuración
a nivel de empresa de arriba siguen válidos mientras tienes exactamente una
cuenta conectada: el GET devuelve la configuración efectiva de esa única cuenta
y el PUT hace de proxy hacia ella (escribe en la configuración de la cuenta,
nunca una copia duplicada a nivel de empresa).
En cuanto conectas una segunda cuenta, la configuración a nivel de empresa ya
no puede responder a "¿qué cuenta?". Tanto el GET como el PUT devuelven
entonces 422 (per_account_config_required, mensaje en español) apuntándote a
los endpoints por cuenta — Factuarea nunca escribe en dos sitios, así que no hay
divergencia entre una configuración a nivel de empresa y las cuentas.
Una sola fuente de verdad. La configuración por cuenta es el único lugar
donde viven los ajustes. El endpoint a nivel de empresa es una comodidad que hace
de proxy a la cuenta única; nunca guarda una copia separada, de modo que leer y
escribir siempre coinciden. Con dos o más cuentas, usa connected-accounts/{account}
directamente.
Factura ordinaria o simplificada
Una factura española necesita el NIF del destinatario para emitirse como factura ordinaria (F1). Un cobro B2C sin NIF es justo el caso que la normativa resuelve con una factura simplificada (F2). Factuarea decide cuál emitir a partir de los datos que trae el cobro más tu política fiscal:
| Situación | Factura emitida |
|---|---|
| Se captura un NIF válido en el Checkout, o el cliente resuelto ya tiene un NIF en su ficha | Ordinaria (F1) |
| Sin NIF, total del cobro igual o por debajo del umbral y "exigir NIF" desactivado | Simplificada (F2) |
| Sin NIF y (total por encima del umbral o "exigir NIF" activado) | Revisión manual — no se auto-emite ninguna factura |
Un NIF capturado se valida contra el formato español (NIF/NIE/CIF). Un NIF con formato no válido cuenta como sin NIF, así que nunca se emite una F1 con datos basura.
Los cobros derivados a revisión manual no se pierden: quedan registrados en el log de la integración para que emitas la factura a mano. La auto-facturación continúa con el resto — un cobro en revisión nunca hace fallar el webhook. Todos aparecen listados en la bandeja de eventos de integración, que es donde ves qué le pasó a cada uno.
El tope legal absoluto de una factura simplificada es de 3.000 €, garantizado por el propio dominio de facturación: un cobro sin NIF por encima de 3.000 € siempre va a revisión manual, sea cual sea el umbral configurado.
Capturar el NIF en el Checkout
Para que un cliente que sí tiene NIF pueda aportarlo, las Checkout Sessions que
crea Factuarea (Flujo A) activan la recogida del identificador fiscal de
Stripe. El cliente puede introducir su es_cif/eu_vat al pagar, y ese NIF
encamina hacia la F1.
El NIF también se lee de cualquier checkout.session.completed entrante:
- de
customer_details.tax_ids(el campo estándar de Stripe), y - de los campos personalizados de los Payment Links que el comercio construye
en su propio Dashboard de Stripe — Factuarea busca un campo cuya clave parezca
un identificador fiscal (
nif,dni,cif,vat,tax).
Un cobro que llega solo como payment_intent.succeeded (sin Checkout) no trae
ningún NIF capturado, pero aún puede ser una F1 si el cliente se resuelve por
email y ya tiene un NIF en su ficha.
El umbral
simplified_threshold_cents es el importe en céntimos igual o por debajo del
cual un cobro sin NIF se auto-emite como factura simplificada. Su valor por
defecto es 40000 (400 €) y acepta cualquier valor en el rango [0, 300000]
(0–3.000 €).
Un valor por defecto conservador de 400 € es deliberado: el tope de 3.000 € solo es legal en sectores tasados concretos, y Factuarea no conoce tu sector — sube el umbral únicamente si tu actividad lo permite.
curl -X PUT https://api.factuarea.com/v1/stripe-autoinvoicing/config \
-H "Authorization: Bearer fact_test_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 0192f3a4-…" \
-d '{
"enabled": true,
"simplified_threshold_cents": 100000,
"require_nif": false
}'Ambos campos fiscales son opcionales: si omites simplified_threshold_cents o
require_nif, se conservan sus valores actuales. Un umbral fuera del rango
devuelve 422 (validation_error).
Exigir un NIF
require_nif (por defecto false) tiene dos efectos coherentes:
- En las Checkout Sessions que crea Factuarea, el identificador fiscal se marca
como obligatorio (
if_supported), de modo que se le solicita al cliente. - En la decisión de arriba, veta la F2: un cobro sin NIF va a revisión manual en lugar de convertirse en factura simplificada.
Actívalo cuando tu empresa no quiera nunca facturas simplificadas automáticas: cada cobro pasa entonces a tener NIF (F1) o a esperarte en revisión manual.
Las facturas auto-emitidas son fiscalmente reales e irreversibles (se crea
el registro de Alta de VeriFactu). Valida tu política fiscal primero con una
clave fact_test_: en el sandbox el registro de VeriFactu
se crea localmente y nunca se transmite a la AEAT, así que puedes ejercitar la
decisión F1/F2/revisión sin riesgo antes de pasar a producción.
El evento saliente
Cada factura auto-creada — F1 o F2 — emite el evento
invoice.auto_created y un
evento payment.received. En una factura simplificada el payload lleva
client_id: null (sin destinatario), de modo que un receptor de webhooks puede
distinguir la F1 de la F2.
Desglose real de IVA con Stripe Tax
Si usas Stripe Tax, cada cobro ya trae el desglose fiscal real por línea — el tipo, la base imponible y, cuando aplica, la causa por la que una línea está exenta o sujeta a inversión del sujeto pasivo. Factuarea espeja ese desglose en la factura en lugar de aplanarlo todo a un único tipo por defecto.
El webhook del Checkout no incluye las líneas, así que Factuarea hace una segunda llamada de solo lectura a la API en tu nombre para recuperarlas con sus impuestos, y mapea cada línea:
| Dato de Stripe Tax | Línea de la factura |
|---|---|
rate.percentage | el tipo de IVA de la línea (vat_rate), tal cual — nunca se recalcula |
taxable_amount | la base imponible de la línea (precio unitario = base ÷ cantidad) |
taxability_reason zero_rated / product_exempt / customer_exempt | línea exenta al 0 % |
taxability_reason reverse_charge | línea con inversión del sujeto pasivo (ISP) al 0 % |
Así, un cobro con IVA mixto (p. ej. 21 % de consultoría + 10 % de un libro) se convierte en una factura con dos líneas reales, cada una a su tipo, y el desglose multi-IVA llega hasta el registro de VeriFactu.
El IVA nunca se recalcula — Stripe ya lo calculó, y recalcularlo introduciría desviaciones de céntimos. Factuarea toma el tipo y la base imponible directamente de Stripe Tax.
Cuando no hay Stripe Tax (no lo has activado en tu cuenta de Stripe) no cambia nada: cada línea recurre al IVA por defecto de tu empresa, igual que antes. Una empresa sin IVA por defecto configurado recurre al 0 % — nunca a un 21 % fantasma.
Líneas reales
Cuando un cobro trae varias líneas (varios productos o conceptos), aparecen como líneas reales e independientes en la factura — cada una con su descripción, cantidad y precio — en lugar de colapsarse en una sola. Son líneas libres (no enlazadas a tu catálogo de productos).
Esto es ortogonal al tipo de factura: la decisión F1/F2 de arriba elige el tipo, el mapeo de líneas elige las líneas — tanto una factura ordinaria como una simplificada obtienen las mismas líneas reales.
Un cobro que llega solo como payment_intent.succeeded (sin Checkout Session,
por lo que no hay líneas recuperables), o un cobro cuya recuperación de líneas
falla tras los reintentos, sigue produciendo una factura: recurre a una sola
línea con el IVA por defecto. La factura nunca se pierde por un detalle no
esencial.
Antes de emitir, Factuarea valida el total: el total derivado de las líneas
espejadas (suma de subtotal + IVA por línea, en EUR) debe coincidir con el
importe realmente cobrado, dentro de una pequeña tolerancia de redondeo (±1
céntimo por línea, mínimo ±0,05 €). Si no coincide, el cobro se deriva a
revisión manual (total_mismatch) en lugar de emitir una factura cuyo total
diverja del cobro real — el webhook responde correctamente igualmente.
Cobros en otra moneda
Un cobro en una moneda distinta del EUR ya no se descarta. Factuarea lo convierte a EUR usando el tipo de cambio de referencia del Banco Central Europeo (BCE) de la fecha de pago y emite la factura en euros — base, IVA y total, y el registro de VeriFactu/AEAT, todo en EUR (Art. 12.1 RD 1619/2012: la cuota de IVA debe consignarse en euros).
- Un cobro en EUR pasa intacto.
- Un cobro ≠EUR con tipo disponible se convierte; la traza de la conversión — importe y moneda originales, tipo BCE y fecha del tipo — se escribe en las notas de la factura y en el registro de integración para auditoría fiscal.
- Un cobro en una moneda sin tipo BCE disponible no se auto-factura: se
deriva a revisión manual y el webhook responde correctamente igualmente.
Factuarea nunca inventa un tipo. La entrada aparece en la
bandeja como
unsupported_currency, aparcada: en cuanto se publique el tipo oficial de esa fecha, reprocesar el evento emite la factura.
Los tipos del BCE se cachean a diario (sin tabla de base de datos adicional), así que varios cobros ≠EUR del mismo día comparten una sola consulta del tipo.
Emitir la factura en la moneda original (opción B) queda intencionadamente fuera de alcance — Factuarea siempre convierte a EUR (opción A).
Devoluciones y facturas rectificativas
Una factura emitida es fiscalmente irreversible — nunca se borra ni se anula
una vez pagada. La única forma legal de deshacerla es una factura rectificativa.
Por eso, cuando Stripe devuelve un cobro que Factuarea facturó, Factuarea cierra
el ciclo fiscal por ti: el webhook charge.refunded genera automáticamente una
factura rectificativa enlazada (con su propio registro R de VeriFactu), sin
rectificar a mano.
Lo controla refunds_enabled (por defecto true). Solo actúa mientras la
auto-facturación está activada — el gating es enabled && refunds_enabled. Una
empresa que nunca activó la auto-facturación no ve ningún cambio.
| Devolución | Rectificativa emitida |
|---|---|
Total (refunded: true) | Una rectificativa total que refleja toda la factura original como líneas negativas |
Parcial (amount_refunded < amount) | Una rectificativa partial con una única línea negativa por el importe devuelto, al tipo impositivo de la línea original |
La rectificativa se emite siempre por diferencias (AEAT TipoRectificativa: I),
porque una devolución es un abono con importes negativos. Lleva el motivo de
corrección devolucion; si la original es una factura simplificada (F2), la
rectificativa se emite como R5 automáticamente.
La factura original se localiza por dos caminos — por el metadato
factuarea_invoice del cobro (Flujo A) o por el UUID determinista derivado del
payment_intent (Flujo B) — así que las devoluciones de cobros emitidos antes
de que existiera esta función también se rectifican.
La idempotencia es por devolución individual. Stripe reenvía
charge.refunded con el amount_refunded acumulado, pero Factuarea se basa
en el id de cada devolución individual (re_xxx): cada una produce como
máximo una rectificativa. Un evento reentregado, o una segunda devolución
parcial, nunca abonan por duplicado. Una devolución cuya factura original no se
pueda localizar, que no esté en un estado rectificable (sent/paid) o que ya
esté rectificada queda registrada en la
bandeja para revisión manual — nunca hace
fallar el webhook.
Cada rectificativa automática emite el evento
invoice.corrective_auto_created
(que lleva la rectificativa, la factura original, el refund_id de origen y el
provider), y puedes listarlas con
Listar rectificativas auto-facturadas.
Para recibir devoluciones debes habilitar el evento charge.refunded en el
endpoint de webhook de Connect en el Dashboard de Stripe. Como con la
auto-facturación, valida el flujo primero con una clave fact_test_: en el
sandbox el registro R de VeriFactu se crea localmente y
nunca se transmite a la AEAT.
Desactivarlo aparca las devoluciones, no las descarta. Con
refunds_enabled: false, cada devolución queda registrada en la
bandeja como
refund_autoinvoicing_disabled y se conserva, cifrada, 30 días. Dentro de
esa ventana elige una de las dos salidas, nunca las dos: vuelve a poner
refunds_enabled en true y
reprocesa el evento, o emite la
rectificativa a mano. Hacer ambas cosas deja la devolución con dos
rectificativas, cada una numerada en su serie y dada de alta en VeriFactu — la
idempotencia del reproceso solo reconoce las rectificativas emitidas por esa
misma vía automática, así que una hecha a mano no lo frena. Pasados los 30 días
el contenido se purga y emitirla a mano es la única salida que queda.
Ciclos de suscripción
Si cobras con Stripe Billing en tu cuenta conectada — suscripciones recurrentes mensuales o anuales — Factuarea puede emitir una factura automáticamente por cada ciclo cobrado. Cada renovación que cobra Stripe produce su propia factura conforme a VeriFactu, espejando el desglose real (líneas, periodo, impuestos) que Stripe ya calculó, igual que los cobros únicos.
Es un toggle separado, subscription_autoinvoicing_enabled (por defecto
false), por encima del flag general enabled. Ambos deben estar activos para
que un ciclo se facture — activar solo las suscripciones no hace nada mientras la
auto-facturación está globalmente desactivada.
curl -X PUT https://api.factuarea.com/v1/stripe-autoinvoicing/config \
-H "Authorization: Bearer fact_test_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 0192f3a4-…" \
-d '{
"enabled": true,
"subscription_autoinvoicing_enabled": true
}'Qué ciclos se facturan
Factuarea factura el ciclo de facturación estándar y deriva el resto a revisión
o lo ignora, según el billing_reason de Stripe:
billing_reason | Qué hace Factuarea |
|---|---|
subscription_create (primer ciclo) | Se factura |
subscription_cycle (cada renovación) | Se factura |
subscription_update, subscription_threshold (prorrateos standalone) | Revisión manual — se registra en el log de la integración, no se auto-factura |
manual, upcoming, quote_accept, otros | Se ignora con un log informativo |
- Los trials no facturan. Un ciclo cuyo
invoice.totales 0 € (un periodo de prueba, o un ciclo cubierto íntegramente por crédito) no emite factura; el primer cobro real tras el trial se factura con normalidad. - Los prorrateos standalone (un upgrade/downgrade cobrado por su cuenta, fuera
del ciclo regular) no se auto-facturan hoy — van a revisión manual para que
decidas. Aparecen en la
bandeja como
subscription_proration_review, que es accionable pero no reprocesable: elbilling_reasonnunca cambia, así que la factura hay que emitirla a mano. El ciclo regular siguiente se factura como siempre. - La decisión F1/F2 es la misma. El NIF del destinatario se lee de los
customer_tax_idsde la invoice (más la ficha del cliente resuelto); con NIF el ciclo es una factura ordinaria (F1), sin él e igual o por debajo del umbral una simplificada (F2), y sin él por encima del umbral (o con "exigir NIF" activado) va a revisión manual — idéntico a las reglas de la sección de decisión. Se aplican el mismo espejo de líneas/IVA de Stripe Tax, la conversión a EUR y la validación de total.
Cada ciclo es su propia factura
Un ciclo de suscripción se convierte en una factura suelta — no crea ni
toca ninguna factura recurrente de Factuarea. Las dos son independientes:
Stripe lleva la cadencia y cada invoice.paid produce una factura.
Evita la doble facturación. Si ya modelas la suscripción del mismo cliente como una factura recurrente manual en Factuarea, activar la auto-facturación de suscripciones para esa suscripción de Stripe producirá dos facturas por periodo — una de tu plantilla recurrente y otra del ciclo de Stripe. Elige una sola fuente por cliente: detén la factura recurrente manual o deja este toggle desactivado para esas suscripciones.
El evento saliente
Una factura de ciclo de suscripción emite un evento distinto,
invoice.subscription_auto_created
(además de payment.received), de modo que un receptor de webhooks puede
distinguir los ciclos de suscripción de los cobros únicos. Un cobro único sigue
emitiendo invoice.auto_created como antes. El listado de cobros auto-facturados
(Listar cobros auto-facturados)
expone el contexto de suscripción (subscription_id, stripe_invoice_id,
period_start, period_end) para los cobros de ciclo (null para cobros únicos),
y un filtro opcional origin (subscription/oneshot).
Ese mismo contexto viaja también en la propia factura, como claves de metadata de sistema por las que puedes filtrar los listados. En Metadata y conciliación tienes la tabla completa —qué claves se escriben siempre y cuáles se omiten cuando no aplican— y dos recetas listas para usar: todas las facturas de una suscripción, y las facturas de un único periodo de facturación.
La idempotencia es por ciclo de facturación. Factuarea se basa en cada id de
invoice de Stripe (in_xxx): un evento reentregado, o un segundo evento del
mismo ciclo, produce como máximo una factura.
Un ciclo que llegó con el toggle apagado todavía se puede facturar — durante 30
días. No se descarta en silencio: queda registrado en la
bandeja como
subscription_autoinvoicing_disabled, con su contenido guardado cifrado. Activa
subscription_autoinvoicing_enabled (y enabled) y
reprocesa el evento dentro de esa
ventana, y se emite la factura real del ciclo — con su número de serie y su
registro VeriFactu, igual que si se hubiera facturado en su momento. Pasados los
30 días el contenido se purga, la fila permanece y la única salida es emitir la
factura a mano. Los ciclos cobrados antes de que tu endpoint de Connect empezara
a enviar invoice.paid nunca llegaron a Factuarea, así que ahí no hay nada que
reprocesar.
Para recibir los ciclos de suscripción debes habilitar el evento
invoice.paid en el endpoint de webhook de Connect en el Dashboard de
Stripe. Como siempre, valida el flujo primero con una clave fact_test_: en el
sandbox el registro de Alta de VeriFactu se crea localmente
y nunca se transmite a la AEAT.
Valores por defecto de un vistazo
| Campo | Por defecto | Efecto del valor por defecto |
|---|---|---|
enabled | false | Auto-facturación desactivada — no se emite nada hasta que la actives |
simplified_threshold_cents | 40000 (400 €) | Los cobros sin NIF de hasta 400 € se convierten en F2 |
require_nif | false | Se permiten facturas simplificadas; el identificador fiscal se ofrece pero no se exige |
refunds_enabled | true | Una devolución de Stripe genera una factura rectificativa automática (mientras la auto-facturación está activada) |
subscription_autoinvoicing_enabled | false | Los ciclos de suscripción de Stripe no se auto-facturan hasta que lo actives (requiere también enabled) |
Con estos valores por defecto, el único cambio de comportamiento una vez activada
la auto-facturación es que un cobro sin NIF de hasta 400 € se convierte en factura
simplificada en lugar de esperar en revisión, y una devolución genera su factura
rectificativa automáticamente. Los cobros con NIF se comportan exactamente igual
que antes. Los ciclos de suscripción quedan desactivados hasta que optes por ellos
con subscription_autoinvoicing_enabled.