Buenas prácticas

Buenas prácticas del manejo de webhooks.

Manejo de idempotencia

Los webhooks pueden, en algunos casos, enviar la misma notificación varias veces. Para mitigar cualquier efecto no deseado, se recomienda implementar la idempotencia en el procesamiento de eventos. Asigna un identificador único a cada evento recibido y utiliza este identificador para asegurarte de que el evento se procese una sola vez, incluso si se recibe múltiples veces.

Previene ataques de reinyección

Para evitar un ataque de reproducción de mensajes, recomendamos que utilices una marca de tiempo. Al recibir y obtener el timestamp de un evento, verifica que este se encuentre dentro de tu rango de tolerancia. De no ser así, puedes descartar el evento.

Procesamiento inmediato de eventos

Es fundamental responder rápidamente a las solicitudes de webhook para evitar timeout. Para lograr esto, responde a la solicitud con un código de estado 2XX inmediatamente después de recibirla, indicando que la solicitud se ha recibido con éxito. Posteriormente, procesa el evento de manera asíncrona, utilizando colas o herramientas de procesamiento de datos asíncronos. Esta estrategia permite una respuesta rápida a la fuente del webhook, mientras se garantiza el procesamiento seguro y eficiente de los eventos.

Prueba la recepción de eventos

Realiza pruebas regulares de la recepción de webhooks para asegurarte de que tu sistema esté preparado para manejar los eventos correctamente. Utiliza nuestra API de prueba de webhooks para simular envíos de eventos y verificar la integridad de tu configuración de webhook y la capacidad de tu sistema para procesarlos adecuadamente.

Lectura de Webhooks

Cuando se trabaja con webhooks, es fundamental asegurarse de que la integración sea robusta y resistente a cambios en la estructura del payload enviado. Una de las mejores prácticas es leer la información por key, en lugar de acceder a datos por posición o asumir una estructura fija.

❌ Ejemplo de una mala implementación

Este método es riesgoso porque asume que los datos siempre estarán en la misma posición dentro del JSON.

import json

def handle_webhook_bad(request_body):
    data = json.loads(request_body)  # Convertir el JSON en un diccionario

    # ❌ Mala práctica: Acceder por posición en lugar de por clave
    payment_method_id = list(data["payment_method"].values())[0]  # Error si cambia la estructura

    print(f"ID del método de pago: {payment_method_id}")

# Ejemplo de llamada
webhook_payload = '''
{
  "id": "wheve_MkPZQrwEAVF0E1UdB-8IkdzRQS7IKO-W",
  "event_type": "payment_method.attached_products",
  "payment_method": {
    "id": "pm_rSXZ3Vp1urLI8Ta-_gXF0urdJlt44LK_",
    "customer": "cus_8CHZ-w3GdJ_1AtDxhMzT_MgT_cxYBwQb",
    "gateway": "internal_psp",
    "card_type": null,
    "card_number": "XXXXXXXXXXXX8477",
    "status": "chargeable",
    "additional": {
      "holder_id": null,
      "account_type": null,
      "account_number": null,
      "institution_id": null,
      "institution_name": null,
      "institution_country": null,
      "limit_uf": null,
      "mandate_id": null,
      "voucher_url": null,
      "voucher": null
    },
    "product_ids": [
      "123"
    ],
    "id_account": "acc_5pe6OvW_pWp8qEB16cMs96J-lS95E1sC"
  }
}
'''

handle_webhook_bad(webhook_payload)  # ❌ Puede fallar si la estructura cambia


✅ Ejemplo de una buena implementación

Este método es seguro, ya que accede directamente a las claves sin importar el orden.
Además, si el webhook agrega nuevos campos, esto no afectará la integración, ya que solo se extraen los valores que se necesitan.

import json

def handle_webhook_good(request_body):
    data = json.loads(request_body)  # Convertir el JSON en un diccionario

    # ✅ Buena práctica: Acceder por key y usar .get() para evitar errores
    payment_method_id = data.get("payment_method", {}).get("id", "ID no disponible")

    print(f"ID del método de pago: {payment_method_id}")

# Ejemplo de llamada
webhook_payload = '''
{
  "id": "wheve_MkPZQrwEAVF0E1UdB-8IkdzRQS7IKO-W",
  "event_type": "payment_method.attached_products",
  "payment_method": {
    "id": "pm_rSXZ3Vp1urLI8Ta-_gXF0urdJlt44LK_",
    "customer": "cus_8CHZ-w3GdJ_1AtDxhMzT_MgT_cxYBwQb",
    "gateway": "internal_psp",
    "card_type": null,
    "card_number": "XXXXXXXXXXXX8477",
    "status": "chargeable",
    "additional": {
      "holder_id": null,
      "account_type": null,
      "account_number": null,
      "institution_id": null,
      "institution_name": null,
      "institution_country": null,
      "limit_uf": null,
      "mandate_id": null,
      "voucher_url": null,
      "voucher": null
    },
    "product_ids": [
      "123"
    ],
    "id_account": "acc_5pe6OvW_pWp8qEB16cMs96J-lS95E1sC"
  },
  "new_field": "Este campo no afectará la lectura"
}
'''

handle_webhook_good(webhook_payload)  # ✅ Maneja correctamente la estructura del JSON sin importar nuevos campos


Siguientes pasos