Estructura de Errores
Todos los errores siguen esta estructura:
{
"error": "VALIDATION_ERROR",
"message": "Invalid currency code",
"details": {
"field": "currency",
"value": "INVALID",
"allowed": ["USD", "VES", "COP", "MXN"]
},
"requestId": "req_abc123"
}
| Campo | Descripción |
|---|
error | Código de error (para lógica) |
message | Mensaje legible |
details | Información adicional |
requestId | ID para soporte |
Códigos HTTP
4xx - Errores del Cliente
| Código | Error | Descripción |
|---|
| 400 | INVALID_REQUEST | Parámetros inválidos |
| 401 | UNAUTHORIZED | API Key inválida |
| 403 | FORBIDDEN | Sin permisos |
| 404 | NOT_FOUND | Recurso no existe |
| 409 | CONFLICT | Conflicto de estado |
| 422 | VALIDATION_ERROR | Error de validación |
| 429 | RATE_LIMITED | Demasiadas solicitudes |
5xx - Errores del Servidor
| Código | Error | Descripción |
|---|
| 500 | INTERNAL_ERROR | Error interno |
| 502 | BAD_GATEWAY | Error de gateway |
| 503 | SERVICE_UNAVAILABLE | Servicio no disponible |
Errores Comunes
UNAUTHORIZED (401)
{
"error": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
Causas:
- API Key faltante
- API Key inválida
- API Key de ambiente incorrecto
Solución:
# Verificar que el header esté presente
curl -v -X GET "https://sandbox-api.cobrix.co/api/..." \
-H "Authorization: Bearer tu_access_token"
PRODUCT_NOT_FOUND (404)
{
"error": "PRODUCT_NOT_FOUND",
"message": "No product found with SKU: PROD-001"
}
Solución:
Es normal si el producto no existe. Créalo:
// Si no existe, crear
try {
product = await client.get(`/product/sku/${sku}`);
} catch (error) {
if (error.response?.status === 404) {
product = await client.post('/checkout/product', data);
}
}
VALIDATION_ERROR (422)
{
"error": "VALIDATION_ERROR",
"message": "Invalid currency code",
"details": {
"field": "currency",
"value": "INVALID",
"allowed": ["USD", "VES", "COP", "MXN"]
}
}
Solución:
Revisar el campo indicado en details.field y usar un valor válido.
RATE_LIMITED (429)
{
"error": "RATE_LIMITED",
"message": "Too many requests",
"retryAfter": 60
}
Headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706023860
Retry-After: 60
Solución:
Implementar backoff exponencial:
async function requestWithRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.response?.status === 429) {
const wait = error.response.headers['retry-after'] || 60;
await new Promise(r => setTimeout(r, wait * 1000));
} else {
throw error;
}
}
}
}
SESSION_EXPIRED (409)
{
"error": "SESSION_EXPIRED",
"message": "Checkout session has expired"
}
Solución:
Crear una nueva sesión de checkout.
Errores de Webhook
Firma Inválida
Síntoma: Tu endpoint retorna 401
Checklist:
¿Estás usando el raw body (sin parsear)?
¿El webhook secret es correcto?
¿El timestamp no es muy antiguo (>5 min)?
Express.js correcto:
// ✅ Correcto
app.post('/webhooks/cobrix',
express.raw({ type: 'application/json' }),
(req, res) => {
const rawBody = req.body.toString();
// ...
}
);
// ❌ Incorrecto - body ya parseado
app.use(express.json());
Manejo de Errores
Ejemplo Completo
async function cobrixRequest(fn) {
try {
return await fn();
} catch (error) {
const { status, data } = error.response || {};
switch (status) {
case 401:
console.error('API Key inválida');
throw new Error('Authentication failed');
case 404:
// Puede ser normal (producto no existe)
return null;
case 422:
console.error('Validación fallida:', data.details);
throw new Error(`Validation: ${data.message}`);
case 429:
const wait = error.response.headers['retry-after'];
console.log(`Rate limited, esperando ${wait}s`);
await sleep(wait * 1000);
return cobrixRequest(fn); // Reintentar
case 500:
case 502:
case 503:
console.error('Error del servidor, reintentando...');
await sleep(5000);
return cobrixRequest(fn);
default:
throw error;
}
}
}
Si el error persiste, contacta soporte con:
- Request ID (del error)
- Timestamp
- Endpoint
- Request/Response (sin datos sensibles)
- Ambiente (Sandbox/Producción)
📧 [email protected]