🔍
CULTIVA IA — Revisor de Código Automatizado
PR #47 — Módulo de pagos v2
📁 feature/payments-v2 → main 👤 Carlos Mendez 📅 12 jun 2026 📄 3 archivos · 669 líneas 🏢 NexusPay SaaS
⛔ BLOQUEAR MERGE
BLOQUEAR — No apto para producción
Se detectaron 4 secretos hardcodeados (API keys y contraseñas en texto plano), múltiples inyecciones SQL por concatenación de strings, y un bloque except: pass que silencia errores críticos. Este PR no puede mergearse hasta resolver las incidencias de seguridad marcadas como CRÍTICO.
82
/ 100 — Nota B
Promedio 3 archivos
4
Secretos expuestos
API keys, passwords en texto plano
6
SQL Injections
Concatenación de strings en queries
24
Code Smells
3 high · 5 medium · 16 low
3
Archivos revisados
Python 3.11 · 669 líneas totales
🚨 Problemas críticos de seguridad
🔑 Secretos hardcodeados detectados — CRÍTICO
  • STRIPE_SECRET_KEY = "sk_live_abc123xyz789secretkey" — clave Stripe live en texto plano (línea 21, payment_processor.py)
  • DB_PASSWORD = "nexuspay_prod_pass_2024" — contraseña de base de datos expuesta (línea 22)
  • REDIS_PASSWORD = "redis_secret_abc" — credencial Redis en código fuente (línea 23)
  • WEBHOOK_SECRET = "whsec_live_secret_key_789abc" — webhook secret de Stripe en webhook_handler.py (línea 14)
💉 Inyección SQL por concatenación de strings — ALTO
  • cursor.execute("SELECT * FROM customers WHERE id = '" + customer_id + "'") — payment_processor.py:67
  • cursor.execute("SELECT * FROM payments WHERE id = '" + payment_id + "'") — payment_processor.py:171
  • Función get_payment_history construye query con f-string interpolando parámetros no sanitizados (líneas 185–191)
  • _check_fraud también usa concatenación para query SQL (línea 145)
⚠ Excepción silenciada — ALTO
  • except: pass en _send_email() — fallos de email se ignoran silenciosamente sin log ni alerta (payment_processor.py:168)
  • Implementar al menos: except Exception as e: logger.error(f"Email failed: {e}")
📊 Resumen por archivo
Archivo Nota Puntuación Líneas Funciones Smells Complejidad Avg
payment_processor.py C
78/100
253 8 13 6.6
subscription_manager.py B
84/100
259 7 5 6.4
webhook_handler.py B
85/100
157 1 6 9.0
📄 Detalle por archivo
🐍 payment_processor.py
253 líneas
8 funciones
1 clase
🔥 Complejidad avg 6.6
C
Puntuación de calidad78 / 100
Smells detectados (13)
medium Function 'process_payment' tiene 89 líneas — supera el límite de 50 process_payment
medium Function 'process_payment' tiene complejidad ciclomática 19 — supera límite de 10 process_payment
low Function 'process_payment' tiene 11 parámetros — máximo recomendado: 5 process_payment
low Function 'refund_payment' tiene 7 parámetros — usar objeto de request DTO refund_payment
low Function 'get_payment_history' tiene 7 parámetros — usar objeto de filtros get_payment_history
low Magic number 30000 — usar constante TIMEOUT_MS línea 43
low Magic number 86400 — usar constante CACHE_TTL_SECONDS línea 105
low Magic number 10000 — usar constante MAX_AMOUNT_FRAUD_THRESHOLD línea 148
Métricas de funciones
Función Líneas Parámetros Complejidad
process_payment 89 ⚠ 11 ⚠
19
_check_fraud 21 4
6
refund_payment 25 7 ⚠
7
get_payment_history 15 7 ⚠
8
calculate_fees 32 7 ⚠
7
_save_card 12 3
3
🐍 subscription_manager.py
259 líneas
7 funciones
1 clase
Complejidad avg 6.4
B
Puntuación de calidad84 / 100
Smells detectados (5)
medium Function 'create_subscription' tiene 62 líneas — refactorizar en helpers privados create_subscription
medium Function 'create_subscription' tiene complejidad 12 — separar validación de lógica Stripe create_subscription
low Function 'create_subscription' tiene 6 parámetros — usar dataclass SubscriptionRequest create_subscription
low Function 'apply_discount' tiene 7 parámetros — consolidar en DiscountRequest DTO apply_discount
low Magic number 300 en get_subscription_status — usar constante SUBSCRIPTION_CACHE_TTL línea 179
🐍 webhook_handler.py
157 líneas
1 función
Complejidad 9.0
B
Puntuación de calidad85 / 100
Smells detectados (6)
medium Function 'handle_stripe_webhook' tiene 57 líneas — extraer handlers por tipo de evento handle_stripe_webhook
low Magic number 400 — usar HTTPStatus.BAD_REQUEST o constante HTTP_400 líneas 32, 34
low Magic number 86400 — usar constante WEBHOOK_DEDUP_TTL línea 154
low Magic numbers 100 (x2) para divisor de centavos — usar constante STRIPE_AMOUNT_DIVISOR líneas 48, 124
📋 Recomendaciones priorizadas para Carlos
🚫
CRÍTICO: Mover todos los secretos a variables de entorno
Revocar inmediatamente las claves expuestas en el repositorio. Usar os.environ.get("STRIPE_SECRET_KEY") o un gestor de secretos (AWS Secrets Manager, Doppler). Añadir los nombres de variables a .env.example sin valores reales.
payment_processor.py:21-24 · webhook_handler.py:14
💉
CRÍTICO: Eliminar todas las inyecciones SQL
Reemplazar concatenación de strings por parámetros preparados: cursor.execute("SELECT * FROM customers WHERE id = %s", (customer_id,)). Afecta a todas las queries dinámicas en process_payment, _check_fraud, refund_payment y get_payment_history.
payment_processor.py:67, 145, 171, 185-191
🔄
ALTO: Descomponer process_payment (89 líneas, complejidad 19)
Extraer lógica en métodos privados: _validate_input(), _create_stripe_intent(), _post_payment_tasks(). Usar un dataclass PaymentRequest para reducir los 11 parámetros a 1.
payment_processor.py:56-149
🔇
ALTO: Corregir except silencioso en _send_email
El bloque except: pass oculta errores de envío de email. Como mínimo: except Exception as e: logger.error("Email send failed", exc_info=True). Considerar también mover email a un servicio asíncrono (Celery/SES).
payment_processor.py:168
📦
MEDIO: Consolidar parámetros excesivos con DTOs/dataclasses
Funciones con 6-11 parámetros deben agruparse en objetos: @dataclass PaymentRequest, @dataclass RefundRequest, @dataclass SubscriptionRequest. Mejora legibilidad y facilita validación con Pydantic.
payment_processor.py · subscription_manager.py
🧹
BAJO: Definir constantes para todos los magic numbers
Crear un archivo constants.py con: CACHE_TTL = 86400, STRIPE_CENTS = 100, FRAUD_THRESHOLD = 75, etc. Elimina ambigüedad y facilita ajustes futuros.
todos los archivos