178 lines
7.2 KiB
Go
178 lines
7.2 KiB
Go
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
|
|
}
|
|
|
|
// GetBoldWebhookLogsPaginated devuelve los logs con paginación y filtro por tipo.
|
|
func GetBoldWebhookLogsPaginated(page, limit int, tipo string) ([]BoldWebhookLog, int64, error) {
|
|
var logs []BoldWebhookLog
|
|
var total int64
|
|
db := app.Http.Database.DB.Model(&BoldWebhookLog{})
|
|
if tipo != "" && tipo != "TODOS" {
|
|
db = db.Where("tipo = ?", tipo)
|
|
}
|
|
db.Count(&total)
|
|
offset := (page - 1) * limit
|
|
if err := db.Order("id DESC").Limit(limit).Offset(offset).Find(&logs).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return logs, total, nil
|
|
}
|
|
|
|
// ─── Callback log (intentos de pago) ─────────────────────────────────────────
|
|
|
|
// BoldCallbackLog registra cada visita a la URL de retorno de Bold.
|
|
// Esto captura usuarios que iniciaron el proceso de pago (llegaron al checkout)
|
|
// pero pueden haber abandonado, fallado o completado el pago.
|
|
type BoldCallbackLog struct {
|
|
gorm.Model
|
|
// Referencia principal recibida de Bold (bold-order-id / payment_link / reference)
|
|
Referencia string `json:"referencia" gorm:"column:referencia;type:varchar(120);index"`
|
|
PaymentLink string `json:"payment_link" gorm:"column:payment_link;type:varchar(80)"`
|
|
// Todos los parámetros GET recibidos en JSON (para depuración)
|
|
Params string `json:"params" gorm:"column:params;type:text"`
|
|
// Estado: pendiente | pagado | fallido | revertido
|
|
Estado string `json:"estado" gorm:"column:estado;type:varchar(20);default:'pendiente'"`
|
|
// Datos del cliente si están disponibles
|
|
PayerEmail string `json:"payer_email" gorm:"column:payer_email;type:varchar(255)"`
|
|
// Datos de red (para análisis)
|
|
IP string `json:"ip" gorm:"column:ip;type:varchar(45)"`
|
|
UserAgent string `json:"user_agent" gorm:"column:user_agent;type:text"`
|
|
}
|
|
|
|
func (BoldCallbackLog) TableName() string { return "bold_callback_log" }
|
|
|
|
// SaveBoldCallbackLog guarda un registro de intento de pago.
|
|
func SaveBoldCallbackLog(entry BoldCallbackLog) error {
|
|
return app.Http.Database.DB.Create(&entry).Error
|
|
}
|
|
|
|
// UpdateBoldCallbackEstado actualiza el estado de los intentos que coincidan con la referencia.
|
|
// Se llama desde el webhook cuando llega un evento SALE_APPROVED, SALE_REJECTED, etc.
|
|
func UpdateBoldCallbackEstado(referencia, estado string) {
|
|
if referencia == "" {
|
|
return
|
|
}
|
|
app.Http.Database.DB.Model(&BoldCallbackLog{}).
|
|
Where("referencia = ? AND estado = 'pendiente'", referencia).
|
|
Update("estado", estado)
|
|
}
|
|
|
|
// GetBoldCallbackLogsPaginated devuelve los intentos de pago con paginación y filtro.
|
|
func GetBoldCallbackLogsPaginated(page, limit int, estado string) ([]BoldCallbackLog, int64, error) {
|
|
var logs []BoldCallbackLog
|
|
var total int64
|
|
db := app.Http.Database.DB.Model(&BoldCallbackLog{})
|
|
if estado != "" && estado != "TODOS" {
|
|
db = db.Where("estado = ?", estado)
|
|
}
|
|
db.Count(&total)
|
|
offset := (page - 1) * limit
|
|
if err := db.Order("id DESC").Limit(limit).Offset(offset).Find(&logs).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return logs, total, nil
|
|
}
|