Appearance
Jobs de Recuperación de Pagos del Portal
⚠️ DOCUMENTACIÓN RETROSPECTIVA — Generada a partir de código implementado el 2026-06-24
Módulo: Portal de Clientes Tipo: Process Estado: Implementado Fecha: 2026-06-24
Descripción
Dos scripts CLI ejecutados via cron resuelven automáticamente los pagos que quedan en estado intermedio sin resolución del gateway o con recibo_error de TX2.
El diseño prioriza el aislamiento por tenant: un fallo en un tenant no aborta el sweep global.
Scripts
| Script | Frecuencia | Propósito |
|---|---|---|
cli/sweep-portal-payments.php | Cada 30 min | Barrer filas issued/pending stale (>2h). Issued: consultar gateway → reconciliar o cancelar. Pending: cancelar directamente. |
cli/retry-recibo-errors.php | Cada 10 min | Reintentar filas approved con recibo_error (TX2 falló antes). Cap: 3 intentos. AMOUNT_MISMATCH excluido siempre. |
Entradas de crontab (solo referencia — no instaladas automáticamente):
cron
*/30 * * * * php /var/www/Bautista/server/cli/sweep-portal-payments.php >> /var/www/Bautista/server/logs/sweep-portal-payments.log 2>&1
*/10 * * * * php /var/www/Bautista/server/cli/retry-recibo-errors.php >> /var/www/Bautista/server/logs/retry-recibo-errors.log 2>&1Ver referencia completa en portal-payment-recovery-crontab.md.
Flujo de sweep-portal-payments.php
Para cada tenant con modulo_portal_clientes=1:
Filas issued (>2h, sin resolución):
→ getPaymentStatus(gateway_payment_id)
→ APPROVED → AutoReconciliacionService::reconciliar()
→ cualquier otro estado → markCancelled() en TX
Filas pending (>2h):
→ markCancelled() en TX (sin consultar gateway — pending nunca tiene gateway_payment_id)Argumentos disponibles:
| Argumento | Default | Descripción |
|---|---|---|
--dry-run | — | Imprime lo que haría, sin mutar nada |
--ttl-hours=N | 2 | Umbral de antigüedad para considerar una fila stale |
Códigos de salida: 0 = éxito (errores por fila se loguean pero no abortan), 1 = error fatal en setup.
Flujo de retry-recibo-errors.php
Para cada tenant con modulo_portal_clientes=1:
Filas approved con recibo_error IS NOT NULL
(excluye AMOUNT_MISMATCH a nivel SQL):
sweep_retries < 3:
→ AutoReconciliacionService::reconciliar()
→ En éxito: clearReciboError() + incrementSweepRetry() en TX
sweep_retries >= 3:
→ AlertService::sendAlert(type='portal_payment_retry_cap_reached')
→ skipsweep_retries se persiste en portal_payments.gateway_response JSONB como {"sweep_retries": N}.
Reglas de negocio
RN-001: Aislamiento por tenant Cada tenant se envuelve en try/catch con continue. Un fallo de bootstrap en tenant A no afecta el procesamiento del tenant B.
RN-002: AMOUNT_MISMATCH nunca se reintenta Filas con recibo_error LIKE 'AMOUNT_MISMATCH:%' se excluyen a nivel SQL en la query de findApprovedWithReciboError() y con un guard en RetryReciboErrorsService::processRow(). Requieren intervención humana obligatoria.
RN-003: Cap de reintentos = 3RetryReciboErrorsService usa retryCap = 3 (configurable por constructor). Al alcanzar el cap, AlertService::sendAlert() emite alerta con type='portal_payment_retry_cap_reached'. La fila no se vuelve a procesar hasta que el sweep_retries sea reseteado manualmente.
RN-004: Transacciones CUD Toda operación de escritura en los services (markCancelled, incrementSweepRetry, clearReciboError) se envuelve en transacción sobre la conexión principal. AutoReconciliacionService gestiona su propia TX2 internamente.
RN-005: Dry-run seguro Ambos scripts respetan --dry-run: iteran y loguean lo que harían pero no ejecutan ningún CUD ni llamada al gateway.
Clases involucradas
| Clase | Archivo | Rol |
|---|---|---|
SweepPortalPaymentsService | Modules/Portal/Application/Sweep/SweepPortalPaymentsService.php | Lógica unitesteable de decisión para filas issued/pending |
RetryReciboErrorsService | Modules/Portal/Application/Sweep/RetryReciboErrorsService.php | Lógica unitesteable de retry con cap y alertas |
AutoReconciliacionService | Modules/Portal/Application/Reconciliacion/Services/AutoReconciliacionService.php | TX2 de conciliación, usada por ambos scripts |
AlertService | App/Core/Services/AlertService.php | Envío de alertas al webhook configurado |
Ver también
⚠️ NOTA IMPORTANTE: Validar con stakeholders antes de considerar final.