This commit is contained in:
lizandrogd
2025-05-03 00:09:03 -05:00
parent e9f63bead3
commit beb50ad892
5 changed files with 597 additions and 0 deletions
+1
View File
@@ -41,6 +41,7 @@ func Migrate() {
&models.QrVcard{},
&models.OssApi{},
&models.VcfVcard{},
&models.DlocalApi{},
); err != nil {
log.Fatalf("Error during main migration: %v", err)
}
+97
View File
@@ -0,0 +1,97 @@
package models
import (
"log"
"github.com/sujit-baniya/fiber-boilerplate/app"
"gorm.io/gorm"
)
type DlocalApi struct {
gorm.Model
AccessKeyID string `gorm:"not null" json:"access_key_id"` // Access Key ID
AccessKeySecret string `gorm:"not null" json:"access_key_secret"` // Access Key Secret
AccessKeyIDdev string `gorm:"not null" json:"access_key_id_dev"` // Access Key ID
AccessKeySecretdev string `gorm:"not null" json:"access_key_secret_dev"`
IsActive bool `gorm:"default:true" json:"is_active"` // Activar o desactivar config
UrlDev string `gorm:"type:text" json:"url_dev"`
UrlProd string `gorm:"type:text" json:"url_prod"`
Modo string `gorm:"default:'prod'" json:"modo"`
}
type PlanRequest struct {
Name string `json:"name"`
Description string `json:"description"`
Country string `json:"country"`
Currency string `json:"currency"`
Amount float64 `json:"amount"`
FrequencyType string `json:"frequency_type"`
FrequencyValue int `json:"frequency_value"`
DayOfMonth int `json:"day_of_month"`
NotificationURL string `json:"notification_url"`
SuccessURL string `json:"success_url"`
BackURL string `json:"back_url"`
ErrorURL string `json:"error_url"`
}
// TableName asegura que GORM use la tabla 'qrvcard'
func (DlocalApi) TableName() string {
return "dlocal_api"
}
// GetAllQrVcard obtiene todos los registros de QrVcard con paginación y búsqueda
func GetAllDlocalApi(limit, offset int, search string) ([]DlocalApi, int64, error) {
var items []DlocalApi
var total int64
db := app.Http.Database.DB.Model(&DlocalApi{})
// Contamos el total de registros
if err := db.Count(&total).Error; err != nil {
log.Printf("Error counting DlocalApi: %v", err)
return nil, 0, err
}
// Obtenemos los registros con paginación
if err := db.Order("id DESC").Limit(limit).Offset(offset).Find(&items).Error; err != nil {
log.Printf("Error retrieving DlocalApi: %v", err)
return nil, 0, err
}
return items, total, nil
}
// CreateQrVcard crea un nuevo registro de QrVcard en la base de datos
func CreateDlocalApi(DlocalApi *DlocalApi) error {
if err := app.Http.Database.DB.Create(&DlocalApi).Error; err != nil {
return err
}
return nil
}
// UpdateQrVcard actualiza un registro de QrVcard existente
func UpdateDlocalApi(DlocalApi *DlocalApi) error {
if err := app.Http.Database.DB.Model(&DlocalApi).Updates(DlocalApi).Error; err != nil {
return err
}
return nil
}
// DeleteQrVcard elimina un registro de QrVcard de la base de datos
func DeleteDlocalApi(DlocalApi *DlocalApi) error {
if err := app.Http.Database.DB.Delete(&DlocalApi).Error; err != nil {
return err
}
return nil
}
// GetLastActiveDlocalApi obtiene el último registro activo
func GetLastActiveDlocalApi() (*DlocalApi, error) {
var dlocalConfig DlocalApi
err := app.Http.Database.DB.
Where("is_active = ?", true).
Order("id DESC").
First(&dlocalConfig).Error
if err != nil {
return nil, err
}
return &dlocalConfig, nil
}
+357
View File
@@ -0,0 +1,357 @@
package services
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
"gorm.io/gorm"
)
// Modelo que refleja la tabla en la base de datos
type DlocalApi struct {
ID uint `gorm:"primaryKey"`
Modo string
AccessKeyID string
AccessKeySecret string
AccessKeyIDdev string
AccessKeySecretdev string
UrlProd string
UrlDev string
}
// Estructura de configuración para usar en la llamada
type dlocalConfig struct {
Modo string
AccessKeyID string
AccessKeySecret string
AccessKeyIDdev string
AccessKeySecretdev string
UrlProd string
UrlDev string
}
// Obtiene la configuración desde la base de datos
func GetDlocalConfigFromDB(db *gorm.DB) (dlocalConfig, error) {
var api DlocalApi
if err := db.First(&api).Error; err != nil {
return dlocalConfig{}, err
}
return dlocalConfig{
Modo: api.Modo,
AccessKeyID: api.AccessKeyID,
AccessKeySecret: api.AccessKeySecret,
AccessKeyIDdev: api.AccessKeyIDdev,
AccessKeySecretdev: api.AccessKeySecretdev,
UrlProd: api.UrlProd,
UrlDev: api.UrlDev,
}, nil
}
type PlanRequest struct {
Name string `json:"name"`
Description string `json:"description"`
Country string `json:"country,omitempty"`
Currency string `json:"currency"`
Amount float64 `json:"amount"`
FrequencyType string `json:"frequency_type"`
FrequencyValue int `json:"frequency_value,omitempty"`
DayOfMonth int `json:"day_of_month,omitempty"`
NotificationURL string `json:"notification_url,omitempty"`
SuccessURL string `json:"success_url"`
BackURL string `json:"back_url"`
ErrorURL string `json:"error_url"`
}
type PlanUpdated struct {
Name string `json:"name"` // Plan name
Description string `json:"description"` // Plan description
Amount float64 `json:"amount"` // Plan amount
NotificationURL string `json:"notification_url"` // Webhook notification URL
BackURL string `json:"back_url"` // URL to return client to merchant site
SuccessURL string `json:"success_url"` // Redirect URL on payment success
ErrorURL string `json:"error_url"` // Redirect URL on payment error
}
// Llama al endpoint /v1/me de DLocal usando la configuración
func GetDLocalMe(cfg dlocalConfig) ([]byte, error) {
// Determinar URL base según el modo
baseURL := cfg.UrlDev
AccessKeyID := cfg.AccessKeyIDdev
AccessKeySecret := cfg.AccessKeySecretdev
if cfg.Modo == "prod" {
baseURL = cfg.UrlProd
AccessKeyID = cfg.AccessKeyID
AccessKeySecret = cfg.AccessKeySecret
}
// Construir token de autorización
authToken := fmt.Sprintf("Bearer %s:%s", AccessKeyID, AccessKeySecret)
// Crear la solicitud HTTP
req, err := http.NewRequest("GET", baseURL+"/v1/me", bytes.NewBuffer([]byte("{}")))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", authToken)
// Ejecutar la solicitud
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// Leer la respuesta
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
// Función para crear el plan en DLocal
func CreatePlan(cfg models.DlocalApi, plan models.PlanRequest) ([]byte, error) {
// Determinar la URL base según el modo
baseURL := cfg.UrlDev
AccessKeyID := cfg.AccessKeyIDdev
AccessKeySecret := cfg.AccessKeySecretdev
if cfg.Modo == "prod" {
baseURL = cfg.UrlProd
AccessKeyID = cfg.AccessKeyID
AccessKeySecret = cfg.AccessKeySecret
}
// URL completa para el endpoint
url := baseURL + "/v1/subscription/plan"
// Construir el token de autorización
authToken := fmt.Sprintf("Bearer %s:%s", AccessKeyID, AccessKeySecret)
// Convertir el cuerpo de la solicitud a JSON
requestBody, err := json.Marshal(plan)
if err != nil {
return nil, fmt.Errorf("error al convertir el cuerpo de la solicitud a JSON: %v", err)
}
// Crear la solicitud HTTP
req, err := http.NewRequest("POST", url, bytes.NewBuffer(requestBody))
if err != nil {
return nil, fmt.Errorf("error al crear la solicitud: %v", err)
}
// Agregar los encabezados a la solicitud
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", authToken)
// Configurar el cliente HTTP
client := &http.Client{
Timeout: 30 * time.Second,
}
// Ejecutar la solicitud
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error al enviar la solicitud: %v", err)
}
defer resp.Body.Close()
// Leer la respuesta
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error al leer la respuesta: %v", err)
}
// Si la respuesta no es exitosa, devolver un error
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("error en la respuesta de DLocal: %s", string(responseBody))
}
// Devolver la respuesta exitosa
return responseBody, nil
}
func SeePlan(cfg models.DlocalApi, planID string) ([]byte, error) {
// Determinar la URL base según el modo
baseURL := cfg.UrlDev
accessKeyID := cfg.AccessKeyIDdev
accessKeySecret := cfg.AccessKeySecretdev
if cfg.Modo == "prod" {
baseURL = cfg.UrlProd
accessKeyID = cfg.AccessKeyID
accessKeySecret = cfg.AccessKeySecret
}
// Validar planID (opcional: depende de tus requisitos)
if planID == "" {
return nil, fmt.Errorf("planID no puede estar vacío")
}
// Construir la URL completa para el endpoint
url := fmt.Sprintf("%s/v1/subscription/plan/%s", baseURL, planID)
// Construir el token de autorización
authToken := fmt.Sprintf("Bearer %s:%s", accessKeyID, accessKeySecret)
// Crear la solicitud HTTP
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("error al crear la solicitud: %v", err)
}
// Agregar los encabezados a la solicitud
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", authToken)
// Configurar el cliente HTTP
client := &http.Client{
Timeout: 30 * time.Second,
}
// Ejecutar la solicitud
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error al enviar la solicitud: %v", err)
}
defer resp.Body.Close()
// Leer la respuesta
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error al leer la respuesta: %v", err)
}
// Si la respuesta no es exitosa, devolver un error
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("error en la respuesta de DLocal (%d): %s", resp.StatusCode, string(responseBody))
}
// Devolver la respuesta exitosa
return responseBody, nil
}
func UpdatedPlan(cfg models.DlocalApi, plan PlanUpdated, planID string) ([]byte, error) {
// Determinar la URL base según el modo
baseURL := cfg.UrlDev
accessKeyID := cfg.AccessKeyIDdev
accessKeySecret := cfg.AccessKeySecretdev
if cfg.Modo == "prod" {
baseURL = cfg.UrlProd
accessKeyID = cfg.AccessKeyID
accessKeySecret = cfg.AccessKeySecret
}
// Construir la URL completa para el endpoint
url := fmt.Sprintf("%s/v1/subscription/plan/%s", baseURL, planID)
// Construir el token de autorización
authToken := fmt.Sprintf("Bearer %s:%s", accessKeyID, accessKeySecret)
// Convertir el cuerpo de la solicitud a JSON
requestBody, err := json.Marshal(plan)
if err != nil {
return nil, fmt.Errorf("error al convertir el cuerpo de la solicitud a JSON: %v", err)
}
// Crear la solicitud HTTP
req, err := http.NewRequest("PATCH", url, bytes.NewBuffer(requestBody))
if err != nil {
return nil, fmt.Errorf("error al crear la solicitud: %v", err)
}
// Agregar los encabezados a la solicitud
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", authToken)
// Configurar el cliente HTTP
client := &http.Client{
Timeout: 30 * time.Second,
}
// Ejecutar la solicitud
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error al enviar la solicitud: %v", err)
}
defer resp.Body.Close()
// Leer la respuesta
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error al leer la respuesta: %v", err)
}
// Si la respuesta no es exitosa, devolver un error
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("error en la respuesta de DLocal (%d): %s", resp.StatusCode, string(responseBody))
}
// Devolver la respuesta exitosa
return responseBody, nil
}
func DeactivatePlan(cfg models.DlocalApi, planID string, subscriptionId string) ([]byte, error) {
// Determinar la URL base según el modo
baseURL := cfg.UrlDev
accessKeyID := cfg.AccessKeyIDdev
accessKeySecret := cfg.AccessKeySecretdev
if cfg.Modo == "prod" {
baseURL = cfg.UrlProd
accessKeyID = cfg.AccessKeyID
accessKeySecret = cfg.AccessKeySecret
}
// Construir la URL completa para el endpoint
url := fmt.Sprintf("%s/v1/subscription/plan/%s/subscription/%s/deactivate", baseURL, planID, subscriptionId)
// Construir el token de autorización
authToken := fmt.Sprintf("Bearer %s:%s", accessKeyID, accessKeySecret)
// Crear la solicitud HTTP
req, err := http.NewRequest("PATCH", url, nil)
if err != nil {
return nil, fmt.Errorf("error al crear la solicitud: %v", err)
}
// Agregar los encabezados a la solicitud
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", authToken)
// Configurar el cliente HTTP
client := &http.Client{
Timeout: 30 * time.Second,
}
// Ejecutar la solicitud
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error al enviar la solicitud: %v", err)
}
defer resp.Body.Close()
// Leer la respuesta
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error al leer la respuesta: %v", err)
}
// Si la respuesta no es exitosa, devolver un error
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("error en la respuesta de DLocal (%d): %s", resp.StatusCode, string(responseBody))
}
// Devolver la respuesta exitosa
return responseBody, nil
}
+138
View File
@@ -0,0 +1,138 @@
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"
)
// Función para manejar la solicitud de creación de un plan
func CreatePlan(c *fiber.Ctx) error {
var plan models.PlanRequest
// Parsear el cuerpo de la solicitud a la estructura PlanRequest
if err := c.BodyParser(&plan); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": fmt.Sprintf("Error al parsear el cuerpo de la solicitud: %v", err),
})
}
// Obtener la configuración activa de DlocalApi
dlocalConfig, err := models.GetLastActiveDlocalApi()
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Error al obtener la configuración activa de DlocalApi.",
})
}
// Llamar al servicio para crear el plan
response, err := services.CreatePlan(*dlocalConfig, plan)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": fmt.Sprintf("Error al crear el plan: %v", err),
})
}
// Devolver la respuesta exitosa
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"message": "Plan creado exitosamente.",
"response": string(response),
})
}
func SeePlan(c *fiber.Ctx) error {
planID := c.Params("planID")
// Verifica si se recibió correctamente
if planID == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "PlanID es requerido.",
})
}
// Obtener la configuración activa de DlocalApi
dlocalConfig, err := models.GetLastActiveDlocalApi()
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Error al obtener la configuración activa de DlocalApi.",
})
}
// Llamar al servicio para ver el plan
response, err := services.SeePlan(*dlocalConfig, planID)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": fmt.Sprintf("Error al obtener el plan: %v", err),
})
}
// Devolver la respuesta exitosa
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"message": "Plan obtenido exitosamente.",
"response": string(response),
})
}
func UpdatedPlan(c *fiber.Ctx) error {
planID := c.Params("planID")
var plan services.PlanUpdated
// Parsear el cuerpo de la solicitud a la estructura PlanRequest
if err := c.BodyParser(&plan); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": fmt.Sprintf("Error al parsear el cuerpo de la solicitud: %v", err),
})
}
// Obtener la configuración activa de DlocalApi
dlocalConfig, err := models.GetLastActiveDlocalApi()
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Error al obtener la configuración activa de DlocalApi.",
})
}
// Llamar al servicio para crear el plan
response, err := services.UpdatedPlan(*dlocalConfig, plan, planID)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": fmt.Sprintf("Error al crear el plan: %v", err),
})
}
// Devolver la respuesta exitosa
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"message": "Plan actualizado exitosamente.",
"response": string(response),
})
}
func DeactivatePlan(c *fiber.Ctx) error {
planID := c.Params("planID")
subscriptionId := c.Params("subscriptionId")
// Obtener la configuración activa de DlocalApi
dlocalConfig, err := models.GetLastActiveDlocalApi()
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Error al obtener la configuración activa de DlocalApi.",
})
}
// Llamar al servicio para crear el plan
response, err := services.DeactivatePlan(*dlocalConfig, planID, subscriptionId)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": fmt.Sprintf("Error al crear el plan: %v", err),
})
}
// Devolver la respuesta exitosa
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"message": "Suscripción cancelada exitosamente.",
"response": string(response),
})
}
+4
View File
@@ -14,6 +14,10 @@ func v1AuthRoutes(api fiber.Router) {
api.Post("/generate-qr-tmp", apiControllers.CreateQrTmp)
api.Post("/generate-qr", apiControllers.CreateQr)
api.Post("/generate-vcf", apiControllers.CreateVcf)
api.Post("/dlocal/subscription/crear-plan", apiControllers.CreatePlan)
api.Get("/dlocal/subscription/ver-plan/:planID", apiControllers.SeePlan)
api.Patch("/dlocal/subscription/actualizar-plan/:planID", apiControllers.UpdatedPlan)
api.Patch("/dlocal/subscription/plan/:planId/subscription/:subscriptionId/deactivate", apiControllers.DeactivatePlan)
}