Ejemplos de Implementación
Ejemplos completos de implementación de webhooks en diferentes lenguajes y frameworks
Ejemplos de Implementación
Esta página proporciona ejemplos completos de cómo implementar webhooks de Docutray en diferentes lenguajes y frameworks.
Node.js/Express
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.raw({ type: 'application/json' }));
app.post('/webhooks/docutray', (req, res) => {
const signature = req.headers['x-docutray-signature'];
const eventType = req.headers['x-docutray-event'];
const payload = req.body;
// Verificar firma
const secret = process.env.DOCUTRAY_WEBHOOK_SECRET;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
if (`sha256=${expectedSignature}` !== signature) {
return res.status(401).send('Signature verification failed');
}
const data = JSON.parse(payload);
// Procesar según el tipo de evento
switch (eventType) {
// Eventos de Conversión
case 'CONVERSION_STARTED':
console.log(`Conversión iniciada: ${data.conversion_id}`);
break;
case 'CONVERSION_COMPLETED':
console.log(`Conversión completada: ${data.conversion_id}`);
console.log('Datos extraídos:', data.data);
break;
case 'CONVERSION_FAILED':
console.log(`Conversión fallida: ${data.conversion_id}`);
console.log('Error:', data.error);
break;
// Eventos de Identificación
case 'IDENTIFICATION_STARTED':
console.log(`Identificación iniciada: ${data.identification_id}`);
break;
case 'IDENTIFICATION_COMPLETED':
console.log(`Identificación completada: ${data.identification_id}`);
console.log('Tipo identificado:', data.document_type);
break;
case 'IDENTIFICATION_FAILED':
console.log(`Identificación fallida: ${data.identification_id}`);
console.log('Error:', data.error);
break;
// Eventos de Steps
case 'STEP_STARTED':
console.log(`Step iniciado: ${data.step_name} (${data.step_execution_id})`);
break;
case 'STEP_COMPLETED':
console.log(`Step completado: ${data.step_name} (${data.step_execution_id})`);
if (data.data) console.log('Datos procesados:', data.data);
if (data.validation) console.log('Validación:', data.validation);
break;
case 'STEP_FAILED':
console.log(`Step fallido: ${data.step_name} (${data.step_execution_id})`);
console.log('Error:', data.error);
break;
}
res.status(200).send('OK');
});
app.listen(3000);Python/Flask
import hmac
import hashlib
import json
import os
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhooks/docutray', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Docutray-Signature')
event_type = request.headers.get('X-Docutray-Event')
payload = request.get_data()
# Verificar firma
secret = os.environ['DOCUTRAY_WEBHOOK_SECRET'].encode()
expected_signature = hmac.new(
secret,
payload,
hashlib.sha256
).hexdigest()
if f'sha256={expected_signature}' != signature:
return 'Signature verification failed', 401
data = json.loads(payload)
# Procesar según el tipo de evento
# Eventos de Conversión
if event_type == 'CONVERSION_STARTED':
print(f"Conversión iniciada: {data['conversion_id']}")
elif event_type == 'CONVERSION_COMPLETED':
print(f"Conversión completada: {data['conversion_id']}")
print(f"Datos extraídos: {data['data']}")
elif event_type == 'CONVERSION_FAILED':
print(f"Conversión fallida: {data['conversion_id']}")
print(f"Error: {data['error']}")
# Eventos de Identificación
elif event_type == 'IDENTIFICATION_STARTED':
print(f"Identificación iniciada: {data['identification_id']}")
elif event_type == 'IDENTIFICATION_COMPLETED':
print(f"Identificación completada: {data['identification_id']}")
print(f"Tipo identificado: {data['document_type']}")
elif event_type == 'IDENTIFICATION_FAILED':
print(f"Identificación fallida: {data['identification_id']}")
print(f"Error: {data['error']}")
# Eventos de Steps
elif event_type == 'STEP_STARTED':
print(f"Step iniciado: {data['step_name']} ({data['step_execution_id']})")
elif event_type == 'STEP_COMPLETED':
print(f"Step completado: {data['step_name']} ({data['step_execution_id']})")
if 'data' in data:
print(f"Datos procesados: {data['data']}")
if 'validation' in data:
print(f"Validación: {data['validation']}")
elif event_type == 'STEP_FAILED':
print(f"Step fallido: {data['step_name']} ({data['step_execution_id']})")
print(f"Error: {data['error']}")
return 'OK', 200
if __name__ == '__main__':
app.run(port=3000)Python/FastAPI
import hmac
import hashlib
import os
from fastapi import FastAPI, Request, Header, HTTPException
app = FastAPI()
@app.post("/webhooks/docutray")
async def handle_webhook(
request: Request,
x_docutray_signature: str = Header(...),
x_docutray_event: str = Header(...)
):
payload = await request.body()
# Verificar firma
secret = os.environ['DOCUTRAY_WEBHOOK_SECRET'].encode()
expected_signature = hmac.new(
secret,
payload,
hashlib.sha256
).hexdigest()
if f'sha256={expected_signature}' != x_docutray_signature:
raise HTTPException(status_code=401, detail="Signature verification failed")
data = await request.json()
# Procesar según el tipo de evento
if x_docutray_event == 'CONVERSION_STARTED':
print(f"Conversión iniciada: {data['conversion_id']}")
elif x_docutray_event == 'CONVERSION_COMPLETED':
print(f"Conversión completada: {data['conversion_id']}")
print(f"Datos extraídos: {data['data']}")
elif x_docutray_event == 'CONVERSION_FAILED':
print(f"Conversión fallida: {data['conversion_id']}")
# ... otros eventos
return {"status": "ok"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=3000)PHP
<?php
$signature = $_SERVER['HTTP_X_DOCUTRAY_SIGNATURE'];
$eventType = $_SERVER['HTTP_X_DOCUTRAY_EVENT'];
$payload = file_get_contents('php://input');
// Verificar firma
$secret = getenv('DOCUTRAY_WEBHOOK_SECRET');
$expectedSignature = 'sha256=' . hash_hmac('sha256', $payload, $secret);
if ($signature !== $expectedSignature) {
http_response_code(401);
echo 'Signature verification failed';
exit;
}
$data = json_decode($payload, true);
// Procesar según el tipo de evento
switch ($eventType) {
case 'CONVERSION_STARTED':
error_log("Conversión iniciada: " . $data['conversion_id']);
break;
case 'CONVERSION_COMPLETED':
error_log("Conversión completada: " . $data['conversion_id']);
error_log("Datos extraídos: " . json_encode($data['data']));
break;
case 'CONVERSION_FAILED':
error_log("Conversión fallida: " . $data['conversion_id']);
error_log("Error: " . $data['error']);
break;
// ... otros eventos
}
http_response_code(200);
echo 'OK';Go
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
type WebhookData map[string]interface{}
func verifySignature(payload []byte, signature string, secret string) bool {
h := hmac.New(sha256.New, []byte(secret))
h.Write(payload)
expectedSignature := "sha256=" + hex.EncodeToString(h.Sum(nil))
return expectedSignature == signature
}
func handleWebhook(w http.ResponseWriter, r *http.Request) {
signature := r.Header.Get("X-Docutray-Signature")
eventType := r.Header.Get("X-Docutray-Event")
payload, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading body", http.StatusBadRequest)
return
}
// Verificar firma
secret := os.Getenv("DOCUTRAY_WEBHOOK_SECRET")
if !verifySignature(payload, signature, secret) {
http.Error(w, "Signature verification failed", http.StatusUnauthorized)
return
}
var data WebhookData
if err := json.Unmarshal(payload, &data); err != nil {
http.Error(w, "Error parsing JSON", http.StatusBadRequest)
return
}
// Procesar según el tipo de evento
switch eventType {
case "CONVERSION_STARTED":
log.Printf("Conversión iniciada: %v", data["conversion_id"])
case "CONVERSION_COMPLETED":
log.Printf("Conversión completada: %v", data["conversion_id"])
log.Printf("Datos extraídos: %v", data["data"])
case "CONVERSION_FAILED":
log.Printf("Conversión fallida: %v", data["conversion_id"])
log.Printf("Error: %v", data["error"])
// ... otros eventos
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "OK")
}
func main() {
http.HandleFunc("/webhooks/docutray", handleWebhook)
log.Println("Servidor iniciado en :3000")
log.Fatal(http.ListenAndServe(":3000", nil))
}Ruby/Sinatra
require 'sinatra'
require 'json'
require 'openssl'
post '/webhooks/docutray' do
signature = request.env['HTTP_X_DOCUTRAY_SIGNATURE']
event_type = request.env['HTTP_X_DOCUTRAY_EVENT']
payload = request.body.read
# Verificar firma
secret = ENV['DOCUTRAY_WEBHOOK_SECRET']
expected_signature = 'sha256=' + OpenSSL::HMAC.hexdigest('sha256', secret, payload)
if signature != expected_signature
halt 401, 'Signature verification failed'
end
data = JSON.parse(payload)
# Procesar según el tipo de evento
case event_type
when 'CONVERSION_STARTED'
puts "Conversión iniciada: #{data['conversion_id']}"
when 'CONVERSION_COMPLETED'
puts "Conversión completada: #{data['conversion_id']}"
puts "Datos extraídos: #{data['data']}"
when 'CONVERSION_FAILED'
puts "Conversión fallida: #{data['conversion_id']}"
puts "Error: #{data['error']}"
# ... otros eventos
end
status 200
body 'OK'
endRecomendaciones de implementación
Procesamiento asíncrono
Para webhooks que requieren procesamiento pesado, considere usar una cola de tareas:
// Ejemplo con Bull (Redis)
const Queue = require('bull');
const webhookQueue = new Queue('webhook-processing');
app.post('/webhooks/docutray', async (req, res) => {
// Verificar firma primero
if (!verifySignature(req)) {
return res.status(401).send('Invalid signature');
}
// Agregar a cola para procesamiento asíncrono
await webhookQueue.add({
eventType: req.headers['x-docutray-event'],
data: JSON.parse(req.body)
});
// Responder inmediatamente
res.status(200).send('OK');
});
// Procesar en background
webhookQueue.process(async (job) => {
const { eventType, data } = job.data;
// Procesamiento pesado aquí
});Manejo de errores y reintentos
app.post('/webhooks/docutray', async (req, res) => {
try {
// Verificar firma
if (!verifySignature(req)) {
return res.status(401).send('Invalid signature');
}
// Procesar webhook
await processWebhook(req.body);
// Responder exitosamente
res.status(200).send('OK');
} catch (error) {
console.error('Error processing webhook:', error);
// Devolver error 500 para que Docutray reintente
res.status(500).send('Internal server error');
}
});Logging y debugging
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'webhooks.log' })
]
});
app.post('/webhooks/docutray', (req, res) => {
const requestId = req.headers['x-docutray-request-id'];
const eventType = req.headers['x-docutray-event'];
logger.info('Webhook received', {
requestId,
eventType,
timestamp: new Date().toISOString()
});
// Procesar webhook...
logger.info('Webhook processed', {
requestId,
eventType,
duration: Date.now() - startTime
});
res.status(200).send('OK');
});