Appearance
Migracion: portal_payments
Informacion
- Database:
{tenant}(cada DB de tenant) - Schema: Dinamico — mismo schema que
ordcon - Level:
LEVEL_SUCURSALoLEVEL_EMPRESAsegun configuracion del tenant
Proposito
Tabla de pagos online realizados a traves del portal. Registra el estado completo del pago, la respuesta del gateway, y la vinculacion automatica con el recibo generado en el ERP.
Regla de Nivel de Migracion
Misma regla que portal_users: la migracion debe ejecutarse en el mismo nivel que ordcon para el tenant.
php
public function getLevel(): array
{
$ordconLevel = $this->getTableLevel('ordcon');
return [$ordconLevel];
}Definicion DDL
sql
CREATE TABLE portal_payments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cliente_id INTEGER NOT NULL,
tenant_id INTEGER NOT NULL,
sucursal_id INTEGER NOT NULL,
gateway VARCHAR(50) NOT NULL,
payment_method VARCHAR(50) NOT NULL,
amount NUMERIC(15,2) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
external_id VARCHAR(255),
external_response JSONB,
facturas_pagadas JSONB NOT NULL,
recibo_id INTEGER NULL,
payment_date TIMESTAMP NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
CONSTRAINT fk_portal_payments_ordcon
FOREIGN KEY (cliente_id)
REFERENCES ordcon(cnro)
ON DELETE RESTRICT,
CONSTRAINT chk_portal_payments_status
CHECK (status IN ('pending', 'approved', 'rejected', 'refunded', 'cancelled'))
);Indices
sql
-- Idempotencia: evita procesar el mismo pago del gateway dos veces
CREATE UNIQUE INDEX uq_portal_payments_external_id
ON portal_payments (external_id)
WHERE external_id IS NOT NULL;
-- Pagos de un cliente
CREATE INDEX idx_portal_payments_cliente_id
ON portal_payments (cliente_id);
-- Filtrado por estado
CREATE INDEX idx_portal_payments_status
ON portal_payments (status);
-- Filtrado por gateway (reportes, estadisticas por proveedor)
CREATE INDEX idx_portal_payments_gateway
ON portal_payments (gateway);Campos
| Campo | Tipo | Restricciones | Descripcion |
|---|---|---|---|
id | UUID | PK, default gen_random_uuid() | Identificador unico del pago |
cliente_id | integer | FK → ordcon.cnro, NOT NULL | Cliente que realizo el pago |
tenant_id | integer | NOT NULL | ID del tenant (para resolucion de contexto en webhook) |
sucursal_id | integer | NOT NULL | ID de la sucursal (para resolucion de schema en webhook) |
gateway | varchar(50) | NOT NULL | Adapter de gateway usado (paypertic, mercadopago). Para webhook routing y reportes |
payment_method | varchar(50) | NOT NULL | Metodo: mercadopago, pagotic, etc. |
amount | numeric(15,2) | NOT NULL | Monto total del pago |
status | varchar(20) | NOT NULL, CHECK, default 'pending' | Estado del pago |
external_id | varchar(255) | UNIQUE (parcial, WHERE NOT NULL) | ID del pago en el gateway externo |
external_response | jsonb | nullable | Respuesta completa del gateway (para debug/auditoria) |
facturas_pagadas | jsonb | NOT NULL | Array de {id, tipo, numero, monto} de facturas pagadas |
recibo_id | integer | FK → recibo, nullable | Recibo generado automaticamente tras aprobacion |
payment_date | timestamp | NOT NULL | Fecha/hora en que se realizo el pago |
created_at | timestamp | NOT NULL, default now() | Fecha de creacion del registro |
updated_at | timestamp | NOT NULL, default now() | Ultima modificacion |
Restricciones
- FK a ordcon:
cliente_idreferenciaordcon.cnrocon RESTRICT (no CASCADE). Un cliente con pagos registrados no puede eliminarse del ERP sin resolver primero los pagos. - UNIQUE external_id (parcial): Solo aplica cuando
external_idno es NULL. Esto permite registros sin external_id (pagos manuales o en proceso de creacion) pero garantiza que los IDs del gateway no se dupliquen. - CHECK status: Solo acepta valores validos:
pending,approved,rejected,refunded,cancelled. - recibo_id nullable: Es NULL cuando el pago esta pendiente o fue rechazado. Se llena automaticamente cuando el webhook confirma la aprobacion y el sistema genera el recibo.
Flujo de Estados
mermaid
stateDiagram-v2
[*] --> pending : Pago iniciado
pending --> approved : Webhook approved
pending --> rejected : Webhook rejected
pending --> cancelled : Cancelado por usuario
approved --> refunded : Reembolso procesado
rejected --> [*]
cancelled --> [*]
refunded --> [*]
approved --> [*]pending→ Se crea al iniciar el pago, antes de redirigir al gatewayapproved→ El webhook del gateway confirma la aprobacion. Se genera recibo automaticamente.rejected→ El gateway rechazo el pago. No se genera recibo.cancelled→ El usuario cancelo el pago antes de que fuera aprobado.refunded→ Reembolso posterior a una aprobacion.
Rollback
sql
DROP TABLE IF EXISTS portal_payments CASCADE;