up
This commit is contained in:
@@ -348,8 +348,8 @@ func DlocalRegistrarPago(c *fiber.Ctx) error {
|
||||
PayerEmail string `json:"payer_email"`
|
||||
Monto float64 `json:"monto"`
|
||||
Moneda string `json:"moneda"`
|
||||
Estado string `json:"estado"` // PAID, PENDING, REJECTED, …
|
||||
Tipo string `json:"tipo"` // PAYMENT, SUBSCRIPTION_CHARGE, manual, …
|
||||
Estado string `json:"estado"` // PAID, PENDING, REJECTED, …
|
||||
Tipo string `json:"tipo"` // PAYMENT, SUBSCRIPTION_CHARGE, manual, …
|
||||
Nota string `json:"nota"`
|
||||
}
|
||||
var b body
|
||||
|
||||
@@ -244,34 +244,34 @@ func GetHistorialContrato(c *fiber.Ctx) error {
|
||||
|
||||
// Construir timeline unificado
|
||||
type Evento struct {
|
||||
Tipo string `json:"tipo"` // creacion | renovacion | notificacion | pago
|
||||
Icono string `json:"icono"`
|
||||
Titulo string `json:"titulo"`
|
||||
Detalle string `json:"detalle"`
|
||||
Estado string `json:"estado"` // ok | error | info
|
||||
FechaISO string `json:"fecha"`
|
||||
Tipo string `json:"tipo"` // creacion | renovacion | notificacion | pago
|
||||
Icono string `json:"icono"`
|
||||
Titulo string `json:"titulo"`
|
||||
Detalle string `json:"detalle"`
|
||||
Estado string `json:"estado"` // ok | error | info
|
||||
FechaISO string `json:"fecha"`
|
||||
}
|
||||
|
||||
var timeline []Evento
|
||||
|
||||
// Evento: creación del contrato
|
||||
timeline = append(timeline, Evento{
|
||||
Tipo: "creacion",
|
||||
Icono: "document",
|
||||
Titulo: "Contrato creado",
|
||||
Detalle: "Inicio: " + contrato.FechaInicio.Format("02/01/2006") + " · Vence: " + contrato.FechaVencimiento.Format("02/01/2006"),
|
||||
Estado: "info",
|
||||
Tipo: "creacion",
|
||||
Icono: "document",
|
||||
Titulo: "Contrato creado",
|
||||
Detalle: "Inicio: " + contrato.FechaInicio.Format("02/01/2006") + " · Vence: " + contrato.FechaVencimiento.Format("02/01/2006"),
|
||||
Estado: "info",
|
||||
FechaISO: contrato.CreatedAt.Format(time.RFC3339),
|
||||
})
|
||||
|
||||
// Evento: renovaciones (updated_at con estado renovado — heurístico por estado)
|
||||
if contrato.Estado == "renovado" {
|
||||
timeline = append(timeline, Evento{
|
||||
Tipo: "renovacion",
|
||||
Icono: "refresh",
|
||||
Titulo: "Contrato renovado",
|
||||
Detalle: "Nuevo vencimiento: " + contrato.FechaVencimiento.Format("02/01/2006"),
|
||||
Estado: "ok",
|
||||
Tipo: "renovacion",
|
||||
Icono: "refresh",
|
||||
Titulo: "Contrato renovado",
|
||||
Detalle: "Nuevo vencimiento: " + contrato.FechaVencimiento.Format("02/01/2006"),
|
||||
Estado: "ok",
|
||||
FechaISO: contrato.UpdatedAt.Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
@@ -293,11 +293,11 @@ func GetHistorialContrato(c *fiber.Ctx) error {
|
||||
reglaLabel = " (" + n.Regla.Nombre + ")"
|
||||
}
|
||||
timeline = append(timeline, Evento{
|
||||
Tipo: "notificacion",
|
||||
Icono: "mail",
|
||||
Titulo: "Notificación enviada" + reglaLabel,
|
||||
Detalle: detalle,
|
||||
Estado: estado,
|
||||
Tipo: "notificacion",
|
||||
Icono: "mail",
|
||||
Titulo: "Notificación enviada" + reglaLabel,
|
||||
Detalle: detalle,
|
||||
Estado: estado,
|
||||
FechaISO: n.CreatedAt.Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
@@ -316,11 +316,11 @@ func GetHistorialContrato(c *fiber.Ctx) error {
|
||||
detalle += " · " + p.Fuente
|
||||
}
|
||||
timeline = append(timeline, Evento{
|
||||
Tipo: "pago",
|
||||
Icono: "currency",
|
||||
Titulo: "Pago recibido",
|
||||
Detalle: detalle,
|
||||
Estado: estado,
|
||||
Tipo: "pago",
|
||||
Icono: "currency",
|
||||
Titulo: "Pago recibido",
|
||||
Detalle: detalle,
|
||||
Estado: estado,
|
||||
FechaISO: p.CreatedAt.Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -315,6 +315,104 @@ func ValidarDlocalLog(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Validación de intento de pago (callback) ───────────────────────────────
|
||||
|
||||
// ValidarBoldCallback verifica si el pago de un intento pendiente realmente se realizó.
|
||||
// Recorre las mismas fuentes que verificarPago() y, si confirma, marca el contrato + actualiza el callback.
|
||||
// POST /app/pasarelas/bold/callbacks/:id/validar
|
||||
func ValidarBoldCallback(c *fiber.Ctx) error {
|
||||
cbID, err := c.ParamsInt("id")
|
||||
if err != nil || cbID <= 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
|
||||
}
|
||||
|
||||
var cb models.BoldCallbackLog
|
||||
if err := models.GetBoldCallbackLogByID(uint(cbID), &cb); err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Intento no encontrado"})
|
||||
}
|
||||
|
||||
var contratoID uint
|
||||
if _, err := fmt.Sscanf(cb.Referencia, "contrato-%d", &contratoID); err != nil || contratoID == 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Referencia no tiene formato contrato-{id}"})
|
||||
}
|
||||
|
||||
contrato, err := models.GetContratoParaVerificacion(contratoID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Contrato no encontrado"})
|
||||
}
|
||||
|
||||
pagoConfirmado := contrato.PagoConfirmado
|
||||
fuente := ""
|
||||
|
||||
// ─── 1. Ya confirmado en DB ───────────────────────────────────────────────
|
||||
if pagoConfirmado {
|
||||
fuente = "db"
|
||||
}
|
||||
|
||||
// ─── 2. dlocal_payment_log ───────────────────────────────────────────────
|
||||
if !pagoConfirmado {
|
||||
if dlLogs, dlErr := models.GetDlocalPaymentLogsByRef(cb.Referencia); dlErr == nil {
|
||||
for _, l := range dlLogs {
|
||||
if l.Estado == "PAID" || l.Estado == "AUTHORIZED" {
|
||||
pagoConfirmado = true
|
||||
fuente = "dlocal_log"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 3. Bold API ─────────────────────────────────────────────────────────
|
||||
if !pagoConfirmado && contrato.EnlacePagoLinkID != "" {
|
||||
if boldCfg, boldErr := models.GetBoldConfig(); boldErr == nil {
|
||||
if paid, _, _, apiErr := services.CheckBoldLinkPaid(boldCfg, contrato.EnlacePagoLinkID); apiErr == nil && paid {
|
||||
pagoConfirmado = true
|
||||
fuente = "bold_api"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 4. dLocal API ───────────────────────────────────────────────────────
|
||||
if !pagoConfirmado {
|
||||
if dlocalCfg, dlErr := models.GetLastActiveDlocalApi(); dlErr == nil {
|
||||
if paid, _, _, _, apiErr := services.CheckDlocalPaymentByOrderID(*dlocalCfg, cb.Referencia); apiErr == nil && paid {
|
||||
pagoConfirmado = true
|
||||
fuente = "dlocal_api"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar contrato si se confirmó ahora
|
||||
if pagoConfirmado && !contrato.PagoConfirmado {
|
||||
_ = models.MarcarContratoPagado(contratoID)
|
||||
go services.EnviarCorreoConfirmacionPago(contratoID)
|
||||
}
|
||||
|
||||
// Actualizar estado del callback
|
||||
nuevoEstado := cb.Estado
|
||||
if pagoConfirmado && cb.Estado == "pendiente" {
|
||||
nuevoEstado = "pagado"
|
||||
_ = models.UpdateBoldCallbackEstadoByID(uint(cbID), "pagado")
|
||||
cb.Estado = "pagado"
|
||||
}
|
||||
|
||||
msg := "Pago aún no confirmado"
|
||||
if pagoConfirmado && !contrato.PagoConfirmado {
|
||||
msg = fmt.Sprintf("Pago confirmado via %s — contrato marcado como pagado", fuente)
|
||||
} else if pagoConfirmado {
|
||||
msg = fmt.Sprintf("Pago ya confirmado (%s)", fuente)
|
||||
}
|
||||
_ = nuevoEstado
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"ok": true,
|
||||
"confirmado": pagoConfirmado,
|
||||
"fuente": fuente,
|
||||
"mensaje": msg,
|
||||
"data": cb,
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Validación de log API_CHECK ─────────────────────────────────────────────
|
||||
|
||||
// ValidarBoldLog verifica el estado de pago real de un log API_CHECK consultando
|
||||
|
||||
@@ -143,6 +143,7 @@ func UserRoutes(app fiber.Router) {
|
||||
protected.Get("/pasarelas/bold/logs", controllers.BoldWebhookLogs)
|
||||
protected.Post("/pasarelas/bold/logs/:id/validar", controllers.ValidarBoldLog)
|
||||
protected.Get("/pasarelas/bold/callbacks", controllers.BoldCallbackLogs)
|
||||
protected.Post("/pasarelas/bold/callbacks/:id/validar", controllers.ValidarBoldCallback)
|
||||
// dLocal
|
||||
protected.Get("/pasarelas/dlocal/config", controllers.GetDlocalConfigAPI)
|
||||
protected.Post("/pasarelas/dlocal/save", controllers.SaveDlocalConfigWeb)
|
||||
|
||||
Reference in New Issue
Block a user