Integración con Webhook
  • 05 Apr 2023
  • Oscuro
    Ligero

Integración con Webhook

  • Oscuro
    Ligero

Article Summary

Para configurar un Webhook, primero debes crear un endpoint en el que se enviarán las notificaciones. Este endpoint debe ser una URL que acepte peticiones HTTP POST. Una vez que se haya generado el endpoint, debes configurar el Webhook en el portal de Zenkipay

Configuración del webhook en el portal del comercio

Para configurar los webhooks, ingresa al portal en el menú Configuración > Webhooks. 

En la sección de Webhooks en la pestaña endpoints, se listan todos los endpoints configurados.

Hay dos opciones de configuración disponibles, cada una con sus propias características:

  • Entorno de pruebas: Aquí se encuentran los endpoints que se han configurado para realizar pruebas.
  • Entorno de producción: Al seleccionar esta opción, se tendrán acceso a los endpoints configurados específicamente para el entorno productivo.

Nota importante:
  • El proceso de configuración es idéntico para cualquiera de las opciones que se tienen (ya sea entorno de pruebas o producción).
  • Es importante distinguir correctamente entre el entorno de pruebas y el de producción.

Para configurar un endpoint, selecciona la opción Add Endpoint.

Debes seguir los pasos que se muestran a continuación:

  1. Agrega la URL de tu endpoint, opcionalmente, puedes anexar una descripción de tu endpoint.
  2. Selecciona el evento que se notificará al endpoint en la opción Message Filtering, debes de elegir al menos un evento a notificar.
  3. Opcionalmente, puedes configurar la limitación de velocidad, en la sección Advanced Configuration.

❗Únicamente se permite configurar la URL con protocolo HTTPS.

🔒 Autenticación: Una vez configurado tu webhook, puedes agregar autenticación con el estándar de autenticación básica HTTP o a través del uso de token de autenticación (Este último es el mecanismo recomendado).

✅ Pruebas: Puedes probar el webhook en la sección Advanced/Testing, una vez registrado el endpoint puedes realizar pruebas de los eventos disponibles, en las cuales se enviará una notificación con datos de prueba, aunado a ello puedes realizar pruebas de conectividad con la opción ping.

Firma de Webhook

Cada notificación que se envía se firma como medida de seguridad. La firma del Webhook permite al comercio validar el origen de las notificaciones y procesar las notificaciones con un origen válido, para realizar la validación es necesario la firma secreta de tu endpoint.

Una vez configurado tu endpoint puedes obtener la firma secreta, en la sección de endpoints y selecciona tu endpoint configurado.


En caso de requerir actualizar ó cambiar la firma secreta puedes realizarlo en la misma sección, seleccionando la opción "Rotate secret".

Logs

En la sección de Logs, se listan las notificaciones que se envían al webhook configurado, en cada notificación se muestra los detalles del mensaje, los intentos de la notificación y los detalles de las respuestas de cada notificación.

Actividad

En este apartado se muestra de forma gráfica el envío de mensajes, para poder ver de forma resumida la actividad de notificaciones a tu webhook.

¿Qué eventos son notificados?

Consulta el detalle de las notificaciones enviadas al webhook en el siguiente enlace.

Pago completado

Se envía esta notificación de eventoal comercio cuando una orden se ha pagado correctamente por el comprador.

eventType: order.pay

flatData: Se va a recibir el objeto Order cuando se notifique el evento order.pay.

Verifica los Webhooks

Como patrón de seguridad, se firma cada webhook y sus metadatos con una clave única para cada endpoint. Esta firma se puede utilizar para verificar que el webhook proviene realmente de Zenkipay, y sólo procesarlo si el origen es válido. Cada llamada de webhook incluye tres cabeceras con información adicional que se utilizan para la verificación:

  • svix-id: Identificador único del mensaje del webhook. Este identificador es único para todos los mensajes.
  • svix-signature: Firma codificada en Base64. 
  • svix-timestamp: Marca de tiempo Epoch.

Verificación mediante las librerías oficiales de nuestro proveedor Svix

Se requiere instalar las librerías de Svix:

npm install svix
// Or
yarn add svix
composer require svix/svix
// Gradle: Añade esta dependencia
    implementation "com.svix:svix:0.68.0"

// Maven:  Añade esta dependencia en el archivo POM:
    <dependency>
        <groupId>com.svix</groupId>
        <artifactId>svix</artifactId>
        <version>0.68.0</version>
    </dependency>

A continuación, verifique los webhooks utilizando el siguiente código. La carga útil es el cuerpo (cadena) de la solicitud, y las cabeceras son los encabezados pasados en la solicitud.

💡 Es necesario utilizar el cuerpo de la solicitud sin procesar cuando se verifican los webhooks, ya que la firma criptográfica es sensible incluso a los más mínimos cambios. Debes tener cuidado con los frameworks que parsean la petición como JSON porque esto también romperá la verificación de la firma.

