products-payment-refacil-mcp
v1.1.3
Published
MCP Server para la API products-payment-refacil
Maintainers
Readme
products-payment-refacil MCP Server
Servidor MCP (Model Context Protocol) generado automáticamente para la API products-payment-refacil.
🚀 Características
- 11 herramientas generadas automáticamente desde la colección Postman
- Autenticación flexible: secretId con renovación automática de tokens
- Servidor HTTP con Fastify
- Protocolo MCP estándar para integración con IDEs
📋 Requisitos
- Node.js 20+
- npm o yarn
📦 Instalación desde NPM
Si este paquete está publicado en npm, puedes instalarlo globalmente:
# Instalación global (recomendado)
npm install -g products-payment-refacil-mcp
# Verificar instalación
products-payment-refacil-mcp --versionConfiguración Rápida en IDEs
Cursor
- Abrir Settings → Features → Model Context Protocol
- Click en "Edit Config"
- Agregar:
Windows: %APPDATA%\Cursor\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json
macOS/Linux: ~/.config/Cursor/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json
{
"mcpServers": {
"products-payment-refacil": {
"command": "products-payment-refacil-mcp",
"env": {
"BASE_URL": "https://products-payment-api.qa.refacil.co",
"SECRET_ID": "tu-secret-id",
"AUTH_ENDPOINT": "/auth/generate-token",
"TOKEN_CACHE_TTL": "3600"
}
}
}
}- Reiniciar Cursor
Claude Desktop
Editar archivo de configuración:
Windows: %APPDATA%\Claude\claude_desktop_config.json
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Linux: ~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"products-payment-refacil": {
"command": "products-payment-refacil-mcp",
"env": {
"BASE_URL": "https://products-payment-api.qa.refacil.co",
"SECRET_ID": "tu-secret-id",
"AUTH_ENDPOINT": "/auth/generate-token",
"TOKEN_CACHE_TTL": "3600"
}
}
}
}Reiniciar Claude Desktop.
Visual Studio Code
- Instalar extensión MCP for VS Code
- Editar
settings.json:
{
"mcp.servers": {
"products-payment-refacil": {
"command": "products-payment-refacil-mcp",
"env": {
"BASE_URL": "https://products-payment-api.qa.refacil.co",
"SECRET_ID": "tu-secret-id",
"AUTH_ENDPOINT": "/auth/generate-token",
"TOKEN_CACHE_TTL": "3600"
}
}
}
}- Recargar VS Code
🛠️ Instalación para Desarrollo
Si quieres contribuir o ejecutar desde el código fuente:
# Clonar el repositorio
git clone https://github.com/refacil/products-payment-refacil-mcp.git
cd products-payment-refacil-mcp
# Instalar dependencias
npm install
# Construir el proyecto
npm run build
# Iniciar en desarrollo
npm run dev
# Iniciar en producción
npm start🔧 Configuración
El servidor se configura mediante variables de entorno:
# Copiar archivo de ejemplo
cp .env.example .env
# Editar configuración
nano .envVariables de Entorno
| Variable | Descripción | Valor por defecto |
|----------|-------------|-------------------|
| PORT | Puerto del servidor | 3008 |
| ENVIRONMENT | Entorno de ejecución | development |
| BASE_URL | URL base de la API | https://products-payment-api.qa.refacil.co |
| SECRET_ID | ID secreto para autenticación | 0d92fc6aa394c13... (your secret id) |
| AUTH_ENDPOINT | Endpoint de autenticación | /auth/generate-token |
| TOKEN_CACHE_TTL | TTL del caché de tokens (segundos) | 3600 |
| MCP_TOKEN | Token de autenticación del MCP | mcp-secret-token-123 |
🐳 Docker
# Construir imagen
docker build -t products-payment-refacil-mcp .
# Ejecutar contenedor
docker run -p 3008:3008 products-payment-refacil-mcp
# O usar docker-compose
docker-compose up☸️ Kubernetes
# Aplicar manifests
kubectl apply -f k8s/
# Verificar despliegue
kubectl get pods -l app=products-payment-refacil-mcp🔌 Uso del MCP
Endpoints Disponibles
/mcp: Endpoint principal del protocolo MCP/health: Health check del servidor/tools: Lista de herramientas disponibles
Modo Multi-Usuario (HTTP)
El servidor MCP soporta múltiples usuarios simultáneamente en modo HTTP mediante el header X-Secret-ID.
¿Cómo funciona?
- Sin
X-Secret-ID: Usa elSECRET_IDdel archivo.env(modo single-user) - Con
X-Secret-ID: Usa el secretId proporcionado en el header (modo multi-user)
Ejemplo de uso:
# Usuario 1
curl -X POST http://localhost:3008/mcp \
-H "Authorization: Bearer mcp-token" \
-H "X-Secret-ID: user1-secret-id" \
-H "Content-Type: application/json" \
-d '{"method": "tools/call", "params": {...}}'
# Usuario 2
curl -X POST http://localhost:3008/mcp \
-H "Authorization: Bearer mcp-token" \
-H "X-Secret-ID: user2-secret-id" \
-H "Content-Type: application/json" \
-d '{"method": "tools/call", "params": {...}}'Ventajas del Modo Multi-Usuario:
- ✅ Cada usuario usa sus propias credenciales
- ✅ Tokens cacheados por secretId (mejor performance)
- ✅ Un solo servidor para múltiples clientes
- ✅ Compatible con modo single-user (fallback al .env)
🛠️ Herramientas Disponibles
auth_generate_token
Endpoints para autenticación y generación de tokens
Genera un token JWT usando el secretId del cliente. Este token debe ser usado en todas las peticiones posteriores junto con el secretId.
🔑 MANEJO GLOBAL DE ERRORES 401 - AUTENTICACIÓN:
⚠️ IMPORTANTE: Este endpoint es la base para resolver errores de autenticación en TODOS los endpoints protegidos.
🔄 Flujo de Resolución de Errores 401:
1. Cuando recibas un error 401 en cualquier endpoint:
{
"statusCode": 401,
"message": "Error de autenticación",
"date": 1754924521985,
"payload": {
"error": "AUTHENTICATION_ERROR",
"details": "Authentication failed",
"originalStatus": 401
}
}
2. Acciones inmediatas a realizar:
✅ Verificar que tus datos de autenticación sean correctos
✅ Regenerar un nuevo token usando este endpoint (
/auth/generate-token)✅ Reintentar la operación original con el nuevo token
💡 Mejor práctica: Implementa retry automático para errores 401 antes de fallar.
📋 Flujo de resolución detallado:
Token expirado: Llamar este endpoint con tu
secretIdToken inválido: Verificar formato y regenerar si es necesario
Secret ID incorrecto: Usar el mismo
secretIddel token originalHeaders faltantes: Incluir
Authorization: Bearer {token}yx-secret-id: {secretId}
🔄 Ejemplo de retry automático:
// Si recibes 401, regenera token y reintenta
if (error.statusCode === 401) {
const newToken = await generateNewToken(secretId);
// Reintentar operación original con nuevo token
}
🎯 Aplicable a todos los endpoints protegidos:
📦 Productos y categorías
🛒 Solicitudes de producto
💳 Creación de pagos
🔄 Reembolsos
Y cualquier otro endpoint que requiera autenticación
Respuesta exitosa (200):
{
"statusCode": 200,
"message": "Token generado exitosamente",
"date": 1704441600000,
"payload": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 360
}
}
Campos del payload:
token(string): Token JWT para autenticaciónexpiresIn(number): Tiempo de expiración en minutos (ej: 360 = 6 horas)
🤖 Automatización en Postman:
Este endpoint incluye un script automático que:
✅ Guarda el token en la variable
{{token}}de la colección✅ Valida que la respuesta sea exitosa
✅ Muestra logs informativos en la consola
✅ Maneja errores automáticamente
Resultado: Los siguientes endpoints usarán automáticamente el token generado.
Respuesta de error (400):
{
"statusCode": 400,
"message": "Datos de entrada inválidos",
"date": 1704441600000,
"payload": {
"error": "VALIDATION_ERROR"
}
}
Parámetros:
| Name | Type | Description | | --- | --- | --- | | secretId | string | Campo del body: secretId |
status
Verificación del estado de la API y conectividad con servicios
Verifica el estado de la API y servicios internos.
Respuesta exitosa (200):
{
"statusCode": 200,
"message": "API funcionando correctamente",
"date": 1704441600000,
"payload": {
"status": "ok",
"timestamp": "2024-01-15T10:30:45.123Z",
"uptime": 3600,
"services": "connected",
"version": "1.0.0",
"environment": "development"
}
}Respuesta con error (503):
{
"statusCode": 503,
"message": "API con problemas de conectividad",
"date": 1704441600000,
"payload": {
"status": "error",
"timestamp": "2024-01-15T10:30:45.123Z",
"uptime": 3600,
"services": "disconnected",
"error": "Connection timeout",
"version": "1.0.0",
"environment": "development"
}
}products_categories
Endpoints para consultar categorías y productos disponibles
Retorna las categorías de productos que el cliente puede acceder.
Headers requeridos:
Authorization: Bearer {token}x-secret-id: {secretId}
Respuesta exitosa (200):
{
"statusCode": 200,
"message": "Categorías obtenidas exitosamente",
"date": 1704441600000,
"payload": [
{
"id": 1,
"name": "Recargas",
"description": "Recargas de celular",
"enabled": true
},
{
"id": 2,
"name": "Pines",
"description": "Pines de servicios",
"enabled": true
}
]
}Respuesta de error (401):
{
"statusCode": 401,
"message": "Error de autenticación",
"date": 1754924521985,
"payload": {
"error": "AUTHENTICATION_ERROR",
"details": "Authentication failed",
"originalStatus": 401
}
}Parámetros:
| Name | Type | Description | | --- | --- | --- | | secretId | string | Header personalizado: x-secret-id |
products_by_cus_and_category
Endpoints para consultar categorías y productos disponibles
Retorna los productos filtrados por cliente y categoría.
Headers requeridos:
Authorization: Bearer {token}x-secret-id: {secretId}
Body (JSON):
categoryId(number, requerido): ID de la categoríalimit(number, opcional): Número máximo de productos (default: 10)offset(number, opcional): Número de productos a omitir (default: 0)searchText(string, opcional): Texto para buscar en productos
Ejemplo de Request:
{
"categoryId": 120,
"limit": 10,
"offset": 0,
"searchText": "BANCAMIA"
}
Respuesta exitosa (200):
{
"statusCode": 200,
"message": "Productos obtenidos exitosamente",
"date": 1754924866513,
"payload": {
"products": [
{
"id": 1,
"name": "BANCAMIA",
"amount": null,
"meta": {
"partial": "false",
"overdue": "true",
"type": "RECAUDO CODIGO BARRAS",
"fnArgs": "1,25,44"
},
"description": null,
"image_url": "bills/52368.png",
"query_type": "BILLData",
"sell_type": "Bill",
"agreement": "7700000000001",
"template_data_request": {
"cellphone": "3208888888",
"reference": "123456789"
}
},
{
"id": 16045,
"name": "LIBERACION DE CUPO BANCAMIA",
"amount": null,
"meta": {
"fnArgs": 43278,
"categoryId": 43,
"form": [
{
"active": true,
"type": "text",
"label": "Numero De Documento",
"placeholder": "Ingrese su NUMERO DE DOCUMENTO",
"legend": "NUMERO DE DOCUMENTO",
"value": "reference",
"required": true
},
{
"active": true,
"type": "text",
"label": "Codigo De Corresponsal",
"placeholder": "Ingrese su CODIGO DE CORRESPONSAL",
"legend": "CODIGO DE CORRESPONSAL",
"value": "reference2",
"required": true
}
]
},
"description": null,
"image_url": "bill.png",
"query_type": "BILLData",
"sell_type": "Bill",
"agreement": "2010910",
"template_data_request": {
"cellphone": "3208888888",
"reference": "123456789",
"reference2": "123548"
}
}
]
}
}
📋 Datos para Solicitud de Producto:
Esta respuesta contiene información clave que se debe usar para crear una solicitud de producto:
id: ID del producto (se usa enproduct-requests/create)query_type: Tipo de consulta (se usa enproduct-requests/create)sell_type: Tipo de venta (se usa enproduct-requests/create)agreement: Acuerdo del producto (se puede usar enproduct-requests/create)template_data_request: Template para el campodataenproduct-requests/createamount: Monto del producto (puede sernullpara productos con consulta previa)
Notas importantes:
template_data_request: Es un template que muestra la estructura de datos que se debe enviar en el campodatacuando se crea una solicitud de producto para esta categoría específica. Los valores son ejemplos y deben ser reemplazados con datos reales.datadinámico: El campodataen las solicitudes de producto es dinámico y su estructura depende de la categoría. Cada categoría puede requerir diferentes campos y formatos.Objeto
meta: Es dinámico y puede contener información diferente para cada producto. No se define una estructura fija debido a su naturaleza variable.Tipos de producto: Los productos pueden tener diferentes tipos de
query_typeysell_typesegún la categoría.Campo
amount: Puede sernullpara productos que requieren consulta previa (como facturas), o contener un valor específico para productos de pago directo.
🔄 Flujo de Uso:
Consultar productos con este endpoint
Seleccionar producto de la lista
Usar datos del producto para crear solicitud en
product-requests/createUsar template_data_request como referencia para el campo
dataDeterminar amount según el tipo de producto (null para consulta previa, valor para pago directo)
Respuesta de error (401):
{
"statusCode": 401,
"message": "Error de autenticación",
"date": 1754924521985,
"payload": {
"error": "AUTHENTICATION_ERROR",
"details": "Authentication failed",
"originalStatus": 401
}
}
Parámetros:
| Name | Type | Description | | --- | --- | --- | | secretId | string | Header personalizado: x-secret-id | | categoryId | number | Campo del body: categoryId | | limit | number | Campo del body: limit | | offset | number | Campo del body: offset | | searchText | null | Campo del body: searchText |
product_requests_create
Endpoints para crear solicitudes de productos
Crea una nueva solicitud de producto. Esta es la primera etapa del proceso de pago.
📋 Relación con Endpoint de Productos:
Los datos para crear esta solicitud provienen principalmente del endpoint products/by-customer-and-category:
category_id: ID de la categoría (del endpoint de productos)id: ID del producto seleccionado (del campoiddel producto)query_type: Tipo de consulta (del campoquery_typedel producto)sell_type: Tipo de venta (del camposell_typedel producto)data: Estructura basada entemplate_data_requestdel productoagreement: Acuerdo del producto (del campoagreementdel producto)
Headers requeridos:
Authorization: Bearer {token}x-secret-id: {secretId}
Body (JSON):
category_id(number, requerido): ID de la categoría (obtenido del endpoint de productos)id(number, requerido): ID del producto seleccionado (obtenido del campoiddel producto)amount(number|null, opcional): Monto del producto (ver reglas de uso)data(object, requerido): Datos específicos del producto basados entemplate_data_requestdel productoquery_type(string, requerido): Tipo de consulta del producto (PHONE_QUERY, PLATE_QUERY, etc.)sell_type(string, requerido): Tipo de venta del producto (DIRECT_SALE, QUERY_FIRST)agreement(string, opcional): Aceptación de términos (puede venir del campoagreementdel producto)customer_id(string, opcional): Identificador único del usuario o dispositivocart_id (string, opcional): identificador del carrito de compra al que se quiere agregar la solicictud de producto
📋 Reglas para el campo customer_id:
🔑 Con Usuario Registrado:
Enviar: ID único del usuario en tu sistema
Ejemplo: "customer_id": "user_12345" o "customer_id": "cliente_abc123"
Uso: Para seguimiento, historial de transacciones, y análisis de comportamiento
📱 Sin Usuario Registrado (Frontend Anónimo):
Enviar: Identificador único del dispositivo o sesión
Ejemplos:
Device ID: "customer_id": "device_abc123def456"
Session ID: "customer_id": "session_xyz789"
Browser Fingerprint: "customer_id": "browser_fp_123"
Local Storage ID: "customer_id": "local_456"
Uso: Para seguimiento de transacciones anónimas y análisis de patrones
💡 Mejores Prácticas:
Consistencia: Usar el mismo formato de ID en toda la aplicación
Persistencia: Mantener el ID durante toda la sesión del usuario
Privacidad: No incluir información personal identificable en el ID
Longitud: Recomendado entre 10-50 caracteres
🔄 Flujo de Implementación:
1. Usuario Registrado:
const customerId = user.id || user.customerId || user.uuid;
// customerId = "user_12345"
2. Usuario Anónimo:
// Generar ID único del dispositivo
const deviceId = generateDeviceId(); // "device_abc123def456"
const sessionId = generateSessionId(); // "session_xyz789"
const customerId = deviceId || sessionId;
3. Envío en Request:
{
"category_id": 120,
"id": 1952,
"amount": null,
"data": {
"cellphone": "3208385715",
"reference": "123456"
},
"query_type": "BILLData",
"sell_type": "Bill",
"agreement": "2010910",
"customer_id": "device_abc123def456"
}
📊 Beneficios del Tracking:
Análisis: Comportamiento de usuarios anónimos vs registrados
Seguimiento: Transacciones por dispositivo/sesión
Fraude: Detección de patrones sospechosos
UX: Mejora de la experiencia del usuario
💰 Reglas para el campo amount****:
amount: null: Para productos que generan consulta previa (ej: facturas, servicios con tarifas variables)amount: valor: Para productos de pago directo con precio fijo (ej: recargas, productos con precio conocido)Validación interna: El sistema valida internamente y solo toma el amount del cliente cuando aplica
🔄 Flujo de Creación de Solicitud:
1. Consultar productos:
POST /products/by-customer-and-category
{
"categoryId": 120,
"limit": 10,
"offset": 0,
"searchText": "ACACIAS"
}
2. Respuesta del endpoint de productos:
{
"statusCode": 200,
"message": "Productos obtenidos exitosamente",
"date": 1754945365966,
"payload": {
"template_data_request": {
"cellphone": "3208888888",
"reference": "123456789"
},
"products": [
{
"id": 1952,
"name": "ACACIAS DEL CASTILLO CONJUNTO 3 CALI",
"amount": null,
"meta": {
"fnArgs": 20363,
"categoryId": 44,
"form": [
{
"active": true,
"type": "text",
"label": "Numero De Casa",
"placeholder": "Ingrese su Numero de Casa",
"legend": "Numero de Casa",
"value": "reference",
"required": true
}
]
},
"description": null,
"image_url": "bill.png",
"query_type": "BILLData",
"sell_type": "Bill",
"agreement": "2010910"
}
]
}
}
3. Crear solicitud usando datos del producto:
{
"category_id": 120,
"id": 1952,
"amount": null,
"data": {
"cellphone": "3208385715",
"reference": "123456"
},
"query_type": "BILLData",
"sell_type": "Bill",
"agreement": "2010910"
}
Respuesta exitosa (201):
{
"statusCode": 201,
"message": "Solicitud creada exitosamente",
"date": 1754945173974,
"payload": {
"cartId": "25894df4-a599-4d28-a2f2-bb5b8faa678b",
"requestId": "15974df4-a599-4d28-a2f2-bb5b8faa678b",
"amount": 9999,
"paymentMethods": [
{
"id": 304135,
"name": "Transfiya",
"internal_name": "AppFormTransfiya",
"description": "Transfiya",
"categoryid": 176,
"image": "transfiya.png",
"template_data_request": {
"cellphone": "3208888888"
},
"cost": 500
},
{
"id": 56,
"name": "PSE",
"internal_name": "AppFormPse",
"description": "PSE",
"categoryid": 61,
"image": "pse.png",
"template_data_request": {
"name": "Juan Perez",
"email": "[email protected]",
"bankId": "1007",
"cellphone": "3208888888",
"typePerson": "0",
"documentType": "CC",
"documentNumber": "1032222222"
},
"cost": 500
},
{
"id": 88,
"name": "Nequi",
"internal_name": "AppFormNequiCargar",
"description": "Botón de pago nequi",
"categoryid": 176,
"image": "nequi-responsive.png",
"template_data_request": {
"cellphone": "3208888888"
},
"cost": 500
},
{
"id": 53,
"name": "Bancolombia",
"internal_name": "AppFormDefault",
"description": "Botón de pago Bancolombia",
"categoryid": 177,
"image": "bancolombia.png",
"template_data_request": {
"name": "Juan Perez",
"email": "[email protected]",
"bankId": "1007",
"cellphone": "3208888888",
"typePerson": "0",
"documentType": "CC",
"documentNumber": "1032222222"
},
"cost": 500
},
{
"id": 89,
"name": "Daviplata",
"internal_name": "AppFormDaviplataPse",
"description": "Botón de pago daviplata",
"categoryid": 176,
"image": "daviplata.png",
"template_data_request": {
"cellphone": "3208888888"
},
"cost": 500
}
]
}
}
📋 Datos de la Respuesta:
**
cartId:**Identificador unico del carrito al que se asigno la solicitud de producto (se usa en/products-payment/create)requestId: Identificador único de la solicitud de productoamount: Monto que se debe pagar (calculado por el sistema)additionalData: Datos adicionales del producto (solo si aplica)paymentMethods: Array con medios de pago disponibles
💳 Información de Medios de Pago:
Cada medio de pago contiene:
id: ID del medio de pago (se usa en/products-payment/create)name: Nombre del medio de pagocategoryid: ID de la categoría del medio de pago (se usa en/products-payment/create)template_data_request: Template para el campodataen/products-payment/createcost: Costo del medio de pago para esta transacción (0 = sin costo, >0 = con costo)
🔄 Siguiente Paso - Crear Pago:
Con la respuesta de este endpoint, puedes proceder a crear el pago usando /products-payment/create enviando:
cartId: Del campocartIdde la respuestaid: Del campoiddel medio de pago seleccionadocategoryid: Del campocategoryiddel medio de pago seleccionadodata: Basado entemplate_data_requestdel medio de pago seleccionado
Respuesta de error (403):
{
"statusCode": 403,
"message": "Acceso denegado - Categoría no permitida",
"date": 1704441600000,
"payload": {
"error": "CATEGORY_NOT_ALLOWED"
}
}
Respuesta de error (401):
{
"statusCode": 401,
"message": "Error de autenticación",
"date": 1754924521985,
"payload": {
"error": "AUTHENTICATION_ERROR",
"details": "Authentication failed",
"originalStatus": 401
}
}
Parámetros:
| Name | Type | Description | | --- | --- | --- | | secretId | string | Header personalizado: x-secret-id | | category_id | number | Campo del body: category_id | | id | number | Campo del body: id | | amount | null | Campo del body: amount | | data | object | Campo del body: data | | query_type | string | Campo del body: query_type | | sell_type | string | Campo del body: sell_type | | agreement | string | Campo del body: agreement | | customer_id | string | Campo del body: customer_id |
product_requests_status
Endpoints para crear solicitudes de productos
Consulta el estado actual de una solicitud de producto individual por su requestId o de un carrito completo por su cartId.
🔒 Seguridad:
Este endpoint incluye validación de seguridad que garantiza que solo puedas consultar el estado de las solicitudes/carritos que creaste.
📋 Parámetros:
Debes proporcionar uno de los siguientes parámetros (no ambos obligatorios):
requestId(string, opcional): ID único de la solicitud de producto individual (UUID)cartId(string, opcional): ID único del carrito de compras (UUID)
Headers requeridos:
Authorization: Bearer {token}x-secret-id: {secretId}
📊 Estados Posibles:
🔄 Estados de Proceso:
INITIATED: Solicitud creada y en proceso de validaciónQUERIED: Solicitud consultada exitosamente en API externaPENDING: Solicitud lista para procesar el pagoWAITING_PAYMENT: Esperando confirmación de pago del proveedor
❌ Estados de Error:
INVALID: Datos inválidos en la validación del productoDISCARDED: Usuario decidió no continuar con el pago del productoEXPIRED: La solicitud de producto ha expirado
✅ Estados Finales:
SOLD: Pago exitoso y producto vendidoREFUNDED: Pago exitoso pero venta fallóCANCELLED: Usuario no realizó el pago del producto, el proveedor no confirmó el pago
🔄 Flujo de Uso - Producto Individual:
1. Consultar estado usando requestId:
POST /product-requests/status
{
"requestId": "15974df4-a599-4d28-a2f2-bb5b8faa678b"
}
2. Respuesta del estado (producto individual):
{
"statusCode": 200,
"message": "Estado consultado exitosamente",
"date": 1754945173974,
"payload": {
"id": "15974df4-a599-4d28-a2f2-bb5b8faa678b",
"cartId": "22884df4-a599-4d28-a2f2-bb5b8faa352b",
"status": "SOLD",
"description": "Pago exitoso y producto vendido",
"details": {
"id": 123,
"categoryId": 1,
"amount": 15000,
"data": {...}
},
"urlSummary": "https://example.com/summary/ref-xyz"
}
}
🛒 Flujo de Uso - Carrito Completo:
1. Consultar estado usando cartId:
POST /product-requests/status
{
"cartId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
2. Respuesta del estado (carrito completo):
{
"statusCode": 200,
"message": "Estado consultado exitosamente",
"date": 1754945173974,
"payload": {
"cartId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "COMPLETED",
"totalAmount": 27000,
"itemsCount": 2,
"products": [
{
"id": "20db2a40-77b7-11f0-85d4-7d462019c921",
"status": "SOLD",
"description": "Pago exitoso y producto vendido",
"details": {
"id": 123,
"categoryId": 1,
"amount": 15000,
"data": {...}
},
"urlSummary": "https://example.com/summary/ref-xyz"
},
{
"id": "30db2a40-77b7-11f0-85d4-7d462019c922",
"status": "SOLD",
"description": "Pago exitoso y producto vendido",
"details": {
"id": 456,
"categoryId": 2,
"amount": 12000,
"data": {...}
},
"urlSummary": "https://example.com/summary/ref-xyz"
}
]
}
}
🔐 Validación de Seguridad:
Usuario autenticado: Solo puedes consultar solicitudes/carritos creados con tu
secretIdID válido: Debe ser un UUID válido
Acceso restringido: No puedes consultar solicitudes/carritos de otros usuarios
📱 Casos de Uso:
Frontend: Mostrar estado actual al usuario
Backend: Seguimiento de transacciones
Webhooks: Complementar notificaciones asíncronas
Auditoría: Verificar estado de solicitudes
Carrito: Consultar estado de múltiples productos a la vez
Respuesta de error (400 - Parámetros faltantes):
{
"statusCode": 400,
"message": "Debe proporcionar requestId o cartId",
"date": 1754945173974,
"payload": {
"error": "MISSING_REQUIRED_PARAMETER"
}
}
Respuesta de error (404):
{
"statusCode": 404,
"message": "Solicitud de producto no encontrada",
"date": 1754945173974,
"payload": {
"error": "PRODUCT_REQUEST_NOT_FOUND"
}
}
💡 Tip para Testing:
Usa la variable {{lastCartId}} o {{lastRequestId}} que se llenan automáticamente al crear solicitudes.
Parámetros:
| Name | Type | Description | | --- | --- | --- | | secretId | string | Header personalizado: x-secret-id | | requestId | string | Campo del body: requestId | | cartId | string | Campo del body: cartId |
product_requests_discard
Endpoints para crear solicitudes de productos
Descarta una solicitud de producto individual por su requestId o un carrito completo con todos sus productos por su cartId.
🔒 Seguridad:
Este endpoint incluye validación de seguridad que garantiza que solo puedas descartar solicitudes/carritos que creaste.
📋 Parámetros:
Debes proporcionar uno de los siguientes parámetros (no ambos obligatorios):
requestId(string, opcional): ID único de la solicitud de producto individual (UUID)cartId(string, opcional): ID único del carrito de compras (UUID)
Headers requeridos:
Authorization: Bearer {token}x-secret-id: {secretId}
⚠️ Condiciones para Descartar:
Para producto individual:
El producto debe estar en estado
PENDINGDebe pertenecer al usuario autenticado
Para carrito completo:
El carrito debe estar en estado
ACTIVEDebe pertenecer al usuario autenticado
Debe tener al menos un producto en estado
PENDING
🔄 Flujo de Uso - Producto Individual:
1. Descartar producto usando requestId:
POST /product-requests/discard
{
"requestId": "15974df4-a599-4d28-a2f2-bb5b8faa678b"
}
2. Respuesta (producto individual):
{
"statusCode": 200,
"message": "Solicitud descartada exitosamente",
"date": 1754945173974,
"payload": {
"requestId": "15974df4-a599-4d28-a2f2-bb5b8faa678b",
"status": "DISCARDED"
}
}
🛒 Flujo de Uso - Carrito Completo:
1. Descartar carrito usando cartId:
POST /product-requests/discard
{
"cartId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
2. Respuesta (carrito completo):
{
"statusCode": 200,
"message": "Carrito descartado exitosamente (2 productos)",
"date": 1754945173974,
"payload": {
"cartId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "CANCELLED",
"discardedCount": 2,
"products": [
{
"requestId": "20db2a40-77b7-11f0-85d4-7d462019c921",
"status": "DISCARDED"
},
{
"requestId": "30db2a40-77b7-11f0-85d4-7d462019c922",
"status": "DISCARDED"
}
]
}
}
🔐 Validación de Seguridad:
Usuario autenticado: Solo puedes descartar solicitudes/carritos creados con tu
secretIdID válido: Debe ser un UUID válido
Acceso restringido: No puedes descartar solicitudes/carritos de otros usuarios
📱 Casos de Uso:
Usuario cambia de opinión: Descarta producto antes de pagar
Datos incorrectos: Usuario ingresó datos equivocados
Monto muy alto: Usuario no esperaba ese monto
Limpiar carrito: Descartar todos los productos de una vez
Empezar de nuevo: Usuario quiere crear un nuevo carrito desde cero
Respuesta de error (400 - Parámetros faltantes):
{
"statusCode": 400,
"message": "Debe proporcionar requestId o cartId",
"date": 1754945173974,
"payload": {
"error": "MISSING_REQUIRED_PARAMETER"
}
}
Respuesta de error (400 - Producto no está en PENDING):
{
"statusCode": 500,
"message": "Product request is not pending",
"date": 1754945173974,
"payload": {
"error": "Product request is not pending"
}
}
Respuesta de error (400 - Carrito no está ACTIVE):
{
"statusCode": 500,
"message": "Cart is not active",
"date": 1754945173974,
"payload": {
"error": "Cart is not active"
}
}
Respuesta de error (404):
{
"statusCode": 500,
"message": "Product request not found",
"date": 1754945173974,
"payload": {
"error": "Product request not found"
}
}
💡 Tip para Testing:
Usa la variable {{lastCartId}} o {{lastRequestId}} que se llenan automáticamente al crear solicitudes.
Parámetros:
| Name | Type | Description | | --- | --- | --- | | secretId | string | Header personalizado: x-secret-id | | requestId | string | Campo del body: requestId | | cartId | string | Campo del body: cartId |
products_banks_pse
Endpoints para crear pagos de productos
Obtiene la lista de bancos disponibles para pagos PSE.
📋 Uso:
Este endpoint es esencial para pagos PSE, ya que el usuario debe seleccionar su banco antes de crear el pago.
Headers requeridos:
Authorization: Bearer {token}x-secret-id: {secretId}
Respuesta exitosa (200):
Array de bancos con
id,nameeimageCada banco incluye el ID requerido para el campo
bankIden pagos PSE
Relación con otros endpoints:
El
iddel banco se usa endata.bankIddel endpointPOST /products-payment/createcuando el medio de pago es PSEdocumentType se debe asignar unas de estas opciones:
CC = Cedula de ciudadania
CE = Cedula de extranjeria
NIT = Nit
typePerson se debe asignar una de estas opciones:
0 = Natural
1 = Juridico
Ejemplo de uso en pago PSE:
{
"requestId": "uuid-request",
"id": 56, // ID del medio de pago PSE
"categoryid": 61,
"data": {
"bankId": "1007", // ID obtenido de este endpoint
"name": "Juan Perez",
"email": "[email protected]",
"cellphone": "3001234567",
"typePerson": "0",
"documentType": "CC",
"documentNumber": "1234567890"
}
}
Parámetros:
| Name | Type | Description | | --- | --- | --- | | secretId | string | Header personalizado: x-secret-id |
products_payment_create
Endpoints para crear pagos de productos
Campo cartId (OBLIGATORIO): ID del carrito obtenido de
/product-requests/createFilosofía "Always Cart": Todos los productos (individuales o múltiples) usan carrito
Ejemplo correcto:
{
"cartId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"id": 304135,
"categoryid": 176,
"data": { "cellphone": "3051000002" },
"returnUrl": "https://mi-app.com/return",
"webhookUrl": "https://mi-app.com/webhook"
}
Crea el pago de un producto usando el medio de pago seleccionado. Esta es la etapa final del proceso de pago.
📋 Relación con Endpoint de Solicitud:
Los datos para crear este pago provienen de la respuesta del endpoint product-requests/create:
cartId: Del campocartIdde la respuesta de solicitudid: Del campoiddel medio de pago seleccionadocategoryid: Del campocategoryiddel medio de pago seleccionadodata: Basado entemplate_data_requestdel medio de pago seleccionado
Headers requeridos:
Authorization: Bearer {token}x-secret-id: {secretId}
Body (JSON):
cartId(string, requerido): ID de la solicitud obtenido deproduct-requests/createid(number, requerido): ID del medio de pago seleccionadocategoryid(number, requerido): ID de la categoría del medio de pagodata(object, requerido): Datos del pagador basados entemplate_data_requestdel medio de pagoreturnUrl(string, opcional): URL de retorno después del pagowebhookUrl(string, opcional): URL para notificaciones de estado del pago
🔄 Flujo de Creación de Pago:
1. Respuesta de creación de solicitud:
{
"statusCode": 201,
"message": "Solicitud creada exitosamente",
"date": 1754945173974,
"payload": {
"cartId": "15974df4-a599-4d28-a2f2-bb5b8faa678b",
"requestId": "25884df4-a599-4d28-a2f2-bb5b8faa788a",
"amount": 9999,
"paymentMethods": [
{
"id": 304135,
"name": "Transfiya",
"internal_name": "AppFormTransfiya",
"description": "Transfiya",
"categoryid": 176,
"image": "transfiya.png",
"template_data_request": {
"cellphone": "3208888888"
}
},
{
"id": 56,
"name": "PSE",
"internal_name": "AppFormPse",
"description": "PSE",
"categoryid": 61,
"image": "pse.png",
"template_data_request": {
"name": "Juan Perez",
"email": "[email protected]",
"bankId": "1007",
"cellphone": "3208888888",
"typePerson": "0",
"documentType": "CC",
"documentNumber": "1032222222"
}
}
]
}
}
2. Crear pago con Transfiya (ejemplo):
{
"cartId": "15974df4-a599-4d28-a2f2-bb5b8faa678b",
"id": 304135,
"categoryid": 176,
"data": {
"cellphone": "3051000002"
},
"returnUrl": "https://plataforma.refacil.co/#/",
"webhookUrl": "https://webhook.site/4083efe6-7809-40ac-bbaa-9271d4a0c853"
}
3. Crear pago con PSE (ejemplo):
{
"cartId": "15974df4-a599-4d28-a2f2-bb5b8faa678b",
"id": 56,
"categoryid": 61,
"data": {
"name": "Juan Perez",
"email": "[email protected]",
"bankId": "1007",
"cellphone": "3051000002",
"typePerson": "0",
"documentType": "CC",
"documentNumber": "1032222222"
},
"returnUrl": "https://plataforma.refacil.co/#/",
"webhookUrl": "https://webhook.site/4083efe6-7809-40ac-bbaa-9271d4a0c853"
}
💳 Estructura de Datos por Medio de Pago:
Medios Simples (Solo celular):
Transfiya, Nequi, Daviplata
Campo data:
{"cellphone": "3051000002"}
Medios Bancarios (Datos completos):
PSE, Bancolombia
Campo data:
{"name", "email", "bankId", "cellphone", "typePerson", "documentType", "documentNumber"}
🔗 URLs de Configuración:
returnUrl: URL donde la pasarela de pago redirige al usuario después del pagowebhookUrl: URL donde se envían notificaciones de cambios de estado del pago
Respuesta exitosa (201):
{
"statusCode": 201,
"message": "Recurso de pago creado exitosamente",
"date": 1755026415574,
"payload": {
"cartId": "88d0b575-80f4-4f9b-af8f-5d09ec2cb877",
"url": "https://mf-core.refacil.co/refacilpay/resumen/62/5ff47b60-77b1-11f0-b5e9-6f43bd8928c1",
"reference": "5ff47b60-77b1-11f0-b5e9-6f43bd8928c1",
"amount": 19000
"expiresIn": "2025-08-13T07:20:15.038Z"
}
}
📋 Datos de la Respuesta:
reference: Identificador único del pagourl: URL para redirigir al usuario al gateway de pagosamount: Monto a pagarcartId: ID del carrito que tiene las solicitudes de productos creadasexpiresIn: Fecha y hora de expiracion del recurso de pago
🔄 Siguiente Paso - Procesamiento:
Redirigir usuario: Usar
urlpara enviar al usuario al gateway de pagosProcesar pago: El usuario completa el pago en el gateway
Recibir notificaciones: El
webhookUrlrecibe actualizaciones de estadoRedirigir retorno: El usuario regresa a
returnUrldespués del pago
Respuesta de error (400):
{
"statusCode": 400,
"message": "Datos de entrada inválidos",
"date": 1754945173974,
"payload": {
"error": "INVALID_INPUT_DATA",
"details": "El campo data no coincide con el template del medio de pago"
}
}
Respuesta de error (404):
{
"statusCode": 404,
"message": "Solicitud no encontrada",
"date": 1754945173974,
"payload": {
"error": "REQUEST_NOT_FOUND",
"details": "La solicitud especificada no existe o ha expirado"
}
}
Respuesta de error (401):
{
"statusCode": 401,
"message": "Error de autenticación",
"date": 1754924521985,
"payload": {
"error": "AUTHENTICATION_ERROR",
"details": "Authentication failed",
"originalStatus": 401
}
}
Parámetros:
| Name | Type | Description | | --- | --- | --- | | secretId | string | Header personalizado: x-secret-id | | cartId | string | Campo del body: cartId | | id | number | Campo del body: id | | categoryid | number | Campo del body: categoryid | | data | object | Campo del body: data | | returnUrl | string | Campo del body: returnUrl | | webhookUrl | string | Campo del body: webhookUrl |
products_refund_create
Endpoints para crear reembolsos
Crea un reembolso para un request específico. Usado cuando un pago fue exitoso pero la venta falló.
Headers requeridos:
Authorization: Bearer {token}x-secret-id: {secretId}
Body (JSON):
requestId(string, requerido): ID del request a reembolsarkeyBreB(string, requerido): Clave BreB del cliente (número de celular u otro identificador)email(string, requerido): Email del clientewebhookUrl(string, requerido): URL para notificaciones del reembolso
Respuesta exitosa (201):
{
"statusCode": 201,
"message": "Refund creado exitosamente",
"date": 1704441600000,
"payload": {
"requestId": "0aaef286-867c-486d-bf98-3a16eeabad5c",
"reference": "00af1036-2473-4014-9c50-4d350ccc2560",
"keyBreB": "3051000002",
"amount": "9999.00"
}
}
Respuesta de error (400):
{
"statusCode": 400,
"message": "Datos de entrada inválidos",
"date": 1704441600000,
"payload": {
"error": "INVALID_REQUEST_ID",
"requestId": "req-uuid-1234-5678-9abc"
}
}
Respuesta de error (401):
{
"statusCode": 401,
"message": "Error de autenticación",
"date": 1754924521985,
"payload": {
"error": "AUTHENTICATION_ERROR",
"details": "Authentication failed",
"originalStatus": 401
}
}
Parámetros:
| Name | Type | Description | | --- | --- | --- | | secretId | string | Header personalizado: x-secret-id | | requestId | string | Campo del body: requestId | | keyBreB | string | Campo del body: keyBreB | | email | string | Campo del body: email | | webhookUrl | string | Campo del body: webhookUrl |
docs_cli_web_documentation
Guía para implementar webhooks del lado del cliente para recibir notificaciones de pagos y reembolsos
📋 IMPORTANTE: Implementación de Webhooks del Cliente Requerida
Este NO es un endpoint de la API, sino una guía para implementar webhooks en TU sistema.
🔄 Lo que Necesitas Implementar:
Para recibir notificaciones de estado de pagos y reembolsos, DEBES implementar endpoints webhook en tu sistema:
1. Webhook de Estado de Pago
URL: Tu endpoint (ej.,
https://tu-dominio.com/webhooks/estado-pago)Método: POST
Propósito: Recibir actualizaciones de estado de pago (SOLD, REFUNDED, CANCELLED)
2. Webhook de Estado de Reembolso
URL: Tu endpoint (ej.,
https://tu-dominio.com/webhooks/estado-reembolso)Método: POST
Propósito: Recibir actualizaciones de estado de reembolso (CONFIRMED, FAILED)
🔑 Cómo Configurar:
Implementa endpoints webhook en tu sistema
Proporciona las URLs en el campo
webhookUrlal crear pagos/reembolsosValida las firmas usando HMAC-SHA1 y tu
hashKey
📚 Documentación Disponible:
Guía de Webhooks del Cliente:
client-webhook-documentation.md Solicitar a soporte si es necesarioEjemplos de Código:
webhook-examples.md Solicitar a soporte si es necesarioValidación de Firmas: Algoritmo HMAC-SHA1
⚠️ Requisitos de Seguridad:
Tu webhook debe responder con HTTP 200-299 en menos de 10 segundos
Valida el campo
signatureusando HMAC-SHA1Solicita tu
hashKeyal equipo de soporte📞 Proceso de obtención:
1. Contacta al equipo de soporte para solicitar integración
2. Recibirás tu `hashKey` único para validación de firmas
3. Configura este `hashKey` de forma segura en tu aplicación
Ejemplo de payload webhook que recibirás:
Notificacion de Rembolso
Datos a firmar: `refundId-amount-timestamp`
Estados Posibles:
CONFIRMED: Reembolso confirmado y procesado
FAILED: Reembolso falló
{
"refundId": "ref-uuid-1234-5678-9abc",
"reference": "refund_ref_987654321",
"status": "CONFIRMED",
"amount": 9999,
"method": "BRE-B",
"destination": "3208385715",
"requestId": "15974df4-a599-4d28-a2f2-bb5b8faa678b",
"timestamp": "2024-01-15T10:35:45.123Z",
"signature": "b9c8d7e6f5g4h3i2j1k0l9m8n7o6p5q4r3s2t1u0v9w8x7y6z5"
}
Notificacion de Pago
Datos a firmar: `cartId-amount-timestamp`
Estados del Carrito (campo `status` de nivel superior):
- COMPLETED: Todos los productos del carrito fueron vendidos exitosamente
- PARTIALLY_REFUNDED: Algunos productos vendidos, otros reembolsados
- FULLY_REFUNDED: Todos los productos del carrito fueron reembolsados
- CANCELLED: Pago no confirmado por el proveedor
Estados de Producto Individual (campo `products[].status`):
- SOLD: Producto vendido exitosamente
- REFUNDED: Pago exitoso pero venta del producto falló, se inició reembolso
- CANCELLED: Pago no confirmado para este producto
{
"cartId": "15974df4-a599-4d28-a2f2-bb5b8faa678b",
"reference": "topup_ref_123456789",
"status": "COMPLETED",
"amount": 20000,
"products":[
{
"requestId": "85774df4-a599-4d28-a2f2-bb5b8faa678b",
"status": "SOLD",
"amount": 10000,
"billData": {
"customerName": "Juan Perez",
"dueDate": "2024-02-15",
"totalAmount": 10000
}
},
{
"requestId": "95774df4-b599-5d28-c2f2-cc5b8faa678c",
"status": "SOLD",
"amount": 10000,
"billData": {
"customerName": "Maria Garcia",
"dueDate": "2024-02-20",
"totalAmount": 10000
}
}
],
"timestamp": "2024-01-15T10:30:45.123Z",
"signature": "a8b7c9d2e3f4g5h6i7j8k9l0m1n2o3p4q5r6s7t8u9v0w1x2y3z4"
}
Ejemplo con estado PARTIALLY_REFUNDED:
{
"cartId": "25974df4-b599-5d28-c2f2-cc5b8faa678d",
"reference": "topup_ref_987654321",
"status": "PARTIALLY_REFUNDED",
"amount": 20000,
"products": [
{
"requestId": "a5774df4-c599-6d28-d2f2-dd5b8faa678e",
"status": "SOLD",
"amount": 10000,
"billData": {
"customerName": "Carlos Lopez",
"dueDate": "2024-02-18",
"totalAmount": 10000
}
},
{
"requestId": "b5774df4-d599-7d28-e2f2-ee5b8faa678f",
"status": "REFUNDED",
"amount": 10000,
"errorMessage": "Venta falló en proveedor externo"
}
],
"timestamp": "2024-01-15T10:35:45.123Z",
"signature": "c9d8e7f6g5h4i3j2k1l0m9n8o7p6q5r4s3t2u1v0w9x8y7z6a5"
}
Ejemplo de codigo para validacion de firma
import crypto from 'crypto';
function validateSignature(payload: any, receivedSignature: string): boolean {
// Extraer datos para la firma
const id = payload.cartId || payload.refundId;
const amount = payload.amount;
const timestamp = payload.timestamp;
// Crear string a hashear (MISMO ORDEN)
const dataToHash = `${id}-${amount}-${timestamp}`;
// Generar firma esperada usando tu hashKey (proporcionado por soporte)
const expectedSignature = crypto
.createHmac('sha1', YOUR_HASH_KEY) // ← Tu hashKey de soporte
.update(dataToHash)
.digest('hex');
// Comparación segura de firmas
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
📝 Logs
El servidor genera logs detallados de todas las operaciones:
[2024-01-01T12:00:00.000Z] get_users iniciada
[2024-01-01T12:00:01.000Z] get_users completada exitosamente🔍 Troubleshooting
Error de autenticación
Verificar que las credenciales estén configuradas correctamente en las variables de entorno.
Error de conexión a la API
Verificar que BASE_URL sea accesible y la API esté funcionando.
Error de puerto
Verificar que el puerto 3008 no esté siendo usado por otro servicio.
📞 Soporte
Para soporte técnico, contactar al equipo de desarrollo.
Generado automáticamente por Refacil MCP Framework
