Webhooks

Utiliza webhooks para obtener información de eventos en tiempo real.

¿Por qué utilizar webhooks?

Los webhooks facilitan la transferencia instantánea de datos entre aplicaciones al activar acciones inmediatas frente a eventos específicos. Su valor se encuentra en la capacidad de mantener sistemas actualizados en tiempo real, mejorando la eficiencia, sincronización y agilidad de las aplicaciones.

Para recibir eventos de webhooks, es necesario registrar los endpoints de los mismos. Al hacerlo, Toku puede enviar información cuando se producen eventos en tu cuenta.

Los webhooks consisten en solicitudes HTTP, generalmente utilizando el método POST, dirigidas a tus aplicaciones, y contienen en su body la información asociada a un evento específico. Recibir eventos de webhooks resulta particularmente útil para escuchar eventos asincrónicos, como por ejemplo, un pago exitoso.

Eventos

En Toku, generamos datos de eventos que te enviamos para mantenerte informado sobre la actividad en tu cuenta. Notificamos distintos tipos de eventos que puedes explorar en el listado de eventos.

Para registrar un **Webhook Endpoint y recibir eventos, debes poseer un endpoint que pueda recibir datos a través de una request POST, y que confirme su recepción retornando un status code HTTP 2xx.

Seguridad

Toku firma cada evento enviado a tus Webhook Endpoints mediante el campo Toku-Signature en el header de la request POST al notificar un evento. Esta firma te permite verificar que la notificación proviene de Toku y no de un tercero.

Para verificar esta firma, necesitas tener el secret asociado al Webhook Endpoint, el cual se genera al momento de su creación.

El header Toku-Signature contiene un timestamp y una firma, separados por una coma ,. El timestamp viene precedido de t=, y la firma viene precedida de s=. Un ejemplo de firma es:

{
  "Toku-Signature": "t=1618960495,s=c896f1eb1438c706f4eb8b59d5453582b44a4cb442fd23ed9eb2690e1f9213b7"
}

Las firmas enviadas por Toku son códigos HMAC, generados al firmar el id de la request con la función de hash SHA-256, en el momento indicado por el timestamp. Se utiliza como key de la función de hash el secret del Webhook Endpoint.

¿Cómo obtener el secret de un Webhook Endpoint?

Al crear un Webhook Endpoint, la respuesta de la request contiene en su body el secret asociado a ese Webhook Endpoint en particular.

Ejemplo de respuesta de creación de Webhook Endpoint:

{
  "id": "whe_hXPK3YBs4EUfw2KFZlB6R44MiFa07vFU",
  "secret": "whesec_CUbgd2GIlwwfm14bC2XlJxLHlJQ28G4G",
  "enabled_events": [
    "payment_method.attached",
    "payment_intent.succeeded"
  ],
  "url": "https://prueba.com",
  "status": "enabled"
}

Además, si tienes el id de un Webhook Endpoint y no su secret puedes consultar la información de ese Webhook Endpoint usando el método GETde nuestra API. Puedes también consultar todos los Webhook Endpoints inscritos en tu cuenta usando nuestra API.

📘

Firma de webhook de prueba

Los webhooks de prueba también están firmados, y el secreto que se usa es whesec_CTyIJ1Gvykk0GGM6u5CukPQnj2m_omrw

Ejemplo de validación de firma

A continuación, explicamos los pasos a llevar a cabo para validar la firma del evento. Utilizamos como ejemplo el siguiente evento:

{
	"id": "evt_MOnNVXKNYDCZXzI9slA3smhASQmuRleM",
	"event_type": "payment_method.attached",
	"payment_method": {
		"id": "pm_9tN0ZtjUDjS1qi8qZQ3uJHJbwtcXYH9d",
		"customer": "cus_lq1wGjwgFyqQm4ACZx0QjE84qKm8fffa",
		"gateway": "transbank_oneclick",
		"card_type": "Visa",
		"card_number": "XXXXXXXXXXXX6623",
		"status": "chargeable"
	}
}

