Appearance
Migración: portal_payments
Información
- Database:
{tenant}(cada DB de tenant) - Schema:
public(LEVEL_EMPRESA) - Level:
LEVEL_EMPRESA— tabla compartida para todos los schemas del tenant
Propósito
Tabla de pagos online realizados a través del portal de clientes. Registra el ciclo de vida completo del pago: desde la iniciación hasta la aprobación del gateway y la conciliación automática en ctacte.
Historia de Migraciones
| Migración | Fecha | Descripción |
|---|---|---|
20260410120001_create_portal_payments | 2026-04-10 | Scaffold Phase 1 (mínimo) |
20260422000002_alter_portal_payments_add_payment_columns | 2026-04-22 | Phase 3: columnas de ciclo de vida, renombre de legacy columns |
20260512100000_alter_portal_payments_add_recibo_columns | 2026-05-12 | Auto-reconciliación: recibo_at, recibo_error |
20260512120000_alter_portal_payments_recibo_id_to_uuid | 2026-05-12 | Fix UUID: recibo_id BIGINT → VARCHAR(36) |
20260512130000_alter_portal_payments_drop_legacy_columns | 2026-05-12 | Drop columnas legacy: amount, facturas, external_id |
DDL Actual (post todas las migraciones)
sql
-- Tabla creada en LEVEL_EMPRESA (schema public)
-- Custodiada por la feature flag: isPortalClientesEnabled()
CREATE TABLE portal_payments (
id SERIAL PRIMARY KEY,
portal_user_id INTEGER NOT NULL REFERENCES portal_users(id),
ordcon_id INTEGER NULL,
tenant_id INTEGER NOT NULL,
sucursal_id INTEGER NOT NULL,
gateway VARCHAR(50) NULL,
currency VARCHAR(10) NULL DEFAULT 'ARS',
monto NUMERIC(15,2) NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
gateway_response JSONB NULL,
facturas_json JSONB NOT NULL,
gateway_payment_id VARCHAR(255) NULL,
reference VARCHAR(255) NULL,
recibo_id VARCHAR(36) NULL,
recibo_at TIMESTAMPTZ NULL,
recibo_error TEXT NULL,
refund_id VARCHAR(64) NULL,
issued_at TIMESTAMPTZ NULL,
approved_at TIMESTAMPTZ NULL,
rejected_at TIMESTAMPTZ NULL,
refunded_at TIMESTAMPTZ NULL,
cancelled_at TIMESTAMPTZ NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NULL,
CONSTRAINT portal_payments_status_check
CHECK (status IN ('pending','issued','approved','rejected','refunded','cancelled'))
);Índices
sql
-- Pagos de un cliente (ordcon)
CREATE INDEX idx_portal_payments_ordcon_id
ON portal_payments (ordcon_id);
-- Filtrado por estado
CREATE INDEX idx_portal_payments_status
ON portal_payments (status);
-- Filtrado por gateway (reportes, estadísticas por proveedor)
CREATE INDEX idx_portal_payments_gateway
ON portal_payments (gateway);
-- Búsqueda por referencia (webhook correlation)
CREATE INDEX idx_portal_payments_reference
ON portal_payments (reference);Campos
| Campo | Tipo | Restricciones | Descripción |
|---|---|---|---|
id | serial | PK | Identificador secuencial del pago |
portal_user_id | integer | FK → portal_users.id, NOT NULL | Usuario del portal que inició el pago |
ordcon_id | integer | nullable | FK lógica → ordcon.cnro (cliente) |
tenant_id | integer | NOT NULL | ID del tenant (para resolución de contexto en webhook) |
sucursal_id | integer | NOT NULL | ID de la sucursal (para resolución de schema en webhook/reconciliación) |
gateway | varchar(50) | nullable | Adapter de gateway: paypertic, mercadopago |
currency | varchar(10) | nullable, default 'ARS' | Moneda del pago |
monto | numeric(15,2) | nullable | Monto total del pago |
status | varchar(20) | NOT NULL, CHECK | Estado del pago (ver Flujo de Estados) |
gateway_response | jsonb | nullable | Respuesta completa del gateway (para debug/auditoría) |
facturas_json | jsonb | NOT NULL | Array de facturas pagadas: [{id, tipo, numero, monto, pago, saldo}] |
gateway_payment_id | varchar(255) | nullable | ID del pago en el gateway externo |
reference | varchar(255) | nullable | Referencia de correlación para webhooks |
recibo_id | varchar(36) | nullable | UUID del recibo generado en ordcta post-conciliación automática |
recibo_at | timestamptz | nullable | Timestamp de conciliación automática exitosa (TX2 completada) |
recibo_error | text | nullable | Mensaje del último error de TX2 si la conciliación falló |
refund_id | varchar(64) | nullable | ID del reembolso en el gateway (si aplica) |
issued_at | timestamptz | nullable | Timestamp de emisión del pago (redirección al gateway) |
approved_at | timestamptz | nullable | Timestamp de aprobación del gateway |
rejected_at | timestamptz | nullable | Timestamp de rechazo del gateway |
refunded_at | timestamptz | nullable | Timestamp de reembolso procesado |
cancelled_at | timestamptz | nullable | Timestamp de cancelación por el usuario |
created_at | timestamp | NOT NULL, default now() | Fecha de creación del registro |
updated_at | timestamp | nullable | Última modificación |
Restricciones
- CHECK status: Acepta exactamente:
pending,issued,approved,rejected,refunded,cancelled. - recibo_id nullable: NULL cuando el pago aún no fue conciliado o TX2 falló. Cuando TX2 completa exitosamente, toma el UUID del recibo en ordcta.
- recibo_at / recibo_error: Solo uno puede estar poblado a la vez.
recibo_at IS NOT NULLindica éxito;recibo_error IS NOT NULL AND recibo_id IS NULLindica fallo de TX2.
Flujo de Estados
mermaid
stateDiagram-v2
[*] --> pending : POST /portal/pagos/iniciar
pending --> issued : Preferencia creada en gateway
issued --> approved : Webhook approved → TX1 commit + TX2 auto-reconciliación
issued --> rejected : Webhook rejected
issued --> cancelled : Usuario cancela
approved --> refunded : Reembolso procesado
rejected --> [*]
cancelled --> [*]
refunded --> [*]
approved --> [*]pending→ Pago registrado, antes de redirigir al gatewayissued→ Preferencia creada en el gateway, cliente en proceso de pagoapproved→ Gateway confirmó aprobación. TX2 intenta crear recibo automáticamenterejected→ Gateway rechazó el pagocancelled→ Usuario canceló antes de completar el pagorefunded→ Reembolso posterior a una aprobación
Estado post-TX2
| recibo_id | recibo_at | recibo_error | Interpretación |
|---|---|---|---|
| NULL | NULL | NULL | TX2 pendiente o en ejecución |
<uuid> | <ts> | NULL | Conciliación automática exitosa |
| NULL | NULL | <msg> | TX2 falló — requiere atención |
Columnas Eliminadas (legacy)
Las siguientes columnas existieron en el scaffold Phase 1 y fueron eliminadas en la migración 20260512130000:
| Columna | Motivo de eliminación |
|---|---|
amount | Reemplazada por monto en Phase 3 |
facturas | Reemplazada por facturas_json en Phase 3 |
external_id | Reemplazada por gateway_payment_id en Phase 3 |
Rollback
sql
DROP TABLE IF EXISTS portal_payments CASCADE;