Appearance
Portal Payment Recovery — Crontab Reference
Status: INSTALLED via deploy.sh
phase_postdeploy(). Cron entries are automatically installed whenAPP_ENV=productionis set. Seebautista-backend/etc/cron.d/bautista-portal-recovery.distfor the template.
Scripts
| Script | Purpose | Frequency |
|---|---|---|
cli/sweep-portal-payments.php | Sweep stale issued/pending rows — poll gateway for issued rows (APPROVED → reconcile, else cancel), cancel pending rows directly | Every 30 min |
cli/retry-recibo-errors.php | Retry approved rows with recibo_error — reconcile again if sweep_retries < 3; send alert if cap reached | Every 10 min |
Cron Template (after {PROJECT_ROOT} substitution)
cron
# bautista-portal-recovery — managed by deploy.sh — do not edit manually
MAILTO=""
# Sweep stale issued/pending portal payment rows — every 30 min
*/30 * * * * www-data flock -n /var/lock/sweep-portal-payments.lock php /var/www/Bautista/bautista-backend/cli/sweep-portal-payments.php >> /var/log/bautista/sweep-portal-payments.log 2>&1
# Retry portal payments with recibo_error — every 10 min
*/10 * * * * www-data flock -n /var/lock/retry-recibo-errors.lock php /var/www/Bautista/bautista-backend/cli/retry-recibo-errors.php >> /var/log/bautista/retry-recibo-errors.log 2>&1Cron Installation
deploy.sh phase_postdeploy() calls install_cron() automatically.
- Production host (
APP_ENV=production): installs/etc/cron.d/bautista-portal-recovery(mode 644) if writable, else falls back to idempotentwww-datacrontab. - Non-production (
APP_ENVabsent or any other value): install is skipped — fail-safe default. - Dry-run (
--dry-run): prints rendered template to stdout, writes nothing.
Both install paths are idempotent: running deploy twice produces exactly one cron entry per script. A schedule change overwrites the previous entry.
Manual Migration (REQUIRED before first deploy with logging)
The cron_job_log table must exist in the ini database before the first deploy that ships CronJobLogger::summary() calls.
Run this manually before or after deploy as needed:
bash
php migrations/migrate-db-command.php --migrateIf the table does not exist,
summary()INSERTs fail silently (caught and written to stderr) — the scripts continue processing tenants. The migration is NOT run bydeploy.sh.
APP_ENV Requirement
Production hosts MUST set APP_ENV=production explicitly in the .env file or system environment to enable cron installation:
dotenv
APP_ENV=productionAny other value (absent, development, staging, etc.) causes the cron install to be skipped without error.
Notes
- Both scripts iterate tenants from
ini.sistemafiltered bymodulo_portal_clientes = 1. - Per-tenant isolation: a failure in one tenant's bootstrap does NOT abort the sweep for other tenants (try/catch + continue).
- Each script logs errors to
errorbri(ini DB) and summaries tocron_job_log(ini DB) viaCronJobLogger. A logging failure never aborts tenant processing. sweep-portal-payments.phpuses a 2-hour TTL by default. Override with--ttl-hours=N.- Both scripts support
--dry-runfor safe inspection without mutations. retry-recibo-errors.phpexcludesAMOUNT_MISMATCH:rows at SQL level AND inRetryReciboErrorsService(defence-in-depth). Those rows require human intervention.- Retry cap is 3 (
sweep_retriesingateway_responseJSONB). On cap reached,AlertService::sendAlert()fires once per cron run. flock -nwraps each script invocation — concurrent runs of the same script exit immediately (non-zero) without processing tenants.
Log locations
/var/log/bautista/sweep-portal-payments.log
/var/log/bautista/retry-recibo-errors.logRollback
- Remove cron entries: delete
/etc/cron.d/bautista-portal-recoveryor remove matching lines from thewww-datacrontab. - Revert commits.
- Verify no stale rows were left in an unexpected state — run
SELECT status, count(*) FROM empresa.portal_payments GROUP BY statusper tenant. - The
cron_job_logtable is additive — dropping it is optional.