Files
2026-05-16 21:41:44 -05:00

171 lines
7.7 KiB
Go

package models
import (
"github.com/sujit-baniya/fiber-boilerplate/app"
"gorm.io/gorm"
)
// SaasApiConfig almacena la configuración del endpoint externo al que se notifica
// cuando un pago es confirmado para un producto SaaS.
// La vinculación es: Contrato → Servicios (m2m) → servicio_id ↔ SaasProducto.ServicioID → SaasApiConfig.SaasID
type SaasApiConfig struct {
gorm.Model
SaasID uint `json:"saas_id" gorm:"column:saas_id;not null;index"`
SaasProducto SaasProducto `json:"saas_producto" gorm:"foreignKey:SaasID"`
Nombre string `json:"nombre" gorm:"column:nombre;not null"` // etiqueta amigable
// Pasarela que dispara este callback: dlocal | bold | ambas (default)
Pasarela string `json:"pasarela" gorm:"column:pasarela;default:'ambas'"`
EndpointURL string `json:"endpoint_url" gorm:"column:endpoint_url;type:text;not null"`
Metodo string `json:"metodo" gorm:"column:metodo;default:'POST'"` // POST|PUT|GET
ApiKeyHeader string `json:"api_key_header" gorm:"column:api_key_header"` // ej: "X-API-Key"
ApiKeyValue string `json:"api_key_value" gorm:"column:api_key_value;type:text"` // valor secreto
// PayloadTemplate es un JSON con marcadores que se reemplazarán antes de enviar.
// Variables disponibles: {{.ContratoID}} {{.Referencia}} {{.Email}} {{.Monto}} {{.Moneda}} {{.SaasID}} {{.SaasSlug}} {{.Fuente}} {{.WebhookToken}}
PayloadTemplate string `json:"payload_template" gorm:"column:payload_template;type:text"`
TimeoutSeg int `json:"timeout_seg" gorm:"column:timeout_seg;default:10"`
Activo bool `json:"activo" gorm:"column:activo;default:true"`
// WebhookToken es un token único por integración que identifica las llamadas entrantes
// desde el SaaS externo vía POST /webhooks/saas-in/{token}
WebhookToken string `json:"webhook_token" gorm:"column:webhook_token;uniqueIndex;size:64"`
}
func (SaasApiConfig) TableName() string { return "saas_api_configs" }
// ─── SaasWebhookInLog ─────────────────────────────────────────────────────────
// SaasWebhookInLog registra cada llamada entrante desde un SaaS externo
// identificado por su WebhookToken único.
type SaasWebhookInLog struct {
gorm.Model
SaasApiConfigID uint `json:"saas_api_config_id" gorm:"column:saas_api_config_id;index"`
Token string `json:"token" gorm:"column:token;size:64;index"`
Metodo string `json:"metodo" gorm:"column:metodo;size:10"`
IP string `json:"ip" gorm:"column:ip;size:64"`
Body string `json:"body" gorm:"column:body;type:text"`
Headers string `json:"headers" gorm:"column:headers;type:text"`
}
func (SaasWebhookInLog) TableName() string { return "saas_webhook_in_logs" }
// ─── SaasDispatchLog ──────────────────────────────────────────────────────────
// SaasDispatchLog registra cada intento de notificación a un SaaS externo.
type SaasDispatchLog struct {
gorm.Model
SaasApiConfigID uint `json:"saas_api_config_id" gorm:"column:saas_api_config_id;index"`
SaasApiConfig SaasApiConfig `json:"saas_api_config" gorm:"foreignKey:SaasApiConfigID"`
ContratoID uint `json:"contrato_id" gorm:"column:contrato_id;index"`
Referencia string `json:"referencia" gorm:"column:referencia"`
PayerEmail string `json:"payer_email" gorm:"column:payer_email"`
Fuente string `json:"fuente" gorm:"column:fuente"` // dlocal | bold | manual
HttpStatus int `json:"http_status" gorm:"column:http_status"`
Respuesta string `json:"respuesta" gorm:"column:respuesta;type:text"`
ErrorMsg string `json:"error_msg" gorm:"column:error_msg;type:text"`
Estado string `json:"estado" gorm:"column:estado"` // success | failed
Intentos int `json:"intentos" gorm:"column:intentos;default:1"`
}
func (SaasDispatchLog) TableName() string { return "saas_dispatch_logs" }
// ─── Queries SaasApiConfig ────────────────────────────────────────────────────
func GetAllSaasApiConfigs(limit, offset int) ([]SaasApiConfig, int64, error) {
var items []SaasApiConfig
var total int64
db := app.Http.Database.DB.Model(&SaasApiConfig{}).Preload("SaasProducto")
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
if err := db.Order("created_at DESC").Limit(limit).Offset(offset).Find(&items).Error; err != nil {
return nil, 0, err
}
return items, total, nil
}
func GetSaasApiConfigByID(id uint) (*SaasApiConfig, error) {
var item SaasApiConfig
if err := app.Http.Database.DB.Preload("SaasProducto").First(&item, id).Error; err != nil {
return nil, err
}
return &item, nil
}
// GetSaasApiConfigsBySaasIDs devuelve configuraciones activas para los saas_ids dados,
// opcionalmente filtradas por pasarela ("dlocal", "bold" o "" para todas).
func GetSaasApiConfigsBySaasIDs(saasIDs []uint, pasarela string) ([]SaasApiConfig, error) {
if len(saasIDs) == 0 {
return nil, nil
}
db := app.Http.Database.DB.
Preload("SaasProducto").
Where("saas_id IN ? AND activo = ?", saasIDs, true)
if pasarela != "" {
db = db.Where("pasarela = ? OR pasarela = 'ambas'", pasarela)
}
var items []SaasApiConfig
if err := db.Find(&items).Error; err != nil {
return nil, err
}
return items, nil
}
func CreateSaasApiConfig(item *SaasApiConfig) error {
return app.Http.Database.DB.Create(item).Error
}
func UpdateSaasApiConfig(item *SaasApiConfig) error {
return app.Http.Database.DB.Save(item).Error
}
func DeleteSaasApiConfig(id uint) error {
return app.Http.Database.DB.Delete(&SaasApiConfig{}, id).Error
}
// GetSaasApiConfigByWebhookToken busca una integración por su token de webhook entrante.
func GetSaasApiConfigByWebhookToken(token string) (*SaasApiConfig, error) {
var item SaasApiConfig
if err := app.Http.Database.DB.Preload("SaasProducto").
Where("webhook_token = ? AND activo = true", token).
First(&item).Error; err != nil {
return nil, err
}
return &item, nil
}
// SaveSaasWebhookInLog persiste un log de webhook entrante.
func SaveSaasWebhookInLog(entry *SaasWebhookInLog) error {
return app.Http.Database.DB.Create(entry).Error
}
// ─── Queries SaasDispatchLog ──────────────────────────────────────────────────
func SaveSaasDispatchLog(entry *SaasDispatchLog) error {
return app.Http.Database.DB.Create(entry).Error
}
func GetSaasDispatchLogs(limit, offset int) ([]SaasDispatchLog, int64, error) {
var items []SaasDispatchLog
var total int64
db := app.Http.Database.DB.Model(&SaasDispatchLog{}).Preload("SaasApiConfig")
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
if err := db.Order("created_at DESC").Limit(limit).Offset(offset).Find(&items).Error; err != nil {
return nil, 0, err
}
return items, total, nil
}
// GetDispatchLogsByContratoID devuelve los pagos/dispatches de un contrato específico
func GetDispatchLogsByContratoID(contratoID uint) ([]SaasDispatchLog, error) {
var items []SaasDispatchLog
if err := app.Http.Database.DB.
Where("contrato_id = ?", contratoID).
Order("created_at DESC").
Limit(50).
Find(&items).Error; err != nil {
return nil, err
}
return items, nil
}