Código para la validación

import hmac
from hashlib import sha256

# Obtener el valor 'Toku-Signature' de los headers de la request
header_value = request.headers.get('Toku-Signature')

# Dividir el valor 'Toku-Signature' en partes de timestamp y firma del evento
timestamp, event_signature = [x.split('=')[1] for x in header_value.split(',')]

# Obtener event_id del body de la request como 'id'
event_id = request.json.get('id')

# Manejar el caso en que event_id sea None
if event_id is None:
  raise ValueError("El campo 'id' no está presente en la request")

# Construir el mensaje a verificar
message = f"{timestamp}.{event_id}"

# Codificar el secreto asociado a tu Webhook Endpoint
encoded_secret = YOUR_WEBHOOK_ENDPOINT_SECRET.encode('utf-8')

# Codificar el mensaje a verificar
encoded_message = message.encode('utf-8')

# Crear un objeto HMAC usando el algoritmo de hash SHA256
hmac_object = hmac.new(encoded_secret, msg=encoded_message, digestmod=sha256)

# Calcular la firma HMAC
signature = hmac_object.hexdigest()

# Comparar la firma calculada con la firma del evento recibida
valid_signature = hmac.compare_digest(signature, event_signature)

if valid_signature:
	print("La firma es válida.")
else:
	print("La firma es inválida.")

import * as crypto from 'crypto';

// Obtener el valor 'Toku-Signature' de los headers de la request
const headerValue: string = request.headers['Toku-Signature'];

// Extraer timestamp y firma del evento del valor 'Toku-Signature'
const [timestamp, eventSignature]: string[] = headerValue.split(',').map(part => part.split('=')[1]);

// Obtener event_id del body de la request como 'id'
const event_id: string | undefined = request.body?.id;

// Manejar el caso en que event_id sea undefined
if (!event_id) {
  throw new Error("El campo 'id' no está presente en la request");
}

// Construir el mensaje a verificar
const message: string = `${timestamp}.${event_id}`;

// Codificar el secreto asociado a tu Webhook Endpoint
const encodedSecret: Buffer = Buffer.from(YOUR_WEBHOOK_ENDPOINT_SECRET, 'utf-8');

// Codificar el mensaje a verificar
const encodedMessage: Buffer = Buffer.from(message, 'utf-8');

// Crear un objeto HMAC usando el algoritmo de hash SHA256
const hmac: crypto.Hmac = crypto.createHmac('sha256', encodedSecret);

// Calcular la firma HMAC
const signature: string = hmac.update(encodedMessage).digest('hex');

// Comparar la firma calculada con la firma del evento recibida
const validSignature: boolean = crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(eventSignature, 'utf-8'));

if (validSignature) {
  console.log("La firma es válida.");
} else {
  console.log("La firma es inválida.");
}

Reintentos de Webhooks

Nuestro sistema de reintento de webhooks está diseñado para garantizar la confiabilidad en la entrega de eventos importantes. En caso de que un webhook falle, implementamos un proceso de reintento que sigue los siguientes parámetros:

  • Máximo de Reintentos: Se permite un máximo de 5 intentos para la entrega de un webhook.
  • Intervalos de Reintentos: Los intervalos para los reintentos se han establecido de la siguiente manera:
    • Reintento 1: Programado inmediatamente después del primer fallo.
    • Reintento 2: Programado para 1 minuto después del primer reintento.
    • Reintento 3: Programado para 10 minutos después del segundo reintento.
    • Reintento 4: Programado para 30 minutos después del tercer reintento.
    • Reintento 5: Programado para 1 hora después del cuarto reintento.

Este enfoque de reintento escalonado nos permite gestionar con flexibilidad las situaciones en las que puede haber dificultades temporales en la entrega de eventos a través de webhooks. Al ofrecer múltiples intentos con intervalos estratégicos, buscamos asegurar la entrega exitosa de eventos críticos, manteniendo la integridad y confiabilidad de nuestro sistema de comunicación.


Siguientes pasos