Docutray Logo

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'
end

Recomendaciones 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');
});

Páginas relacionadas