up
This commit is contained in:
@@ -20,6 +20,7 @@ type NotificacionLog struct {
|
|||||||
ErrorMsg string `json:"error_msg" gorm:"column:error_msg;type:text"`
|
ErrorMsg string `json:"error_msg" gorm:"column:error_msg;type:text"`
|
||||||
Asunto string `json:"asunto" gorm:"column:asunto"`
|
Asunto string `json:"asunto" gorm:"column:asunto"`
|
||||||
PreviewHTML string `json:"preview_html" gorm:"column:preview_html;type:text"`
|
PreviewHTML string `json:"preview_html" gorm:"column:preview_html;type:text"`
|
||||||
|
Pasarela string `json:"pasarela" gorm:"column:pasarela;type:varchar(20)"` // bold | dlocal | (vacío para correos de aviso)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (NotificacionLog) TableName() string { return "notificaciones_log" }
|
func (NotificacionLog) TableName() string { return "notificaciones_log" }
|
||||||
@@ -75,6 +76,7 @@ func UpdateNotificacionLog(n NotificacionLog) error {
|
|||||||
return app.Http.Database.DB.Model(&n).Updates(map[string]interface{}{
|
return app.Http.Database.DB.Model(&n).Updates(map[string]interface{}{
|
||||||
"estado": n.Estado,
|
"estado": n.Estado,
|
||||||
"error_msg": n.ErrorMsg,
|
"error_msg": n.ErrorMsg,
|
||||||
|
"pasarela": n.Pasarela,
|
||||||
}).Error
|
}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -325,6 +325,7 @@ func TestCicloNotificaciones(t *testing.T) {
|
|||||||
®laBienvenida,
|
®laBienvenida,
|
||||||
&cliente,
|
&cliente,
|
||||||
[]models.Contrato{contrato},
|
[]models.Contrato{contrato},
|
||||||
|
"",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("EnviarNotificacionGrupo (bienvenida): %v", err)
|
t.Fatalf("EnviarNotificacionGrupo (bienvenida): %v", err)
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ func procesarContratos(regla *models.NotificacionRegla, contratos []models.Contr
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cliente := &grupoContratos[0].Cliente
|
cliente := &grupoContratos[0].Cliente
|
||||||
if err := EnviarNotificacionGrupo(regla, cliente, grupoContratos); err != nil {
|
if err := EnviarNotificacionGrupo(regla, cliente, grupoContratos, ""); err != nil {
|
||||||
log.Printf("[CRON] Error enviando a cliente %d: %v", clienteID, err)
|
log.Printf("[CRON] Error enviando a cliente %d: %v", clienteID, err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[CRON] Enviado a cliente %d (%s) — %d contrato(s)", clienteID, cliente.Email, len(grupoContratos))
|
log.Printf("[CRON] Enviado a cliente %d (%s) — %d contrato(s)", clienteID, cliente.Email, len(grupoContratos))
|
||||||
|
|||||||
@@ -163,8 +163,9 @@ func EnviarCorreoManual(contrato *models.Contrato) error {
|
|||||||
return sendErr
|
return sendErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnviarNotificacionGrupo envía un correo agrupado para un cliente con múltiples contratos
|
// EnviarNotificacionGrupo envía un correo agrupado para un cliente con múltiples contratos.
|
||||||
func EnviarNotificacionGrupo(regla *models.NotificacionRegla, cliente *models.Cliente, contratos []models.Contrato) error {
|
// pasarela indica la pasarela de pago que originó el evento (bold | dlocal | "").
|
||||||
|
func EnviarNotificacionGrupo(regla *models.NotificacionRegla, cliente *models.Cliente, contratos []models.Contrato, pasarela string) error {
|
||||||
p, err := models.GetPlantillaByID(regla.PlantillaID)
|
p, err := models.GetPlantillaByID(regla.PlantillaID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("plantilla no encontrada: %w", err)
|
return fmt.Errorf("plantilla no encontrada: %w", err)
|
||||||
@@ -230,6 +231,7 @@ func EnviarNotificacionGrupo(regla *models.NotificacionRegla, cliente *models.Cl
|
|||||||
Estado: "pendiente",
|
Estado: "pendiente",
|
||||||
Asunto: p.Asunto,
|
Asunto: p.Asunto,
|
||||||
PreviewHTML: html,
|
PreviewHTML: html,
|
||||||
|
Pasarela: pasarela,
|
||||||
}
|
}
|
||||||
savedLog, err := models.CreateNotificacionLog(logEntry)
|
savedLog, err := models.CreateNotificacionLog(logEntry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -276,7 +278,14 @@ func ReenviarLog(logEntry *models.NotificacionLog) error {
|
|||||||
// EnviarCorreoConfirmacionPago envía el correo de "pago recibido" para un contrato.
|
// EnviarCorreoConfirmacionPago envía el correo de "pago recibido" para un contrato.
|
||||||
// Usa la regla/plantilla con tipo_evento = 'pago_recibido'. Si no existe, no hace nada.
|
// Usa la regla/plantilla con tipo_evento = 'pago_recibido'. Si no existe, no hace nada.
|
||||||
// Es idempotente: no reenvía si ya se envió hoy (YaEnviadoHoy por regla+cliente).
|
// Es idempotente: no reenvía si ya se envió hoy (YaEnviadoHoy por regla+cliente).
|
||||||
func EnviarCorreoConfirmacionPago(contratoID uint) {
|
// EnviarCorreoConfirmacionPago envía el correo de "pago recibido" para un contrato.
|
||||||
|
// pasarela (opcional): "bold", "dlocal", etc. Se guarda en el historial del cliente.
|
||||||
|
func EnviarCorreoConfirmacionPago(contratoID uint, pasarela ...string) {
|
||||||
|
pas := ""
|
||||||
|
if len(pasarela) > 0 {
|
||||||
|
pas = pasarela[0]
|
||||||
|
}
|
||||||
|
|
||||||
contrato, err := models.GetContratoByID(contratoID)
|
contrato, err := models.GetContratoByID(contratoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[PAGO-CORREO] contrato %d no encontrado: %v", contratoID, err)
|
log.Printf("[PAGO-CORREO] contrato %d no encontrado: %v", contratoID, err)
|
||||||
@@ -296,10 +305,10 @@ func EnviarCorreoConfirmacionPago(contratoID uint) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := EnviarNotificacionGrupo(regla, &contrato.Cliente, []models.Contrato{*contrato}); err != nil {
|
if err := EnviarNotificacionGrupo(regla, &contrato.Cliente, []models.Contrato{*contrato}, pas); err != nil {
|
||||||
log.Printf("[PAGO-CORREO] error enviando correo de confirmación para contrato %d: %v", contratoID, err)
|
log.Printf("[PAGO-CORREO] error enviando correo de confirmación para contrato %d: %v", contratoID, err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[PAGO-CORREO] correo de confirmación enviado para contrato %d → %s", contratoID, contrato.Cliente.Email)
|
log.Printf("[PAGO-CORREO] correo de confirmación enviado para contrato %d → %s (pasarela: %s)", contratoID, contrato.Cliente.Email, pas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ func BoldWebhook(c *fiber.Ctx) error {
|
|||||||
log.Printf("[BOLD] Webhook: contrato %d marcado como pagado", contratoID)
|
log.Printf("[BOLD] Webhook: contrato %d marcado como pagado", contratoID)
|
||||||
// Notificar SaaS externos + enviar correo de confirmación (goroutine, no bloquea respuesta)
|
// Notificar SaaS externos + enviar correo de confirmación (goroutine, no bloquea respuesta)
|
||||||
go services.DispatchSaasPaymentNotification(contratoID, payerEmail, "bold", float64(monto), "COP")
|
go services.DispatchSaasPaymentNotification(contratoID, payerEmail, "bold", float64(monto), "COP")
|
||||||
go services.EnviarCorreoConfirmacionPago(contratoID)
|
go services.EnviarCorreoConfirmacionPago(contratoID, "bold")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ func DlocalWebhook(c *fiber.Ctx) error {
|
|||||||
log.Printf("[DLOCAL] Webhook: contrato %d marcado como pagado", contratoID)
|
log.Printf("[DLOCAL] Webhook: contrato %d marcado como pagado", contratoID)
|
||||||
// Notificar SaaS externos + enviar correo de confirmación (goroutine, no bloquea respuesta)
|
// Notificar SaaS externos + enviar correo de confirmación (goroutine, no bloquea respuesta)
|
||||||
go services.DispatchSaasPaymentNotification(contratoID, payerEmail, "dlocal", monto, moneda)
|
go services.DispatchSaasPaymentNotification(contratoID, payerEmail, "dlocal", monto, moneda)
|
||||||
go services.EnviarCorreoConfirmacionPago(contratoID)
|
go services.EnviarCorreoConfirmacionPago(contratoID, "dlocal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func PagoExitosoPage(c *fiber.Ctx) error {
|
|||||||
var contratoID uint
|
var contratoID uint
|
||||||
if _, err := fmt.Sscanf(ref, "contrato-%d", &contratoID); err == nil && contratoID > 0 {
|
if _, err := fmt.Sscanf(ref, "contrato-%d", &contratoID); err == nil && contratoID > 0 {
|
||||||
_ = models.MarcarContratoPagado(contratoID)
|
_ = models.MarcarContratoPagado(contratoID)
|
||||||
go services.EnviarCorreoConfirmacionPago(contratoID)
|
go services.EnviarCorreoConfirmacionPago(contratoID, "bold")
|
||||||
}
|
}
|
||||||
estado, fechaPago := verificarPago(ref)
|
estado, fechaPago := verificarPago(ref)
|
||||||
return c.Render("pago_exitoso", fiber.Map{
|
return c.Render("pago_exitoso", fiber.Map{
|
||||||
@@ -136,7 +136,7 @@ func verificarPago(ref string) (confirmado bool, fechaPago string) {
|
|||||||
if l.Estado == "PAID" || l.Estado == "AUTHORIZED" {
|
if l.Estado == "PAID" || l.Estado == "AUTHORIZED" {
|
||||||
log.Printf("[PAGO-ESTADO] Contrato %d confirmado via dlocal_payment_log (id=%d)", contratoID, l.ID)
|
log.Printf("[PAGO-ESTADO] Contrato %d confirmado via dlocal_payment_log (id=%d)", contratoID, l.ID)
|
||||||
_ = models.MarcarContratoPagado(contratoID)
|
_ = models.MarcarContratoPagado(contratoID)
|
||||||
go services.EnviarCorreoConfirmacionPago(contratoID)
|
go services.EnviarCorreoConfirmacionPago(contratoID, "dlocal")
|
||||||
return true, l.CreatedAt.Format("02/01/2006 15:04")
|
return true, l.CreatedAt.Format("02/01/2006 15:04")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,7 +152,7 @@ func verificarPago(ref string) (confirmado bool, fechaPago string) {
|
|||||||
} else if paid {
|
} else if paid {
|
||||||
log.Printf("[PAGO-ESTADO] Contrato %d confirmado via Bold API (payment_id=%s)", contratoID, paymentID)
|
log.Printf("[PAGO-ESTADO] Contrato %d confirmado via Bold API (payment_id=%s)", contratoID, paymentID)
|
||||||
_ = models.MarcarContratoPagado(contratoID)
|
_ = models.MarcarContratoPagado(contratoID)
|
||||||
go services.EnviarCorreoConfirmacionPago(contratoID)
|
go services.EnviarCorreoConfirmacionPago(contratoID, "bold")
|
||||||
if paymentID != "" && !models.IsBoldNotificationDuplicate("api-check-"+paymentID) {
|
if paymentID != "" && !models.IsBoldNotificationDuplicate("api-check-"+paymentID) {
|
||||||
_ = models.SaveBoldWebhookLog(models.BoldWebhookLog{
|
_ = models.SaveBoldWebhookLog(models.BoldWebhookLog{
|
||||||
NotificationID: "api-check-" + paymentID,
|
NotificationID: "api-check-" + paymentID,
|
||||||
@@ -178,7 +178,7 @@ func verificarPago(ref string) (confirmado bool, fechaPago string) {
|
|||||||
} else if paid {
|
} else if paid {
|
||||||
log.Printf("[PAGO-ESTADO] Contrato %d confirmado via dLocal API (payment_id=%s)", contratoID, paymentID)
|
log.Printf("[PAGO-ESTADO] Contrato %d confirmado via dLocal API (payment_id=%s)", contratoID, paymentID)
|
||||||
_ = models.MarcarContratoPagado(contratoID)
|
_ = models.MarcarContratoPagado(contratoID)
|
||||||
go services.EnviarCorreoConfirmacionPago(contratoID)
|
go services.EnviarCorreoConfirmacionPago(contratoID, "dlocal")
|
||||||
notifID := "api-check-" + ref
|
notifID := "api-check-" + ref
|
||||||
if !models.IsDlocalNotificationDuplicate(notifID) {
|
if !models.IsDlocalNotificationDuplicate(notifID) {
|
||||||
_ = models.SaveDlocalPaymentLog(models.DlocalPaymentLog{
|
_ = models.SaveDlocalPaymentLog(models.DlocalPaymentLog{
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ func EnviarCorreoContrato(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(404).JSON(fiber.Map{"error": "Regla no encontrada"})
|
return c.Status(404).JSON(fiber.Map{"error": "Regla no encontrada"})
|
||||||
}
|
}
|
||||||
if err := services.EnviarNotificacionGrupo(regla, &contrato.Cliente, []models.Contrato{*contrato}); err != nil {
|
if err := services.EnviarNotificacionGrupo(regla, &contrato.Cliente, []models.Contrato{*contrato}, ""); err != nil {
|
||||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||||||
}
|
}
|
||||||
return c.JSON(fiber.Map{"message": "Correo enviado", "ok": true})
|
return c.JSON(fiber.Map{"message": "Correo enviado", "ok": true})
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ func EjecutarTestCiclo(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
// ── PASO 1: Enviar correo de bienvenida ───────────────────────────────────
|
// ── PASO 1: Enviar correo de bienvenida ───────────────────────────────────
|
||||||
paso("paso 1 — envío de bienvenida", func() error {
|
paso("paso 1 — envío de bienvenida", func() error {
|
||||||
return services.EnviarNotificacionGrupo(®laBienvenida, &cliente, []models.Contrato{contrato})
|
return services.EnviarNotificacionGrupo(®laBienvenida, &cliente, []models.Contrato{contrato}, "")
|
||||||
})
|
})
|
||||||
|
|
||||||
// Verificar log en DB
|
// Verificar log en DB
|
||||||
@@ -203,7 +203,7 @@ func EjecutarTestCiclo(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return services.EnviarNotificacionGrupo(®laPago, &cliente, []models.Contrato{*reloadContrato})
|
return services.EnviarNotificacionGrupo(®laPago, &cliente, []models.Contrato{*reloadContrato}, "")
|
||||||
})
|
})
|
||||||
|
|
||||||
paso("paso 2 — verificar log de pago en historial", func() error {
|
paso("paso 2 — verificar log de pago en historial", func() error {
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ func ValidarDlocalLog(c *fiber.Ctx) error {
|
|||||||
// Si el pago se confirmó ahora (no estaba marcado antes), actualizar contrato
|
// Si el pago se confirmó ahora (no estaba marcado antes), actualizar contrato
|
||||||
if pagoConfirmado && !contrato.PagoConfirmado {
|
if pagoConfirmado && !contrato.PagoConfirmado {
|
||||||
_ = models.MarcarContratoPagado(contratoID)
|
_ = models.MarcarContratoPagado(contratoID)
|
||||||
go services.EnviarCorreoConfirmacionPago(contratoID)
|
go services.EnviarCorreoConfirmacionPago(contratoID, fuenteConfirmacion)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = models.UpdateDlocalPaymentLogDatos(uint(logID), payerEmail, monto)
|
_ = models.UpdateDlocalPaymentLogDatos(uint(logID), payerEmail, monto)
|
||||||
@@ -385,7 +385,7 @@ func ValidarBoldCallback(c *fiber.Ctx) error {
|
|||||||
// Actualizar contrato si se confirmó ahora
|
// Actualizar contrato si se confirmó ahora
|
||||||
if pagoConfirmado && !contrato.PagoConfirmado {
|
if pagoConfirmado && !contrato.PagoConfirmado {
|
||||||
_ = models.MarcarContratoPagado(contratoID)
|
_ = models.MarcarContratoPagado(contratoID)
|
||||||
go services.EnviarCorreoConfirmacionPago(contratoID)
|
go services.EnviarCorreoConfirmacionPago(contratoID, fuente)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar estado del callback
|
// Actualizar estado del callback
|
||||||
@@ -514,7 +514,7 @@ func ValidarBoldLog(c *fiber.Ctx) error {
|
|||||||
// Si el pago se confirmó ahora (no estaba marcado antes), actualizar contrato
|
// Si el pago se confirmó ahora (no estaba marcado antes), actualizar contrato
|
||||||
if pagoConfirmado && !contrato.PagoConfirmado {
|
if pagoConfirmado && !contrato.PagoConfirmado {
|
||||||
_ = models.MarcarContratoPagado(contratoID)
|
_ = models.MarcarContratoPagado(contratoID)
|
||||||
go services.EnviarCorreoConfirmacionPago(contratoID)
|
go services.EnviarCorreoConfirmacionPago(contratoID, fuenteConfirmacion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar log con los mejores datos disponibles
|
// Actualizar log con los mejores datos disponibles
|
||||||
|
|||||||
Reference in New Issue
Block a user