Skip to content

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ónFechaDescripción
20260410120001_create_portal_payments2026-04-10Scaffold Phase 1 (mínimo)
20260422000002_alter_portal_payments_add_payment_columns2026-04-22Phase 3: columnas de ciclo de vida, renombre de legacy columns
20260512100000_alter_portal_payments_add_recibo_columns2026-05-12Auto-reconciliación: recibo_at, recibo_error
20260512120000_alter_portal_payments_recibo_id_to_uuid2026-05-12Fix UUID: recibo_id BIGINT → VARCHAR(36)
20260512130000_alter_portal_payments_drop_legacy_columns2026-05-12Drop 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

CampoTipoRestriccionesDescripción
idserialPKIdentificador secuencial del pago
portal_user_idintegerFK → portal_users.id, NOT NULLUsuario del portal que inició el pago
ordcon_idintegernullableFK lógica → ordcon.cnro (cliente)
tenant_idintegerNOT NULLID del tenant (para resolución de contexto en webhook)
sucursal_idintegerNOT NULLID de la sucursal (para resolución de schema en webhook/reconciliación)
gatewayvarchar(50)nullableAdapter de gateway: paypertic, mercadopago
currencyvarchar(10)nullable, default 'ARS'Moneda del pago
montonumeric(15,2)nullableMonto total del pago
statusvarchar(20)NOT NULL, CHECKEstado del pago (ver Flujo de Estados)
gateway_responsejsonbnullableRespuesta completa del gateway (para debug/auditoría)
facturas_jsonjsonbNOT NULLArray de facturas pagadas: [{id, tipo, numero, monto, pago, saldo}]
gateway_payment_idvarchar(255)nullableID del pago en el gateway externo
referencevarchar(255)nullableReferencia de correlación para webhooks
recibo_idvarchar(36)nullableUUID del recibo generado en ordcta post-conciliación automática
recibo_attimestamptznullableTimestamp de conciliación automática exitosa (TX2 completada)
recibo_errortextnullableMensaje del último error de TX2 si la conciliación falló
refund_idvarchar(64)nullableID del reembolso en el gateway (si aplica)
issued_attimestamptznullableTimestamp de emisión del pago (redirección al gateway)
approved_attimestamptznullableTimestamp de aprobación del gateway
rejected_attimestamptznullableTimestamp de rechazo del gateway
refunded_attimestamptznullableTimestamp de reembolso procesado
cancelled_attimestamptznullableTimestamp de cancelación por el usuario
created_attimestampNOT NULL, default now()Fecha de creación del registro
updated_attimestampnullableÚ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 NULL indica éxito; recibo_error IS NOT NULL AND recibo_id IS NULL indica 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 gateway
  • issued → Preferencia creada en el gateway, cliente en proceso de pago
  • approved → Gateway confirmó aprobación. TX2 intenta crear recibo automáticamente
  • rejected → Gateway rechazó el pago
  • cancelled → Usuario canceló antes de completar el pago
  • refunded → Reembolso posterior a una aprobación

Estado post-TX2

recibo_idrecibo_atrecibo_errorInterpretación
NULLNULLNULLTX2 pendiente o en ejecución
<uuid><ts>NULLConciliación automática exitosa
NULLNULL<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:

ColumnaMotivo de eliminación
amountReemplazada por monto en Phase 3
facturasReemplazada por facturas_json en Phase 3
external_idReemplazada por gateway_payment_id en Phase 3

Rollback

sql
DROP TABLE IF EXISTS portal_payments CASCADE;