Para realizar la validación es necesario del la firma secreta, aquí te decimos como obtener la firma secreta.

import { Webhook } from "svix";

const secret = "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw";

// Las cabeceras son enviadas por cada notificación 
const headers = {
  "svix-id": "msg_p5jXN8AQM9LWM0D4loKWxJek",
  "svix-timestamp": "1614265330",
  "svix-signature": "v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=",
};
const payload = '{"algorithm":"RSA","encryptedData":"z7YjgSyx0VzXlDGNC4fjjk1IC69qKN8rRLSItUDY9WXQFgr98ORq/ieJunuCucwk6hmrM9CZAlszE/LD/qSeUtOUcv28ngjobZ5UD+zDqLOeqC5KqHtP0I48L1wC+epXMntsd/KxslWh0+s076K8hZFg7dgJOy2HS46tytNX7AAbEzuQouQo3R0OGV//asG3POej3VQTyRTzKVoRDOO7cVGgNenI4AjfAjUJu+gcOzHqrAj5qr92TEZOZf45+pAk6p5nrfL42NBThO8GB3pXQr2/k74HpkFmVXcZJRB7RDSGfhsFCsnDFZ4N4mHQJWc1/u00z7oGzymPSDGQBEUjzIwGbjBLLDHxdCGKWuwdUq5hAH8Nk55HGOycou7ciBBXOl8E3iTaSxldqOkFLpvkMQ6G2i6dH/1ERKxx61LtQveetkGGMPaLRlsgrUGJNftuKNGEMMQgxn4JykppxHqW3KBlzNhpFUn3QELIctk5SoV12XUDVWi4yhd49F0QlbqfDbRN7ogXo12/SYhUEBS4Wa2uo/mtVKkAdo+GYLtgcggP25y+Qw/I5CenBMJtm2mVFi1b/9AwPaDQo+Yd4S7SrGPyhvcRJcSareIyCXIFSDq6j40qPxGclUv0MLHdwiqxcmmiCP9PQwSnKstCNPBx+IN91E6UfnyBYBhXOWFPZqyHG5OtdBfrx9pIa+0TtFiMBbVGUDidj5QkslyLOZ5Zhx+RMOz+47GpiSg9LaObfJdH4vRsbsZgufvt5hceGE6+lUn3zQzTcwPLaEsQv4HsNMEUKW+tt8K6ZB3GLWaWtII4g0gVlQi2T5P4ZsvBFXf2YJFj4cAL21JVcRanjD2vYk0SbYyuOM2fpBtMJR8pIbVTzdyEw3pPQdyHo4LlbYBFkM3DaXxum9qHr2MHFeAefwJRM79ou5laulfj4nqBPi6hhfT1Z9r9ToDPujOtH+0jRnTIPs4zAWY6rXzUivPJkcu3iqUsqCZcQaU5SHhKli/bHakINyRyTd1ozpdrMsE3rn2VorpgJyVDT47/Bh+xG0F8lCZKofgh4w7DTRxOcIJwkUaPqu30lHHbmc8q0JfGXOgTc496TyjdFx4R529DMzDDkSCVFKp3z8qnG46WsIOIGCp2OXvIiSIihyQFO3FnBx16NbdVlicnTwov4TUPRcsYDIx0p33c3hxOmn1RR1aygYx7XvG3tuMS8ktpfq12ENy3zwpeOit1b8ylnBHwEdaAENaVy03TOLMxIj8rZSfj2AXhnwAKPMLE1AWKufE7OkQAGg2JyJ/H5wB69k9FjwmG0UbkpDGCHNKTMSmwWC6ppV08g/VqUxP50cgXdk19u7Atr1AjCmDXdkFJXYeYwg==","flatData":"","keySize":4096}"';
const wh = new Webhook(secret);
// Lanza una excepción en caso de error, devuelve el contenido verificado en caso de éxito
const payload = wh.verify(payload, headers);
// import using composers autoload
require_once('vendor/autoload.php');
// or manually
require_once('/path/to/svix/php/init.php');

$payload = '{"test": 2432232314}';
// Las cabeceras son enviadas por cada notificación 
$header = array(
        'svix-id'  => 'msg_p5jXN8AQM9LWM0D4loKWxJek',
        'svix-timestamp' => '1614265330',
        'svix-signature' => 'v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=',
    );

// Lanza una excepción en caso de error, devuelve el contenido verificado en caso de éxito
$wh = new \Svix\Webhook('whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw');
$json = $wh->verify($payload, $header);
import com.svix.Webhook;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;

