Webhook para notificaciones

Puedes crear webhooks para recibir eventos asociados a pagos instantáneos de tal forma que tu integración pueda reaccionar automáticamente.

Objetivos

Mediante el uso de webhooks, puedes programar aplicaciones para recibir eventos a medida que ocurran, y hacer que tu backend ejecute acciones según el tipo de evento.

En la versión 3.0 de la API de pagos instantáneos, también aparece una nueva versión (3.0) de la API de notificaciones, pero su finalidad continúa la misma: enviar datos en tiempo real al webhook cuando ocurran eventos en el cobro con el endpoint configurado. Khipu usa HTTPS para enviar los eventos a su aplicación con un objeto JSON que representa al evento.

Importante

En la API de notificaciones (1.3) asociada a la API 2.0 de pagos, en el momento que ocurre la conciliación se envía una petición al webhook configurado para el pago, con el parámetro notification_token, que luego se puede utilizar para consultar el detalle del pago en una llamada posterior a la API. En esta nueva versión de API de notificaciones ese modelo deja de existir y se utiliza un flujo simplificado, en el que la petición al webhook contiene datos del pago.

Uso

Para activar las notificaciones automáticas a tu webhook, al momento de crear el cobro debes incluir los siguientes parámetros en la petición:

Copy
Copied
{
  ...
  "notify_url": "<URL HTTPS del webhook>",
  "notify_api_version": "3.0",
  ...
}

Eventos

Khipu es capaz de enviar los datos generados en las actividades de un determinado cobro. Al ocurrir un nuevo evento, Khipu genera el objeto correspondiente con los datos necesarios para representar el estado del cobro. Por ejemplo, es posible recibir el evento de creación de cobro, de pago exitoso (o fallido) y de resultado de conciliación. Puedes elegir suscribirte a uno o más eventos.

Cada evento es enviado en una petición de tipo POST al webhook registrado al momento de usar la API de creación de cobro. Puedes programar tu backend para que ejecute acciones al recibir un evento.

Estructura del objeto

Los datos que contiene el objeto JSON está asociado al tipo de eventos. Actualmente, nuestra API es capaz de informar los siguientes eventos:

  • Conciliación realizada exitosamente
  • Cobro generado exitosamente (próximamente)
  • Pago realizado exitosamente (próximamente)
  • Pago terminado en error (próximamente)

El cuerpo de cada evento está definido como un subconjunto de los datos del objeto de pago. Para entender su estructura y detalles, revisar la documentación del endpoint GET /payments/{id} en la sección "Responses - 200".

Ejemplo

El siguiente ejemplo muestra un evento de cobro conciliado exitosamente, tal como se enviaría a un webhook. Posteriormente se utilizará este mismo mensaje para realizar la verificación de la cabecera de firma:

Copy
Copied
{
  "payment_id": "zfxnocsow6mz",
  "receiver_id": 990939,
  "subject": "TEST_COBRO",
  "amount": "1000.0000",
  "discount": "0.0000",
  "currency": "CLP",
  "receipt_url": "https://s3.amazonaws.com/staging.notifications.khipu.com/CPKH-1804240956-zfxnocsow6mz.pdf",
  "bank": "DemoBank",
  "bank_id": "Bawdf",
  "payer_name": "Cobrador de desarrollo #990.939",
  "payer_email": "test@khipu.com",
  "personal_identifier": "44.444.444-4",
  "bank_account_number": "000000000000444444444",
  "out_of_date_conciliation": false,
  "transaction_id": "15f836bd-e8a7-4d12-b2f1-56403012b555",
  "responsible_user_email": "test@khipu.com",
  "payment_method": "simplified_transfer",
  "conciliation_date": "2024-04-18T13:56:54.859Z"
}

Versión de API de notificaciones

Para el caso de cobros generados usando la API 3.0, la notificación se realizará exclusivamente utilizando la versión 3.0 de la API de notificaciones de eventos.

Verificación de la firma

Es recomendable que al momento de la recepción de un evento en su webhook, realices la verificación de la firma para asegurar que el contenido no ha sido modificado y corresponde exactamente a los datos generados por Khipu. La validación solo podrá realizarse utilizando el secreto de su comercio.

Advertencia

Asegúrese de no manipular el contenido del cuerpo del mensaje antes de hacer la verificación. Evite ordenar, indentar o transformar el objeto JSON.

