# 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: ```json { ... "notify_url": "", "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}](/apis/v3/instant-payments/openapi) 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: ```json { "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: ```other 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: ol li strong Extraer i timestamp y firma de la cabecera br El valor de la cabecera debe ser separado por la coma code , para obtener los elementos, y luego se debe separar cada elemento por el signo de igualdad code = para obtener el par prefijo/valor. li strong Prepara la cadena para verificación de firma br Crea una cadena de texto (string) concatenando: ul li El valor de timestamp (convertido a texto) li El punto code . li El objeto JSON recibido en el cuerpo del mensaje. li strong Calcular la firma esperada br 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. li strong Comparar firmas br 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. ```python 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: ```shell Received hash: GYzpjnXlTKQ+BJY7pZJmrM6DZgWMSJdtOr/dleBKTdg= Generated hash: GYzpjnXlTKQ+BJY7pZJmrM6DZgWMSJdtOr/dleBKTdg= HMAC is correct ```