Appearance
Implementación
Esta guía técnica te ayudará a implementar correctamente el endpoint para recibir webhooks de Quralo en tu sistema HIS.
Requerimientos del Endpoint
Tu endpoint debe cumplir con los siguientes requerimientos:
1. Método HTTP
- POST: Todos los webhooks se envían mediante HTTP POST
2. Headers Requeridos
Tu endpoint debe validar los siguientes headers:
http
POST /webhook
Content-Type: application/json
Authorization: Bearer {tu-token-de-autenticacion}
X-Webhook-Event: {tipo-de-evento}
X-Webhook-Signature: {firma-hmac-sha256}3. Estructura de la Respuesta
Tu endpoint debe responder con códigos HTTP apropiados:
- 200: Evento procesado correctamente
- 400: Solicitud malformada o tipo de evento no soportado
- 401: Falla de autenticación o firma inválida
- 422: Payload con campos faltantes o inválidos
- 500: Error interno del servidor
Flujo de Validación
Implementa las validaciones en el siguiente orden:
1. Autenticación por Token
javascript
// Validar header Authorization
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
error: 'Unauthorized',
message: 'Missing or invalid Authorization header'
});
}
const token = authHeader.replace('Bearer ', '');
if (token !== YOUR_AUTH_TOKEN) {
return res.status(401).json({
error: 'Unauthorized',
message: 'Invalid authentication token'
});
}2. Verificación de Firma HMAC
javascript
// Validar firma del webhook
const signature = req.headers['x-webhook-signature'];
if (!signature) {
return res.status(400).json({
error: 'Bad Request',
message: 'Missing X-Webhook-Signature header'
});
}
// Generar firma esperada
const crypto = require('crypto');
const expectedSignature = crypto
.createHmac('sha256', YOUR_WEBHOOK_SECRET)
.update(JSON.stringify(req.body))
.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).json({
error: 'Unauthorized',
message: 'Invalid webhook signature'
});
}3. Validación del Tipo de Evento
javascript
const eventType = req.headers['x-webhook-event'];
const supportedEvents = ['medication_request.created', 'encounter.created'];
if (!supportedEvents.includes(eventType)) {
return res.status(400).json({
error: 'Bad Request',
message: `Unsupported event type: ${eventType}`,
supported_events: supportedEvents
});
}4. Validación del Payload
javascript
const payload = req.body;
// Validar estructura base
if (!payload.data) {
return res.status(422).json({
error: 'Unprocessable Entity',
message: 'Missing data field in payload'
});
}
// Validar campos específicos según el tipo de evento
switch (eventType) {
case 'medication_request.created':
if (!payload.data.prescription_id || !payload.data.patient_id) {
return res.status(422).json({
error: 'Unprocessable Entity',
message: 'Missing required fields for medication_request.created',
required_fields: ['prescription_id', 'patient_id']
});
}
break;
case 'encounter.created':
if (!payload.data.encounter_id || !payload.data.patient_id) {
return res.status(422).json({
error: 'Unprocessable Entity',
message: 'Missing required fields for encounter.created',
required_fields: ['encounter_id', 'patient_id']
});
}
break;
}Procesamiento del Evento
Una vez validado el webhook, procesa el evento según su tipo:
javascript
switch (eventType) {
case 'medication_request.created':
await processMedicationRequest(payload.data);
break;
case 'encounter.created':
await processEncounter(payload.data);
break;
default:
console.log(`Unhandled event type: ${eventType}`);
}
// Responder con éxito
res.status(200).json({
status: 'success',
message: 'Webhook processed successfully'
});Consideraciones Importantes
Idempotencia
- Implementa verificación de duplicados usando el ID único del evento
- Almacena los IDs de eventos procesados para evitar duplicados
Timeouts
- Tu endpoint debe responder en menos de 30 segundos
- Para procesamientos largos, responde inmediatamente y procesa de forma asíncrona
Logging
- Registra todos los webhooks recibidos para auditoría
- Incluye timestamp, tipo de evento, y resultado del procesamiento
Manejo de Errores
- Implementa manejo robusto de errores
- Los webhooks fallidos serán reintentados automáticamente
Estructura Completa del Endpoint
javascript
app.post('/webhook', async (req, res) => {
try {
// 1. Validar autenticación
await validateAuthentication(req);
// 2. Validar firma
await validateSignature(req);
// 3. Validar tipo de evento
const eventType = validateEventType(req.headers['x-webhook-event']);
// 4. Validar payload
validatePayload(eventType, req.body);
// 5. Procesar evento
await processEvent(eventType, req.body.data);
// 6. Responder con éxito
res.status(200).json({
status: 'success',
message: 'Webhook processed successfully'
});
} catch (error) {
console.error('Webhook processing error:', error);
res.status(error.statusCode || 500).json({
error: error.name || 'Internal Server Error',
message: error.message
});
}
});