Cada mensaje enviado a un webhook es acompañado de una cabecera x-khipu-signature que contiene una marca de tiempo (timestamp) y el valor de la firma que debes verificar. El prefijo del valor de la marca de tiempo es t= y el prefijo del valor de la firma es s=. Los atributos están separados por una coma ,.

Ejemplo de cabecera:

Copy
Copied
x-khipu-signature: t=1711965600393,s=GYzpjnXlTKQ+BJY7pZJmrM6DZgWMSJdtOr/dleBKTdg=

t corresponde al valor del tiempo UNIX en UTC, en milisegundos.

s se genera utilizando un código de autentificación de mensaje (HMAC) con la función de hash SHA-256 aplicada sobre el objeto JSON que contiene el evento.

Los pasos para verificar una firma de mensaje son:

  1. Extraer timestamp y firma de la cabecera
    El valor de la cabecera debe ser separado por la coma , para obtener los elementos, y luego se debe separar cada elemento por el signo de igualdad = para obtener el par prefijo/valor.
  2. Prepara la cadena para verificación de firma
    Crea una cadena de texto (string) concatenando:
    • El valor de timestamp (convertido a texto)
    • El punto .
    • El objeto JSON recibido en el cuerpo del mensaje.
  3. Calcular la firma esperada
    Usar la función SHA256 para calcular el HMAC, utilizando como clave el secreto de su comercio para aplicar la función sobre el texto creado en el paso anterior.
  4. Comparar firmas
    Compare la firma calculada en el paso anterior con la firma recibida en la cabecera, ambos valores deben ser iguales. Además, puedes calcular la diferencia entre la marca de tiempo actual y la marca de tiempo recibida para determinar si la diferencia está dentro de lo aceptable. Esto podría ser útil para descartar mensajes obsoletos o evitar ataques de repetición.

Ejemplo

El siguiente código en python hace la verificación de una firma y mensaje recibidos por el webhook. Notar que son valores simulados. En un escenario real los valores deben ser obtenidos desde la petición recibida por el servicio. El secreto a utilizar es aquel correspondiente a la cuenta utilizada para generar el cobro.

Copy
Copied
import hmac
import hashlib
import base64

# merchant secret:
my_secret = '1a4cbbbeb8bdb7e1d73572b9cc43ce4ce18f79d9'

# extract timestamp and signature values from the received header:
x_khipu_signature = 't=1711965600393,s=GYzpjnXlTKQ+BJY7pZJmrM6DZgWMSJdtOr/dleBKTdg='
signature_values = x_khipu_signature.split(',')
t_value = None
s_value = None

for value in signature_values:
    key, val = value.split('=', 1)
    if key == 't':
        t_value = val
    elif key == 's':
        s_value = val

print('Received hash:  ' + s_value)

# received body payload:
json_payload = """{"payment_id":"zfxnocsow6mz","receiver_id":990939,"subject":"TEST_COBRO","amount":"1000.0000","discount":"0.0000","currency":"CLP","receipt_url":"https:\/\/s3.amazonaws.com\/staging.notifications.khipu.com\/CPKH-1804240956-zfxnocsow6mz.pdf","bank":"DemoBank","bank_id":"Bawdf","payer_name":"Cobrador de desarrollo #990.939","payer_email":"test@khipu.com","personal_identifier":"44.444.444-4","bank_account_number":"000000000000444444444","out_of_date_conciliation":false,"transaction_id":"15f836bd-e8a7-4d12-b2f1-56403012b555","responsible_user_email":"test@khipu.com","payment_method":"simplified_transfer","conciliation_date":"2024-04-18T13:56:54.859Z"}"""

# calculate hash of the received payload:
to_hash = '{}.{}'.format(str(t_value), json_payload)
secret_bytes = bytes(my_secret, 'utf-8')
hash_bytes = bytes(to_hash, 'utf-8')
hmac_signature = hmac.new(secret_bytes, hash_bytes, hashlib.sha256).digest()
hmac_base64 = base64.b64encode(hmac_signature).decode('utf-8')
print('Generated hash: ' + hmac_base64)

# verify:
if (hmac_base64 == s_value):
    print('HMAC is correct')
else:
    print('Message was tampered')

Resultado:

Copy
Copied
Received hash:  GYzpjnXlTKQ+BJY7pZJmrM6DZgWMSJdtOr/dleBKTdg=
Generated hash: GYzpjnXlTKQ+BJY7pZJmrM6DZgWMSJdtOr/dleBKTdg=
HMAC is correct