#!/usr/bin/env bash # Prueba tu receptor de webhooks en tu máquina. No llama a FITAL ni a ningún servicio alojado. # Uso: ./fital-webhook-test.sh [tipo-de-evento] # Firma con un secreto público de prueba: configúralo en tu receptor sólo en desarrollo. # Sale con 0 sólo si cada envío recibe lo esperado: 2xx en los válidos y 4xx en los que debe rechazar. # Sólo ve el código HTTP. Que un duplicado no se procese dos veces se comprueba en el registro de tu receptor. set -euo pipefail URL="${1:?falta la URL de tu receptor}" TIPO="${2:-payment.completed}" SECRETO="${FITAL_WEBHOOK_SECRET:-secreto_de_prueba_publico_no_usar_en_produccion}" enviar() { # enviar [cuerpo-alterado|timestamp-texto|otro-encabezado] local id="$1" seq="$2" tipo="$3" sec="$4" desfase="$5" esperado="$6" truco="${7:-}" ts cuerpo firma codigo encabezado="$1" ts=$(( $(date +%s) + desfase )) if [ "$truco" = timestamp-texto ]; then ts="${ts}a"; fi cuerpo=$(printf '{"id":"%s","type":"%s","resource_id":"pay_demo","api_version":"2026-11-01","created_at":"%s","sequence":%s,"data":{"id":"pay_demo"}}' "$id" "$tipo" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$seq") firma=$(printf '%s' "$ts.$cuerpo" | openssl dgst -sha256 -hmac "$sec" -r | cut -d' ' -f1) if [ "$truco" = cuerpo-alterado ]; then cuerpo="$cuerpo "; fi if [ "$truco" = otro-encabezado ]; then encabezado="${id}_otro"; fi # curl imprime 000 si no conecta: el fallo se reporta abajo, no aquí. codigo=$(curl -s -o /dev/null --max-time 15 -w '%{http_code}' -X POST "$URL" -H 'Content-Type: application/json' \ -H "X-Fital-Timestamp: $ts" -H "X-Fital-Signature: v1=$firma" \ -H "X-Fital-Event-Id: $encabezado" -H "X-Fital-Delivery-Id: dlv_$RANDOM" --data-binary "$cuerpo") || true case "$esperado:$codigo" in ok:2*|rechazo:4*) echo "ok $id $tipo -> $codigo" ;; *) echo "FALLA $id $tipo -> $codigo, se esperaba $esperado"; FALLAS=$((FALLAS + 1)) ;; esac } FALLAS=0 enviar evt_prueba_1 1 "$TIPO" "$SECRETO" 0 ok enviar evt_prueba_2 2 payment.failed "$SECRETO" 0 ok enviar evt_prueba_3 3 payment.returned "$SECRETO" 0 ok enviar evt_prueba_mala 4 "$TIPO" otro-secreto 0 rechazo # firma de otro secreto enviar evt_prueba_vieja 5 "$TIPO" "$SECRETO" -3000 rechazo # timestamp vencido enviar evt_prueba_futura 5 "$TIPO" "$SECRETO" 3000 rechazo # timestamp en el futuro enviar evt_prueba_alterada 5 "$TIPO" "$SECRETO" 0 rechazo cuerpo-alterado # un espacio después de firmar enviar evt_prueba_texto 5 "$TIPO" "$SECRETO" 0 rechazo timestamp-texto # firma válida con timestamp no numérico enviar evt_prueba_1 1 "$TIPO" "$SECRETO" 0 ok # duplicado: 2xx, sin procesarlo otra vez enviar evt_prueba_1 1 "$TIPO" "$SECRETO" 0 ok otro-encabezado # duplicado con otro X-Fital-Event-Id: manda el id del cuerpo enviar evt_prueba_6 6 payment.completed "$SECRETO" 0 ok enviar evt_prueba_5 5 payment.processing "$SECRETO" 0 ok # orden invertido: gana el sequence mayor exit "$FALLAS"