feat: selector de pasarela (Bold/dLocal/ninguna) en regla + muestra {{.EnlacePago}} en editor de plantillas
This commit is contained in:
@@ -7,12 +7,13 @@ import (
|
||||
|
||||
type NotificacionRegla struct {
|
||||
gorm.Model
|
||||
Nombre string `json:"nombre" gorm:"column:nombre"`
|
||||
DiasAntes int `json:"dias_antes" gorm:"column:dias_antes"`
|
||||
PlantillaID uint `json:"plantilla_id" gorm:"column:plantilla_id"`
|
||||
Plantilla PlantillaCorreo `json:"plantilla" gorm:"foreignKey:PlantillaID"`
|
||||
Activo bool `json:"activo" gorm:"column:activo;default:true"`
|
||||
AplicaA string `json:"aplica_a" gorm:"column:aplica_a;default:'todos'"` // todos | renovable | unico
|
||||
Nombre string `json:"nombre" gorm:"column:nombre"`
|
||||
DiasAntes int `json:"dias_antes" gorm:"column:dias_antes"`
|
||||
PlantillaID uint `json:"plantilla_id" gorm:"column:plantilla_id"`
|
||||
Plantilla PlantillaCorreo `json:"plantilla" gorm:"foreignKey:PlantillaID"`
|
||||
Activo bool `json:"activo" gorm:"column:activo;default:true"`
|
||||
AplicaA string `json:"aplica_a" gorm:"column:aplica_a;default:'todos'"` // todos | renovable | unico
|
||||
PasarelaEnlace string `json:"pasarela_enlace" gorm:"column:pasarela_enlace;default:'bold'"` // bold | dlocal | ninguna
|
||||
}
|
||||
|
||||
func (NotificacionRegla) TableName() string { return "notificacion_reglas" }
|
||||
@@ -48,11 +49,12 @@ func CreateRegla(r NotificacionRegla) error {
|
||||
|
||||
func UpdateRegla(r NotificacionRegla) error {
|
||||
return app.Http.Database.DB.Model(&r).Updates(map[string]interface{}{
|
||||
"nombre": r.Nombre,
|
||||
"dias_antes": r.DiasAntes,
|
||||
"plantilla_id": r.PlantillaID,
|
||||
"activo": r.Activo,
|
||||
"aplica_a": r.AplicaA,
|
||||
"nombre": r.Nombre,
|
||||
"dias_antes": r.DiasAntes,
|
||||
"plantilla_id": r.PlantillaID,
|
||||
"activo": r.Activo,
|
||||
"aplica_a": r.AplicaA,
|
||||
"pasarela_enlace": r.PasarelaEnlace,
|
||||
}).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ func EnviarNotificacionGrupo(regla *models.NotificacionRegla, cliente *models.Cl
|
||||
// Generar o reutilizar el enlace de pago para el primer contrato del grupo
|
||||
enlacePago := ""
|
||||
if len(contratos) > 0 {
|
||||
enlacePago = ObtenerOCrearEnlacePago(&contratos[0])
|
||||
enlacePago = ObtenerOCrearEnlacePago(&contratos[0], regla.PasarelaEnlace)
|
||||
}
|
||||
|
||||
datos := DatosPlantilla{
|
||||
@@ -221,24 +221,40 @@ func ReenviarLog(logEntry *models.NotificacionLog) error {
|
||||
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 {
|
||||
// ObtenerOCrearEnlacePago devuelve el enlace de pago vigente del contrato.
|
||||
// gateway puede ser "bold", "dlocal" o "ninguna"/"".
|
||||
// Si ya existe un enlace guardado, lo reutiliza (mismo ciclo).
|
||||
// Cuando el pago se aprueba, llamar models.LimpiarEnlacePago para el siguiente ciclo.
|
||||
func ObtenerOCrearEnlacePago(contrato *models.Contrato, gateway string) string {
|
||||
// Normalizar gateway vacío → bold por defecto
|
||||
if gateway == "" {
|
||||
gateway = "bold"
|
||||
}
|
||||
if gateway == "ninguna" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Reutilizar si ya hay uno vigente
|
||||
if contrato.EnlacePago != "" {
|
||||
return contrato.EnlacePago
|
||||
}
|
||||
|
||||
switch gateway {
|
||||
case "dlocal":
|
||||
return obtenerEnlaceDlocal(contrato)
|
||||
default: // "bold"
|
||||
return obtenerEnlaceBold(contrato)
|
||||
}
|
||||
}
|
||||
|
||||
// obtenerEnlaceBold crea un payment link en Bold y lo persiste en el contrato.
|
||||
func obtenerEnlaceBold(contrato *models.Contrato) string {
|
||||
cfg, err := models.GetBoldConfig()
|
||||
if err != nil {
|
||||
log.Printf("[BOLD] ObtenerOCrearEnlacePago: sin config activa para contrato %d", contrato.ID)
|
||||
log.Printf("[BOLD] 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)
|
||||
@@ -257,13 +273,55 @@ func ObtenerOCrearEnlacePago(contrato *models.Contrato) string {
|
||||
|
||||
result, err := CreateBoldPaymentLink(cfg, req)
|
||||
if err != nil {
|
||||
log.Printf("[BOLD] ObtenerOCrearEnlacePago: error creando link para contrato %d: %v", contrato.ID, err)
|
||||
log.Printf("[BOLD] 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
|
||||
}
|
||||
|
||||
// obtenerEnlaceDlocal crea un pago en dLocal y devuelve la URL de redirección.
|
||||
func obtenerEnlaceDlocal(contrato *models.Contrato) string {
|
||||
cfg, err := models.GetLastActiveDlocalApi()
|
||||
if err != nil {
|
||||
log.Printf("[DLOCAL] sin config activa para contrato %d", contrato.ID)
|
||||
return ""
|
||||
}
|
||||
|
||||
desc := fmt.Sprintf("Renovación contrato #%d", contrato.ID)
|
||||
if len(contrato.Servicios) > 0 {
|
||||
desc = fmt.Sprintf("Renovación: %s", contrato.Servicios[0].Nombre)
|
||||
}
|
||||
|
||||
pagoReq := PagoRequest{
|
||||
Currency: "COP",
|
||||
Amount: contrato.PrecioAcordado,
|
||||
OrderID: int(contrato.ID),
|
||||
Description: desc,
|
||||
SuccessURL: cfg.UrlProd + "/pago-exitoso",
|
||||
BackURL: cfg.UrlProd + "/pago-cancelado",
|
||||
}
|
||||
|
||||
body, err := CreatePago(*cfg, pagoReq)
|
||||
if err != nil {
|
||||
log.Printf("[DLOCAL] error creando pago para contrato %d: %v", contrato.ID, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
RedirectURL string `json:"redirect_url"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &resp); err != nil || resp.RedirectURL == "" {
|
||||
log.Printf("[DLOCAL] respuesta sin redirect_url para contrato %d: %s", contrato.ID, string(body))
|
||||
return ""
|
||||
}
|
||||
|
||||
_ = models.GuardarEnlacePago(contrato.ID, resp.ID, resp.RedirectURL)
|
||||
contrato.EnlacePago = resp.RedirectURL
|
||||
contrato.EnlacePagoLinkID = resp.ID
|
||||
return resp.RedirectURL
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user