This commit is contained in:
Lizandro Guarnizo
2026-05-01 12:02:21 -05:00
parent 240559e867
commit 5e0b6ae2f0
5 changed files with 103 additions and 6 deletions
+22
View File
@@ -19,6 +19,11 @@ type Contrato struct {
Estado string `json:"estado" gorm:"column:estado;default:'activo'"` // activo | vencido | cancelado | renovado
AutoRenovar bool `json:"auto_renovar" gorm:"column:auto_renovar;default:false"`
Notas string `json:"notas" gorm:"column:notas"`
// Enlace de pago Bold: único por ciclo de pago.
// Se reutiliza en múltiples notificaciones del mismo ciclo.
// Se anula (vacía) cuando SALE_APPROVED llega y se registra el pago.
EnlacePago string `json:"enlace_pago" gorm:"column:enlace_pago;type:text"`
EnlacePagoLinkID string `json:"enlace_pago_link_id" gorm:"column:enlace_pago_link_id;type:varchar(64)"`
}
func (Contrato) TableName() string { return "contratos" }
@@ -134,3 +139,20 @@ func DeleteContrato(id uint) error {
db.Exec("DELETE FROM contrato_servicios WHERE contrato_id = ?", id)
return db.Delete(&c).Error
}
// GuardarEnlacePago persiste el enlace de pago Bold en el contrato.
func GuardarEnlacePago(contratoID uint, linkID, url string) error {
return app.Http.Database.DB.Model(&Contrato{}).Where("id = ?", contratoID).Updates(map[string]interface{}{
"enlace_pago": url,
"enlace_pago_link_id": linkID,
}).Error
}
// LimpiarEnlacePago borra el enlace vigente cuando se registra un pago aprobado,
// para que la próxima notificación genere un link nuevo.
func LimpiarEnlacePago(contratoID uint) error {
return app.Http.Database.DB.Model(&Contrato{}).Where("id = ?", contratoID).Updates(map[string]interface{}{
"enlace_pago": "",
"enlace_pago_link_id": "",
}).Error
}
+59
View File
@@ -22,6 +22,9 @@ type DatosPlantilla struct {
FechaVencimiento string
DiasRestantes int
Asunto string
// EnlacePago es la URL de pago Bold para el ciclo actual.
// Mismo link en todas las notificaciones hasta que el pago sea aprobado.
EnlacePago string
}
type ItemServicio struct {
@@ -45,6 +48,7 @@ func DatosEjemplo() DatosPlantilla {
FechaVencimiento: "31/12/2025",
DiasRestantes: 15,
Asunto: "Recordatorio de vencimiento",
EnlacePago: "https://checkout.bold.co/payment/LNK_ejemplo123",
}
}
@@ -134,6 +138,13 @@ func EnviarNotificacionGrupo(regla *models.NotificacionRegla, cliente *models.Cl
}
dias := int(fechaVenc.Sub(time.Now()).Hours() / 24)
// Generar o reutilizar el enlace de pago para el primer contrato del grupo
enlacePago := ""
if len(contratos) > 0 {
enlacePago = ObtenerOCrearEnlacePago(&contratos[0])
}
datos := DatosPlantilla{
ClienteNombre: cliente.Nombre,
ClienteEmpresa: cliente.Empresa,
@@ -143,6 +154,7 @@ func EnviarNotificacionGrupo(regla *models.NotificacionRegla, cliente *models.Cl
FechaVencimiento: fechaVenc.Format("02/01/2006"),
DiasRestantes: dias,
Asunto: p.Asunto,
EnlacePago: enlacePago,
}
html, err := RenderPlantilla(p, datos)
@@ -208,3 +220,50 @@ func ReenviarLog(logEntry *models.NotificacionLog) error {
models.UpdateNotificacionLog(*logEntry)
return sendErr
}
// ObtenerOCrearEnlacePago devuelve el enlace Bold vigente del contrato.
// Si ya existe uno (no ha sido pagado aún), lo reutiliza.
// Si no existe, crea uno nuevo en Bold y lo guarda en el contrato.
// Cuando Bold notifica SALE_APPROVED, llamar models.LimpiarEnlacePago(contratoID)
// para que la próxima notificación genere un link nuevo.
func ObtenerOCrearEnlacePago(contrato *models.Contrato) string {
// Reutilizar si ya hay uno vigente
if contrato.EnlacePago != "" {
return contrato.EnlacePago
}
cfg, err := models.GetBoldConfig()
if err != nil {
log.Printf("[BOLD] ObtenerOCrearEnlacePago: sin config activa para contrato %d", contrato.ID)
return ""
}
// Nombre de servicios en la descripción
desc := fmt.Sprintf("Renovación contrato #%d", contrato.ID)
if len(contrato.Servicios) > 0 {
desc = fmt.Sprintf("Renovación: %s", contrato.Servicios[0].Nombre)
}
req := BoldPaymentLinkRequest{
AmountType: "CLOSE",
Amount: BoldAmountField{
Currency: "COP",
TotalAmount: int64(contrato.PrecioAcordado),
},
Description: desc,
Reference: fmt.Sprintf("contrato-%d", contrato.ID),
CallbackURL: cfg.CallbackUrl,
}
result, err := CreateBoldPaymentLink(cfg, req)
if err != nil {
log.Printf("[BOLD] ObtenerOCrearEnlacePago: error creando link para contrato %d: %v", contrato.ID, err)
return ""
}
// Persistir en BD
_ = models.GuardarEnlacePago(contrato.ID, result.Payload.PaymentLink, result.Payload.URL)
contrato.EnlacePago = result.Payload.URL
contrato.EnlacePagoLinkID = result.Payload.PaymentLink
return result.Payload.URL
}