package models import ( "github.com/sujit-baniya/fiber-boilerplate/app" "gorm.io/gorm" ) // BoldConfig almacena las credenciales de Bold (pasarela de pagos colombiana). // Solo un registro puede estar activo a la vez. type BoldConfig struct { gorm.Model // Claves de producción ApiKeyProd string `json:"api_key_prod" gorm:"column:api_key_prod;type:text"` SecretKeyProd string `json:"secret_key_prod" gorm:"column:secret_key_prod;type:text"` // Claves de prueba / test ApiKeyTest string `json:"api_key_test" gorm:"column:api_key_test;type:text"` // En modo test la secret key es cadena vacía según la doc oficial SecretKeyTest string `json:"secret_key_test" gorm:"column:secret_key_test;type:text"` // Modo activo: "test" | "production" Modo string `json:"modo" gorm:"column:modo;default:'test'"` // URL a la que Bold redirige al usuario tras el pago CallbackUrl string `json:"callback_url" gorm:"column:callback_url;type:text"` // Nota interna Nota string `json:"nota" gorm:"column:nota;type:text"` Activo bool `json:"activo" gorm:"column:activo;default:true"` } func (BoldConfig) TableName() string { return "bold_config" } // GetBoldConfig retorna la configuración activa. func GetBoldConfig() (*BoldConfig, error) { var item BoldConfig if err := app.Http.Database.DB.Where("activo = ?", true).Order("id DESC").First(&item).Error; err != nil { return nil, err } return &item, nil } // SaveBoldConfig desactiva la config previa y guarda la nueva (o actualiza si ya tiene ID). func SaveBoldConfig(s BoldConfig) error { app.Http.Database.DB.Model(&BoldConfig{}). Where("activo = ?", true). Update("activo", false) s.Activo = true if s.ID > 0 { return app.Http.Database.DB.Model(&s).Updates(map[string]interface{}{ "api_key_prod": s.ApiKeyProd, "secret_key_prod": s.SecretKeyProd, "api_key_test": s.ApiKeyTest, "secret_key_test": s.SecretKeyTest, "modo": s.Modo, "callback_url": s.CallbackUrl, "nota": s.Nota, "activo": true, }).Error } return app.Http.Database.DB.Create(&s).Error } // ─── Webhook log para idempotencia ─────────────────────────────────────────── // BoldWebhookLog registra cada notificación recibida de Bold. // La restricción UNIQUE sobre notification_id evita procesar duplicados. type BoldWebhookLog struct { gorm.Model NotificationID string `json:"notification_id" gorm:"column:notification_id;uniqueIndex;type:varchar(64);not null"` Tipo string `json:"tipo" gorm:"column:tipo;type:varchar(30)"` PaymentID string `json:"payment_id" gorm:"column:payment_id;type:varchar(64)"` Referencia string `json:"referencia" gorm:"column:referencia;type:varchar(120)"` PayerEmail string `json:"payer_email" gorm:"column:payer_email;type:varchar(255)"` Monto int64 `json:"monto" gorm:"column:monto"` Procesado bool `json:"procesado" gorm:"column:procesado;default:false"` // Body crudo del webhook para auditoría Raw string `json:"raw" gorm:"column:raw;type:text"` } func (BoldWebhookLog) TableName() string { return "bold_webhook_log" } // IsBoldNotificationDuplicate intenta insertar el log. Devuelve true si ya existía. func IsBoldNotificationDuplicate(notificationID string) bool { result := app.Http.Database.DB. Where("notification_id = ?", notificationID). First(&BoldWebhookLog{}) return result.Error == nil // nil error = registro encontrado = duplicado } // SaveBoldWebhookLog guarda el log del webhook. func SaveBoldWebhookLog(entry BoldWebhookLog) error { return app.Http.Database.DB.Create(&entry).Error } // MarkBoldWebhookProcessed marca la notificación como procesada. func MarkBoldWebhookProcessed(notificationID string) { app.Http.Database.DB.Model(&BoldWebhookLog{}). Where("notification_id = ?", notificationID). Update("procesado", true) } // GetBoldWebhookLogs devuelve los últimos N registros del log. func GetBoldWebhookLogs(limit int) ([]BoldWebhookLog, error) { var logs []BoldWebhookLog if err := app.Http.Database.DB.Order("id DESC").Limit(limit).Find(&logs).Error; err != nil { return nil, err } return logs, nil }