El timeout solo describe lo que sabe el cliente
Un timeout de red significa que el caller no recibió una respuesta concluyente a tiempo. No demuestra que el sistema remoto rechazó, revirtió o nunca recibió la petición. En pagos, authorization, capture o refund pueden haber tenido efecto financiero aunque el cliente vea una excepción.
Después de un timeout ambiguo, el estado más seguro no es failed sino unknown. Convertir unknown en failed mezcla dos realidades distintas y aumenta el riesgo de duplicate execution.
La idempotencia cambia la decisión de retry
Un retry es mucho más seguro cuando el proveedor ofrece idempotencia para esa operación y puede reutilizarse la misma key. La identidad debe representar la acción de negocio, no el intento HTTP. Crear una key nueva en cada retry permite que el proveedor interprete cada llamada como una operación distinta.
Los proveedores difieren en operaciones soportadas, retención de keys, concurrencia y respuesta a una repetición. Estas diferencias deben vivir en la lógica de payment recovery y no esconderse en un middleware genérico.
Recupera el estado antes de crear una segunda acción financiera
Si el resultado es incierto, primero intenta reconciliar la intención original con el estado del proveedor: consulta por merchant reference, consume eventos o usa un mapping durable entre el attempt y el recurso del proveedor. Solo si no puede recuperarse el estado debe evaluarse otra ejecución.
Por eso payment retry es una decisión de orchestration, no solo de HTTP reliability. Depende del business state, del proveedor, del tiempo transcurrido y de la consecuencia financiera de repetir.
Regla práctica
Reintenta automáticamente solo si la operación es segura bajo el contrato de idempotencia del proveedor o si el sistema ha demostrado que la acción original no se completó. En cualquier otro caso, mueve el pago a un recovery state explícito y resuelve la incertidumbre antes de continuar.
Separa el fallo de transporte del fallo de estado de negocio
Un payment state machine fiable necesita al menos tres conceptos distintos: el estado de la petición del cliente, el estado de la operación en el proveedor y el business state que tu sistema decide exponer. En el happy path suelen coincidir y por eso es tentador colapsarlos. El problema aparece cuando la red falla: una petición puede fallar localmente mientras la operación remota termina con éxito, o el proveedor puede aceptar el trabajo y publicar el resultado de forma asíncrona después de que la llamada original ya haya hecho timeout.
La consecuencia práctica es que un transport error no debería disparar automáticamente transiciones de negocio irreversibles. Un payment attempt que termina en timeout puede necesitar un estado como pending verification, unknown o recovery required. Ese estado permite detener la progresión automática mientras se reúne evidencia y evita que servicios downstream interpreten la ausencia de respuesta como prueba de que no se movió dinero.
Construye una jerarquía de recovery consciente del proveedor
El recovery debe empezar por la fuente de verdad más fuerte disponible. Si el proveedor permite buscar por idempotency key, merchant reference, payment intent o provider transaction ID, úsalo antes de crear otro attempt. Si esperas un evento durable del proveedor, correlaciónalo con el intento original y espera dentro de una ventana de recovery limitada. Si no existe ninguno de esos mecanismos, es mejor escalar a reconciliación diferida o manual que adivinar.
Ese recovery específico por proveedor explica por qué las librerías genéricas de HTTP retry no bastan para operaciones financieras. Repetir una lectura no es igual que repetir una autorización; repetir una autorización no es igual que repetir un capture; repetir un refund puede ser aún más peligroso porque la transacción visible para el cliente ya puede estar completa. La política de recovery debe vivir cerca de la semántica de pagos, no solo de la semántica de red.
Diseña observability alrededor de la incertidumbre
Los dashboards operativos deberían mostrar los pagos inciertos como un cohort propio. Mide cuántos attempts entran en estado ambiguo, qué proveedores los generan, cuánto tarda el recovery y cuántos se resuelven por lookup, webhook, reconciliación o intervención manual. Si los estados unknown se esconden dentro de un error rate genérico, el equipo optimizará la métrica equivocada y puede responder aumentando retries, precisamente lo que eleva el riesgo de duplicados.
Las alertas también deben separar el aumento de outcomes ambiguos del aumento de declines explícitos. Un proveedor con más issuer declines tiene un problema distinto a uno que hace timeout después de recibir la petición. El primero afecta principalmente conversión; el segundo añade incertidumbre de conversión y riesgo de integridad financiera. Distintas clases de fallo necesitan distintos playbooks.
Framework de decisión antes de un retry automático
Antes de reintentar responde cuatro preguntas: ¿se sabe que la operación original no terminó? ¿el proveedor garantiza comportamiento idempotente para esa operación y key? ¿el intento original puede consultarse o reconciliarse? ¿qué impacto financiero tendría que ambos intentos terminaran? Si las dos primeras respuestas son inciertas y el impacto de un duplicado es material, el retry automático debe detenerse.
Este enfoque favorece deliberadamente la corrección financiera frente a la finalización inmediata. No significa aceptar mala experiencia de cliente. Significa hacer explícito el recovery: mostrar pending cuando corresponda, continuar verification de forma asíncrona y comunicar al cliente un estado solo cuando exista evidencia. Unos segundos de incertidumbre honesta suelen costar menos que un duplicate charge y el soporte, refund, dispute y pérdida de confianza que lo siguen.
Un timeout es un resultado de transporte indeterminado, no prueba de fallo del pago.
Reutiliza la misma identidad de idempotencia para la misma acción de negocio.
Modela las semánticas específicas de cada proveedor.
Reconcilia los estados unknown antes de volver a ejecutar.
