From 553fc2547350dd63a769fb40ee18d10c01469f18 Mon Sep 17 00:00:00 2001
From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com>
Date: Tue, 12 May 2026 21:15:53 -0500
Subject: [PATCH] up
---
pkg/models/bold_config.go | 28 ++-
pkg/models/dlocal_api.go | 42 ++++
resources/views/pasarelas_pago.html | 248 ++++++++++++++++++++-
rest/controllers/pasarelas_controller.go | 263 ++++++++++++++++++++++-
rest/routes/user.go | 4 +-
5 files changed, 578 insertions(+), 7 deletions(-)
diff --git a/pkg/models/bold_config.go b/pkg/models/bold_config.go
index f74db14..c0d6abb 100644
--- a/pkg/models/bold_config.go
+++ b/pkg/models/bold_config.go
@@ -106,11 +106,17 @@ func GetBoldWebhookLogs(limit int) ([]BoldWebhookLog, error) {
}
// GetBoldWebhookLogsPaginated devuelve los logs con paginación y filtro por tipo.
+// El valor especial "APROBADOS" incluye tanto SALE_APPROVED como API_CHECK.
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" {
+ switch tipo {
+ case "", "TODOS":
+ // sin filtro
+ case "APROBADOS":
+ db = db.Where("tipo IN ?", []string{"SALE_APPROVED", "API_CHECK"})
+ default:
db = db.Where("tipo = ?", tipo)
}
db.Count(&total)
@@ -121,6 +127,26 @@ func GetBoldWebhookLogsPaginated(page, limit int, tipo string) ([]BoldWebhookLog
return logs, total, nil
}
+// UpdateBoldWebhookLogDatos actualiza email y monto de un log existente (para rellenar datos faltantes en API_CHECK).
+func UpdateBoldWebhookLogDatos(id uint, payerEmail string, monto int64) error {
+ updates := map[string]interface{}{}
+ if payerEmail != "" {
+ updates["payer_email"] = payerEmail
+ }
+ if monto > 0 {
+ updates["monto"] = monto
+ }
+ if len(updates) == 0 {
+ return nil
+ }
+ return app.Http.Database.DB.Model(&BoldWebhookLog{}).Where("id = ?", id).Updates(updates).Error
+}
+
+// GetBoldWebhookLogByID carga un registro por su PK.
+func GetBoldWebhookLogByID(id uint, out *BoldWebhookLog) error {
+ return app.Http.Database.DB.First(out, id).Error
+}
+
// ─── Callback log (intentos de pago) ─────────────────────────────────────────
// BoldCallbackLog registra cada visita a la URL de retorno de Bold.
diff --git a/pkg/models/dlocal_api.go b/pkg/models/dlocal_api.go
index 2aa02ff..da1c913 100644
--- a/pkg/models/dlocal_api.go
+++ b/pkg/models/dlocal_api.go
@@ -150,6 +150,48 @@ func GetDlocalPaymentLogs(limit int) ([]DlocalPaymentLog, error) {
return logs, nil
}
+// GetDlocalPaymentLogsPaginated devuelve los logs con paginación y filtro por estado.
+// El valor especial "APROBADOS" agrupa PAID + AUTHORIZED.
+func GetDlocalPaymentLogsPaginated(page, limit int, estado string) ([]DlocalPaymentLog, int64, error) {
+ var logs []DlocalPaymentLog
+ var total int64
+ db := app.Http.Database.DB.Model(&DlocalPaymentLog{})
+ switch estado {
+ case "", "TODOS":
+ // sin filtro
+ case "APROBADOS":
+ db = db.Where("estado IN ?", []string{"PAID", "AUTHORIZED"})
+ default:
+ 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
+}
+
+// GetDlocalPaymentLogByID carga un registro por su PK.
+func GetDlocalPaymentLogByID(id uint, out *DlocalPaymentLog) error {
+ return app.Http.Database.DB.First(out, id).Error
+}
+
+// UpdateDlocalPaymentLogDatos actualiza email y monto de un log existente.
+func UpdateDlocalPaymentLogDatos(id uint, payerEmail string, monto float64) error {
+ updates := map[string]interface{}{}
+ if payerEmail != "" {
+ updates["payer_email"] = payerEmail
+ }
+ if monto > 0 {
+ updates["monto"] = monto
+ }
+ if len(updates) == 0 {
+ return nil
+ }
+ return app.Http.Database.DB.Model(&DlocalPaymentLog{}).Where("id = ?", id).Updates(updates).Error
+}
+
// GetDlocalPaymentLogsByRef devuelve todos los registros que coinciden con una referencia/order_id.
func GetDlocalPaymentLogsByRef(ref string) ([]DlocalPaymentLog, error) {
var logs []DlocalPaymentLog
diff --git a/resources/views/pasarelas_pago.html b/resources/views/pasarelas_pago.html
index 5bc7b79..c7dada8 100644
--- a/resources/views/pasarelas_pago.html
+++ b/resources/views/pasarelas_pago.html
@@ -330,7 +330,8 @@
x-text="log.procesado ? 'Procesado' : 'Pendiente'">
|
-
+ |
+
+
+
+
|
@@ -536,6 +548,14 @@
+
@@ -810,6 +830,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sin notificaciones de dLocal registradas aún.
+
+
+
+
+
+ | Tipo / Fuente |
+ Payment ID |
+ Referencia |
+ Email pagador |
+ Monto |
+ Estado |
+ Fecha |
+ |
+
+
+
+
+
+ |
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+
+
+ |
+ |
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
Mostrando de registros
+
+
+ Pág
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Detalle de notificación dLocal
+
+
+
Notification ID
+
Payment ID
+
Order ID
+
Referencia
+
Tipo
+
Email pagador
+
Monto
+
Procesado
+
Fecha
+
+
+
+
+
@@ -844,8 +1010,8 @@ function pasarelasApp() {
boldLogsLimit: 25,
boldLogsFilter: '',
boldLogsStats: [
- { tipo: '', label: 'Todos', color: 'bg-gray-400', count: 0 },
- { tipo: 'SALE_APPROVED', label: 'Aprobados', color: 'bg-green-400', count: 0 },
+ { tipo: '', label: 'Todos', color: 'bg-gray-400', count: 0 },
+ { tipo: 'APROBADOS', label: 'Aprobados', color: 'bg-green-400', count: 0 },
{ tipo: 'SALE_REJECTED', label: 'Rechazados', color: 'bg-red-400', count: 0 },
{ tipo: 'SALE_REVERSED', label: 'Revertidos', color: 'bg-orange-400', count: 0 },
{ tipo: 'CHARGEBACK', label: 'Contracargos', color: 'bg-purple-400', count: 0 },
@@ -863,6 +1029,7 @@ function pasarelasApp() {
selectedLog: null,
showCallbackModal: false,
selectedCallback: null,
+ validandoLogID: 0,
// ─── dLocal ─────────────────────────────────────────────────────
dlocalModo: 'dev',
@@ -879,6 +1046,22 @@ function pasarelasApp() {
// Sub-tabs dLocal
dlocalTab: 'config',
+ // Notificaciones dLocal
+ dlocalLogs: [],
+ dlocalLogsTotal: 0,
+ dlocalLogsPage: 1,
+ dlocalLogsLimit: 25,
+ dlocalLogsFilter: '',
+ dlocalLogsStats: [
+ { estado: '', label: 'Todos', color: 'bg-gray-400', count: 0 },
+ { estado: 'APROBADOS', label: 'Aprobados', color: 'bg-green-400', count: 0 },
+ { estado: 'PENDING', label: 'Pendientes', color: 'bg-yellow-400', count: 0 },
+ { estado: 'REJECTED', label: 'Rechazados', color: 'bg-red-400', count: 0 },
+ ],
+ showDlocalLogModal: false,
+ selectedDlocalLog: null,
+ validandoDlocalLogID: 0,
+
// Planes dLocal
dlocalPlanes: [],
dlocalPlanesLoading: false,
@@ -950,7 +1133,7 @@ function pasarelasApp() {
const all = r.data.total || 0;
this.boldLogsStats[0].count = all;
// Cargar contadores por tipo en paralelo
- ['SALE_APPROVED','SALE_REJECTED','SALE_REVERSED','CHARGEBACK'].forEach((tipo, i) => {
+ ['APROBADOS','SALE_REJECTED','SALE_REVERSED','CHARGEBACK'].forEach((tipo, i) => {
axios.get(`/app/pasarelas/bold/logs?page=1&limit=1&tipo=${tipo}`)
.then(res => { this.boldLogsStats[i+1].count = res.data.total || 0; })
.catch(() => {});
@@ -980,6 +1163,27 @@ function pasarelasApp() {
});
},
+ async validarLog(log) {
+ this.validandoLogID = log.ID;
+ try {
+ const { data } = await axios.post(`/app/pasarelas/bold/logs/${log.ID}/validar`);
+ if (data.ok && data.data) {
+ // Actualizar la fila en memoria
+ const idx = this.boldLogs.findIndex(l => l.ID === log.ID);
+ if (idx !== -1) this.boldLogs[idx] = data.data;
+ }
+ const msg = data.mensaje || (data.confirmado ? 'Pago confirmado ✓' : 'Datos actualizados');
+ const tipo = data.confirmado ? 'success' : 'info';
+ this.showToast(msg, data.confirmado ? 'success' : 'error');
+ // Recargar stats si el pago se confirmó ahora
+ if (data.confirmado) this.loadBoldLogs();
+ } catch (e) {
+ this.showToast(e.response?.data?.error || 'Error al validar', 'error');
+ } finally {
+ this.validandoLogID = 0;
+ }
+ },
+
tryPrettyJson(raw) {
if (!raw) return '';
try { return JSON.stringify(JSON.parse(raw), null, 2); } catch (_) { return raw; }
@@ -1103,6 +1307,42 @@ function pasarelasApp() {
navigator.clipboard.writeText(url).then(() => this.showToast('URL copiada: ' + url));
},
+ async loadDlocalLogs() {
+ try {
+ const params = new URLSearchParams({ page: this.dlocalLogsPage, limit: this.dlocalLogsLimit });
+ if (this.dlocalLogsFilter) params.set('estado', this.dlocalLogsFilter);
+ const r = await axios.get('/app/pasarelas/dlocal/logs?' + params.toString());
+ this.dlocalLogs = r.data.data || [];
+ this.dlocalLogsTotal = r.data.total || 0;
+ if (!this.dlocalLogsFilter) {
+ this.dlocalLogsStats[0].count = r.data.total || 0;
+ ['APROBADOS','PENDING','REJECTED'].forEach((e, i) => {
+ axios.get(`/app/pasarelas/dlocal/logs?page=1&limit=1&estado=${e}`)
+ .then(res => { this.dlocalLogsStats[i+1].count = res.data.total || 0; })
+ .catch(() => {});
+ });
+ }
+ } catch (_) {}
+ },
+
+ async validarDlocalLog(log) {
+ this.validandoDlocalLogID = log.ID;
+ try {
+ const { data } = await axios.post(`/app/pasarelas/dlocal/logs/${log.ID}/validar`);
+ if (data.ok && data.data) {
+ const idx = this.dlocalLogs.findIndex(l => l.ID === log.ID);
+ if (idx !== -1) this.dlocalLogs[idx] = data.data;
+ }
+ const msg = data.mensaje || (data.confirmado ? 'Pago confirmado ✓' : 'Datos actualizados');
+ this.showToast(msg, data.confirmado ? 'success' : 'error');
+ if (data.confirmado) this.loadDlocalLogs();
+ } catch (e) {
+ this.showToast(e.response?.data?.error || 'Error al validar', 'error');
+ } finally {
+ this.validandoDlocalLogID = 0;
+ }
+ },
+
// ─── Toast ───────────────────────────────────────────────────────
showToast(msg, type = 'success') {
this.toast = { show: true, msg, type };
diff --git a/rest/controllers/pasarelas_controller.go b/rest/controllers/pasarelas_controller.go
index ee17395..8de82d8 100644
--- a/rest/controllers/pasarelas_controller.go
+++ b/rest/controllers/pasarelas_controller.go
@@ -1,8 +1,11 @@
package controllers
import (
+ "fmt"
+
"github.com/gofiber/fiber/v2"
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
+ "github.com/sujit-baniya/fiber-boilerplate/pkg/services"
)
// PasarelasPage renderiza la vista unificada de pasarelas de pago.
@@ -173,7 +176,7 @@ func BoldCallbackLogs(c *fiber.Ctx) error {
// ─── Logs dLocal ──────────────────────────────────────────────────────────────
-// DlocalPaymentLogs devuelve los últimos 100 registros de pagos de dLocal.
+// DlocalPaymentLogs devuelve los últimos 100 registros de pagos de dLocal (legacy).
func DlocalPaymentLogs(c *fiber.Ctx) error {
logs, err := models.GetDlocalPaymentLogs(100)
if err != nil {
@@ -181,3 +184,261 @@ func DlocalPaymentLogs(c *fiber.Ctx) error {
}
return c.JSON(fiber.Map{"data": logs})
}
+
+// DlocalPaymentLogsPaginated devuelve los logs con paginación y filtro por estado.
+// Query params: page (default 1), limit (default 25), estado (PAID|PENDING|REJECTED|APROBADOS|TODOS)
+func DlocalPaymentLogsPaginated(c *fiber.Ctx) error {
+ page := c.QueryInt("page", 1)
+ limit := c.QueryInt("limit", 25)
+ estado := c.Query("estado", "")
+ if page < 1 {
+ page = 1
+ }
+ logs, total, err := models.GetDlocalPaymentLogsPaginated(page, limit, estado)
+ if err != nil {
+ return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
+ }
+ return c.JSON(fiber.Map{
+ "data": logs,
+ "total": total,
+ "page": page,
+ "limit": limit,
+ })
+}
+
+// ValidarDlocalLog verifica el estado de pago real de un log dLocal consultando
+// todas las fuentes disponibles (dlocal_payment_log → Bold API → dLocal API).
+// Si se confirma el pago, marca el contrato como pagado y envía confirmación.
+// POST /app/pasarelas/dlocal/logs/:id/validar
+func ValidarDlocalLog(c *fiber.Ctx) error {
+ logID, err := c.ParamsInt("id")
+ if err != nil || logID <= 0 {
+ return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
+ }
+
+ var entry models.DlocalPaymentLog
+ if err := models.GetDlocalPaymentLogByID(uint(logID), &entry); err != nil {
+ return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Log no encontrado"})
+ }
+
+ ref := entry.Referencia
+ if ref == "" {
+ ref = entry.OrderID
+ }
+
+ var contratoID uint
+ if _, err := fmt.Sscanf(ref, "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"})
+ }
+
+ payerEmail := entry.PayerEmail
+ if payerEmail == "" {
+ payerEmail = contrato.Cliente.Email
+ }
+ monto := entry.Monto
+ pagoConfirmado := contrato.PagoConfirmado
+ fuenteConfirmacion := ""
+
+ // ─── 1. Ya confirmado en DB ───────────────────────────────────────────────
+ if pagoConfirmado {
+ fuenteConfirmacion = "db"
+ }
+
+ // ─── 2. Mismo log si ya tiene estado PAID ────────────────────────────────
+ if !pagoConfirmado && (entry.Estado == "PAID" || entry.Estado == "AUTHORIZED") {
+ pagoConfirmado = true
+ fuenteConfirmacion = "dlocal_log"
+ }
+
+ // ─── 3. Bold API ─────────────────────────────────────────────────────────
+ if !pagoConfirmado && contrato.EnlacePagoLinkID != "" {
+ boldCfg, boldErr := models.GetBoldConfig()
+ if boldErr == nil {
+ paid, _, apiMonto, apiErr := services.CheckBoldLinkPaid(boldCfg, contrato.EnlacePagoLinkID)
+ if apiErr == nil && paid {
+ pagoConfirmado = true
+ fuenteConfirmacion = "bold_api"
+ if apiMonto > 0 {
+ monto = float64(apiMonto)
+ }
+ }
+ }
+ }
+
+ // ─── 4. dLocal API ───────────────────────────────────────────────────────
+ if !pagoConfirmado {
+ dlocalCfg, dlErr := models.GetLastActiveDlocalApi()
+ if dlErr == nil {
+ paid, _, apiMonto, apiEmail, apiErr := services.CheckDlocalPaymentByOrderID(*dlocalCfg, ref)
+ if apiErr == nil && paid {
+ pagoConfirmado = true
+ fuenteConfirmacion = "dlocal_api"
+ if apiMonto > 0 {
+ monto = apiMonto
+ }
+ if apiEmail != "" {
+ payerEmail = apiEmail
+ }
+ }
+ }
+ }
+
+ // Si el pago se confirmó ahora (no estaba marcado antes), actualizar contrato
+ if pagoConfirmado && !contrato.PagoConfirmado {
+ _ = models.MarcarContratoPagado(contratoID)
+ go services.EnviarCorreoConfirmacionPago(contratoID)
+ }
+
+ _ = models.UpdateDlocalPaymentLogDatos(uint(logID), payerEmail, monto)
+ _ = models.GetDlocalPaymentLogByID(uint(logID), &entry)
+
+ msg := "Datos actualizados"
+ if pagoConfirmado && !contrato.PagoConfirmado {
+ msg = fmt.Sprintf("Pago confirmado via %s — contrato marcado como pagado", fuenteConfirmacion)
+ } else if pagoConfirmado {
+ msg = fmt.Sprintf("Pago ya confirmado (%s)", fuenteConfirmacion)
+ } else {
+ msg = "Pago aún no confirmado — datos actualizados"
+ }
+
+ return c.JSON(fiber.Map{
+ "ok": true,
+ "confirmado": pagoConfirmado,
+ "fuente": fuenteConfirmacion,
+ "mensaje": msg,
+ "data": entry,
+ })
+}
+
+// ─── Validación de log API_CHECK ─────────────────────────────────────────────
+
+// ValidarBoldLog verifica el estado de pago real de un log API_CHECK consultando
+// todas las fuentes disponibles (dlocal_payment_log → Bold API → dLocal API).
+// Si se confirma el pago, marca el contrato como pagado y envía el email de confirmación.
+// También rellena email y monto faltantes. Funciona para cualquier tipo de log Bold.
+// POST /app/pasarelas/bold/logs/:id/validar
+func ValidarBoldLog(c *fiber.Ctx) error {
+ logID, err := c.ParamsInt("id")
+ if err != nil || logID <= 0 {
+ return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
+ }
+
+ // Cargar el registro existente
+ var entry models.BoldWebhookLog
+ if err := models.GetBoldWebhookLogByID(uint(logID), &entry); err != nil {
+ return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Log no encontrado"})
+ }
+
+ // Parsear contrato ID de la referencia (contrato-{id})
+ var contratoID uint
+ if _, err := fmt.Sscanf(entry.Referencia, "contrato-%d", &contratoID); err != nil || contratoID == 0 {
+ return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Referencia no tiene formato contrato-{id}"})
+ }
+
+ // Cargar contrato con cliente
+ contrato, err := models.GetContratoParaVerificacion(contratoID)
+ if err != nil {
+ return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Contrato no encontrado"})
+ }
+
+ // Valores iniciales: reutilizar lo que ya hay en el log, fallback al cliente
+ payerEmail := entry.PayerEmail
+ if payerEmail == "" {
+ payerEmail = contrato.Cliente.Email
+ }
+ monto := entry.Monto
+ pagoConfirmado := contrato.PagoConfirmado
+ fuenteConfirmacion := ""
+
+ // ─── 1. Ya confirmado en DB ───────────────────────────────────────────────
+ if pagoConfirmado {
+ fuenteConfirmacion = "db"
+ }
+
+ // ─── 2. dlocal_payment_log ────────────────────────────────────────────────
+ if !pagoConfirmado {
+ dlocalLogs, dlErr := models.GetDlocalPaymentLogsByRef(entry.Referencia)
+ if dlErr == nil {
+ for _, l := range dlocalLogs {
+ if l.Estado == "PAID" || l.Estado == "AUTHORIZED" {
+ pagoConfirmado = true
+ fuenteConfirmacion = "dlocal_log"
+ if l.PayerEmail != "" {
+ payerEmail = l.PayerEmail
+ }
+ if l.Monto > 0 {
+ monto = int64(l.Monto)
+ }
+ break
+ }
+ }
+ }
+ }
+
+ // ─── 3. Bold API ─────────────────────────────────────────────────────────
+ if !pagoConfirmado && contrato.EnlacePagoLinkID != "" {
+ boldCfg, boldErr := models.GetBoldConfig()
+ if boldErr == nil {
+ paid, _, apiMonto, apiErr := services.CheckBoldLinkPaid(boldCfg, contrato.EnlacePagoLinkID)
+ if apiErr == nil && paid {
+ pagoConfirmado = true
+ fuenteConfirmacion = "bold_api"
+ if apiMonto > 0 {
+ monto = apiMonto
+ }
+ }
+ }
+ }
+
+ // ─── 4. dLocal API ───────────────────────────────────────────────────────
+ if !pagoConfirmado {
+ dlocalCfg, dlErr := models.GetLastActiveDlocalApi()
+ if dlErr == nil {
+ paid, _, apiMonto, apiEmail, apiErr := services.CheckDlocalPaymentByOrderID(*dlocalCfg, entry.Referencia)
+ if apiErr == nil && paid {
+ pagoConfirmado = true
+ fuenteConfirmacion = "dlocal_api"
+ if apiMonto > 0 {
+ monto = int64(apiMonto)
+ }
+ if apiEmail != "" {
+ payerEmail = apiEmail
+ }
+ }
+ }
+ }
+
+ // Si el pago se confirmó ahora (no estaba marcado antes), actualizar contrato
+ if pagoConfirmado && !contrato.PagoConfirmado {
+ _ = models.MarcarContratoPagado(contratoID)
+ go services.EnviarCorreoConfirmacionPago(contratoID)
+ }
+
+ // Actualizar log con los mejores datos disponibles
+ _ = models.UpdateBoldWebhookLogDatos(uint(logID), payerEmail, monto)
+
+ // Devolver el log actualizado
+ _ = models.GetBoldWebhookLogByID(uint(logID), &entry)
+
+ msg := "Datos actualizados"
+ if pagoConfirmado && !contrato.PagoConfirmado {
+ msg = fmt.Sprintf("Pago confirmado via %s — contrato marcado como pagado", fuenteConfirmacion)
+ } else if pagoConfirmado {
+ msg = fmt.Sprintf("Pago ya confirmado (%s)", fuenteConfirmacion)
+ } else {
+ msg = "Pago aún no confirmado — datos del contrato actualizados"
+ }
+
+ return c.JSON(fiber.Map{
+ "ok": true,
+ "confirmado": pagoConfirmado,
+ "fuente": fuenteConfirmacion,
+ "mensaje": msg,
+ "data": entry,
+ })
+}
diff --git a/rest/routes/user.go b/rest/routes/user.go
index 1e90dbf..feaea6c 100755
--- a/rest/routes/user.go
+++ b/rest/routes/user.go
@@ -141,11 +141,13 @@ func UserRoutes(app fiber.Router) {
protected.Get("/pasarelas/bold/config", controllers.GetBoldConfigAPI)
protected.Post("/pasarelas/bold/save", controllers.SaveBoldConfig)
protected.Get("/pasarelas/bold/logs", controllers.BoldWebhookLogs)
+ protected.Post("/pasarelas/bold/logs/:id/validar", controllers.ValidarBoldLog)
protected.Get("/pasarelas/bold/callbacks", controllers.BoldCallbackLogs)
// dLocal
protected.Get("/pasarelas/dlocal/config", controllers.GetDlocalConfigAPI)
protected.Post("/pasarelas/dlocal/save", controllers.SaveDlocalConfigWeb)
- protected.Get("/pasarelas/dlocal/logs", controllers.DlocalPaymentLogs)
+ protected.Get("/pasarelas/dlocal/logs", controllers.DlocalPaymentLogsPaginated)
+ protected.Post("/pasarelas/dlocal/logs/:id/validar", controllers.ValidarDlocalLog)
protected.Post("/pasarelas/dlocal/registro-pago", apiControllers.DlocalRegistrarPago)
// Bold API (crear link, consultar estado)
protected.Post("/pasarelas/bold/crear-link", apiControllers.BoldCreatePaymentLink)