Saltar al contenido principal

Python

El ejemplo en Python utiliza el framework Flask y la librería cryptography.

1. Recepción de la Notificación (Flask)

from flask import Flask, request, jsonify
from signature_validator import validate_sypago_signature, SignatureValidationError

app = Flask(__name__)

@app.route('/webhook-sypago', methods=['POST'])
def webhook_sypago():
try:
# 1. Extraer los componentes de la notificación
signature = request.headers.get('X-Signature')
if not signature:
return "Cabecera X-Signature es requerida", 400

nonce = request.headers.get('X-Signature-Nonce')
if not nonce:
return "Cabecera X-Signature-Nonce es requerida", 400

# Importante: obtener el cuerpo crudo de la solicitud
payload = request.get_data()

# 2. Validar la firma
validate_sypago_signature(signature, payload, nonce)

# 3. La firma es válida. Procesar la notificación.
print("Firma válida. Procesando notificación...")
# notification_data = request.get_json()
# ... lógica de negocio ...

return jsonify({"message": "Firma válida y notificación recibida"}), 200

except SignatureValidationError as e:
print(f"Error al validar la firma: {e}")
return str(e), 401
except Exception as e:
print(f"Error inesperado: {e}")
return "Error interno del servidor", 500

# if __name__ == '__main__':
# app.run(port=3000)

2. Validación de la Firma

Asegúrese de instalar la librería cryptography: pip install cryptography.

import base64
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import load_pem_public_key

class SignatureValidationError(Exception):
pass

def validate_sypago_signature(signature_b64: str, payload: bytes, nonce: str):
# --- Obtención de Secretos ---
# El `operation_secret` DEBE ser recuperado de su base de datos.
# Lo guardó cuando inició la transacción con SyPago.
operation_secret = "9f4aaf08-8d04-4007-a097-c0e95eddad5e" # ¡EJEMPLO!

# La `public_key_pem` DEBE ser obtenida del endpoint GET /api/v1/user/key
# usando su token JWT. Es recomendable cachear esta clave.
public_key_pem = b"""-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9hXXl4g886loHmI10dLrJWFHEB8r
cyqD1hBdIM3ekQkb5YTpOShAu+7xk0fyL/0IBjLEKwSd7rsKrYGtIgJj0w==
-----END PUBLIC KEY-----""" # ¡EJEMPLO!

# --- Proceso de Verificación ---

# 1. Reconstruir el mensaje a verificar
# El payload ya está en bytes, lo cual es ideal.
string_to_verify = b'.'.join([
payload,
nonce.encode('utf-8'),
operation_secret.encode('utf-8')
])

try:
# 2. Cargar la clave pública PEM
public_key = load_pem_public_key(public_key_pem, backend=None)

# 3. Decodificar la firma Base64
signature_bytes = base64.b64decode(signature_b64)

# 4. Verificar la firma
# La librería verificará el hash (SHA-256) contra la firma (formato ASN.1 DER)
public_key.verify(
signature_bytes,
string_to_verify,
ec.ECDSA(hashes.SHA256())
)

# La firma es válida, la función no retorna nada si es exitosa.

except InvalidSignature:
raise SignatureValidationError("La firma no es válida")
except Exception as e:
# Capturar otros errores (p. ej., mal formato de clave)
raise SignatureValidationError(f"Error en el proceso de validación: {e}")