import javax.servlet.http.HttpServletRequest;
import java.net.http.HttpHeaders;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class VerificationZenkiWebhookMessage {
    private static final String HEADER_MESSAGE_ID        = "svix-id";
    private static final String HEADER_MESSAGE_SIGNATURE = "svix-signature";
    private static final String HEADER_MESSAGE_TIMESTAMP = "svix-timestamp";


    public void verificationZenkiWebhookMessage(@RequestBody String payload, HttpServletRequest httpRequest) throws Exception {
        String secret = "<signed secret>";

        Map<String, List<String>> headersMap = Collections.list(httpRequest.getHeaderNames())
                .stream()
                .collect(Collectors.toMap(
                        Function.identity(),
                        h -> Collections.list(httpRequest.getHeaders(h))
                ));

        HttpHeaders headers = HttpHeaders.of(headersMap, (headerName, headerValue) -> {
            return StringUtils.isNotEmpty(headerValue) &&
                    Arrays.asList(HEADER_MESSAGE_ID, HEADER_MESSAGE_SIGNATURE, HEADER_MESSAGE_TIMESTAMP).stream().anyMatch(headerValidate -> headerValidate.equals(headerName));
        });
        //Ejemplo del contenido de una notificacion
        // String payload = 	"{\"algorithm\":\"RSA\",\"encryptedData\":\"z7YjgSyx0VzXlDGNC4fjjk1IC69qKN8rRLSItUDY9WXQFgr98ORq/ieJunuCucwk6hmrM9CZAlszE/LD/qSeUtOUcv28ngjobZ5UD+zDqLOeqC5KqHtP0I48L1wC+epXMntsd/KxslWh0+s076K8hZFg7dgJOy2HS46tytNX7AAbEzuQouQo3R0OGV//asG3POej3VQTyRTzKVoRDOO7cVGgNenI4AjfAjUJu+gcOzHqrAj5qr92TEZOZf45+pAk6p5nrfL42NBThO8GB3pXQr2/k74HpkFmVXcZJRB7RDSGfhsFCsnDFZ4N4mHQJWc1/u00z7oGzymPSDGQBEUjzIwGbjBLLDHxdCGKWuwdUq5hAH8Nk55HGOycou7ciBBXOl8E3iTaSxldqOkFLpvkMQ6G2i6dH/1ERKxx61LtQveetkGGMPaLRlsgrUGJNftuKNGEMMQgxn4JykppxHqW3KBlzNhpFUn3QELIctk5SoV12XUDVWi4yhd49F0QlbqfDbRN7ogXo12/SYhUEBS4Wa2uo/mtVKkAdo+GYLtgcggP25y+Qw/I5CenBMJtm2mVFi1b/9AwPaDQo+Yd4S7SrGPyhvcRJcSareIyCXIFSDq6j40qPxGclUv0MLHdwiqxcmmiCP9PQwSnKstCNPBx+IN91E6UfnyBYBhXOWFPZqyHG5OtdBfrx9pIa+0TtFiMBbVGUDidj5QkslyLOZ5Zhx+RMOz+47GpiSg9LaObfJdH4vRsbsZgufvt5hceGE6+lUn3zQzTcwPLaEsQv4HsNMEUKW+tt8K6ZB3GLWaWtII4g0gVlQi2T5P4ZsvBFXf2YJFj4cAL21JVcRanjD2vYk0SbYyuOM2fpBtMJR8pIbVTzdyEw3pPQdyHo4LlbYBFkM3DaXxum9qHr2MHFeAefwJRM79ou5laulfj4nqBPi6hhfT1Z9r9ToDPujOtH+0jRnTIPs4zAWY6rXzUivPJkcu3iqUsqCZcQaU5SHhKli/bHakINyRyTd1ozpdrMsE3rn2VorpgJyVDT47/Bh+xG0F8lCZKofgh4w7DTRxOcIJwkUaPqu30lHHbmc8q0JfGXOgTc496TyjdFx4R529DMzDDkSCVFKp3z8qnG46WsIOIGCp2OXvIiSIihyQFO3FnBx16NbdVlicnTwov4TUPRcsYDIx0p33c3hxOmn1RR1aygYx7XvG3tuMS8ktpfq12ENy3zwpeOit1b8ylnBHwEdaAENaVy03TOLMxIj8rZSfj2AXhnwAKPMLE1AWKufE7OkQAGg2JyJ/H5wB69k9FjwmG0UbkpDGCHNKTMSmwWC6ppV08g/VqUxP50cgXdk19u7Atr1AjCmDXdkFJXYeYwg==\",\"flatData\":\"\",\"keySize\":4096}";

        Webhook webhook = new Webhook(secret);
        webhook.verify(payload, headers);
        // Lanza una excepción en caso de error, devuelve el contenido verificado en caso de éxito
    }
}

Validación de direcciones IP de origen

En caso de que tu endpoint receptor de webhooks contenga la configuración de un firewall o NAT, considera permitir el trafico del siguiente listado de direcciones IP para recibir notificaciones de Zenki.

  • 54.216.8.72
  • 54.173.54.49
  • 52.215.16.239
  • 52.55.123.25
  • 52.6.93.106
  • 63.33.109.123
  • 44.228.126.217
  • 50.112.21.217
  • 52.24.126.164
  • 54.148.139.208



¿Te ha sido útil este artículo?

What's Next