@startuml uml-state-factura !include _style.iuml title 8. State machine — Factura CFDI 4.0 (estatus_factura) [*] --> pendiente_timbrado : solicitar_emision(pago)\n/ webhook payment_intent.succeeded\n (Phase 5 — emision automatica) state pendiente_timbrado { pendiente_timbrado : Encolada en lib/billing/queue.ts pendiente_timbrado : pac_proveedor = 'facturama' pendiente_timbrado : (sandbox CFDI 4.0) } state timbrada { timbrada : uuid_cfdi = UUID asignado por PAC timbrada : serie + folio timbrada : xml_contenido + pdf_contenido (base64 TEST) timbrada : / object store (PROD) timbrada : pac_referencia = id interno PAC } state fallida { fallida : webhook_events.error poblado fallida : retry_count incrementado } state cancelada { cancelada : cancelada_at = NOW() cancelada : cancelada_motivo (01-04 SAT) cancelada : Acuse SAT guardado } pendiente_timbrado --> timbrada : timbrar() / PAC devuelve UUID-SAT\n (PacAdapter.facturama.timbrar) pendiente_timbrado --> fallida : timbrado_fallo(error)\n / Facturama 5xx o validacion XSD fallida --> pendiente_timbrado : retimbrar() / cron 15min\n o admin manual (max 3 reintentos) fallida --> [*] : descartar() / dropear tras 3 fallos timbrada --> cancelada : cancelar(motivo, aprobador)\n [SoD: emisor != cancelador]\n / PAC genera acuse SAT timbrada --> [*] cancelada --> [*] note right of pendiente_timbrado Nuevo estado introducido por migrations/007_pagos_facturas_extensions.sql. La emision es asincrona: el webhook ack a Stripe NO bloquea espera de PAC. end note note right of timbrada XML + PDF se almacenan en columnas base64 para entorno TEST. PROD migra a object store (R2/S3) sin cambio de modelo: el adaptador escribe ambos. end note note left of fallida El pago NUNCA queda en estado inconsistente si la factura falla. estatus_pago='procesado' se confirma ANTES de intentar timbrado. end note note left of cancelada Reglas SAT: cancelable solo si no han pasado mas de 72h o si la contraparte acepta. Motivos validos: 01-04. Acuse obligatorio para auditoria. end note footer Fuente: ENUM estatus_factura 00.sql:65 + migrations/007 (pendiente_timbrado, fallida) | tabla facturas 00.sql:559 + migration 007 | (pendiente: lib/billing/{cfdi,queue,pac/facturama}.ts) @enduml