# ST4.2: PCI-DSS Compliance - Context & Analysis

**Blocker:** BLOCKER-002
**Prioridad:** P0 - CRÍTICO
**Esfuerzo Estimado:** 80h
**Fecha:** 2026-01-26
**Estado:** 🔄 ANÁLISIS EN PROGRESO

---

## C: CONTEXTO - Revisión Implementación Actual

### Backend (✅ PCI-DSS Compliant)

#### 1. Payment Intents Implementation

**Archivo:** `apps/backend/src/modules/payments/services/stripe.service.ts`

**Métodos encontrados:**
- ✅ `createPaymentIntent()` (línea 336)
  - Crea Payment Intent en Stripe
  - Retorna `clientSecret` al frontend
  - No maneja datos de tarjeta directamente

- ✅ `confirmPaymentIntent()` (línea 361)
  - Confirma Payment Intent server-side
  - Usado para flujos sin cliente

```typescript
async createPaymentIntent(
  userId: string,
  amount: number,
  currency: string = 'usd',
  metadata: Record<string, string> = {}
): Promise<Stripe.PaymentIntent> {
  const customer = await this.getOrCreateCustomer(userId, userResult.rows[0].email);

  const paymentIntent = await stripe.paymentIntents.create({
    amount: Math.round(amount * 100),
    currency,
    customer: customer.stripeCustomerId,
    metadata: { userId, ...metadata },
  });

  return paymentIntent;
}
```

**Estado:** ✅ Implementación correcta PCI-DSS compliant

---

#### 2. Webhook Handler

**Archivo:** `apps/backend/src/modules/payments/controllers/payments.controller.ts`

**Método:** `handleStripeWebhook()` (líneas 391-490)

**Eventos procesados:**
- ✅ `checkout.session.completed`
- ✅ `customer.subscription.created`
- ✅ `customer.subscription.updated`
- ✅ `customer.subscription.deleted`
- ✅ `invoice.paid`
- ✅ `invoice.payment_failed`

```typescript
export async function handleStripeWebhook(req: Request, res: Response, next: NextFunction): Promise<void> {
  try {
    const signature = req.headers['stripe-signature'] as string;
    const event = stripeService.constructWebhookEvent(req.body, signature);

    switch (event.type) {
      case 'checkout.session.completed': {
        await handleCheckoutComplete(session);
        break;
      }
      // ... more cases
    }

    res.json({ received: true });
  } catch (error) {
    next(error);
  }
}
```

**Estado:** ✅ Webhook handler completo y funcional

---

#### 3. Routes

**Archivo:** `apps/backend/src/modules/payments/payments.routes.ts`

**Rutas encontradas:**
- ✅ `POST /payments/webhook` - Stripe webhook endpoint
- ✅ `POST /payments/checkout` - Create checkout session
- ✅ `POST /payments/wallet/deposit` - Wallet deposit (usado por DepositForm)

**Estado:** ✅ Rutas configuradas correctamente

---

### Frontend - Análisis de Componentes

#### 1. ✅ CORRECTO: DepositForm (PCI-DSS Compliant)

**Archivo:** `apps/frontend/src/modules/investment/components/DepositForm.tsx`

**Implementación:**
```typescript
import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';

// Renderiza CardElement (NO maneja datos de tarjeta)
<CardElement options={cardElementOptions} />

// Confirma pago con Stripe
const { error, paymentIntent } = await stripe.confirmCardPayment(
  clientSecret,
  {
    payment_method: {
      card: cardElement,  // ← Stripe.js maneja datos
    },
  }
);
```

**Flujo:**
1. Usuario completa formulario (monto, cuenta)
2. Frontend llama `POST /api/v1/payments/wallet/deposit`
3. Backend crea Payment Intent → devuelve `clientSecret`
4. Frontend usa `stripe.confirmCardPayment(clientSecret, { payment_method: { card: cardElement } })`
5. Stripe procesa pago sin que frontend toque datos sensibles

**Estado:** ✅ **100% PCI-DSS compliant**

**Mensaje en UI (línea 260):**
> "Your payment is secured by Stripe. We never store your card details."

---

#### 2. ✅ CORRECTO: StripeElementsWrapper (PCI-DSS Compliant)

**Archivo:** `apps/frontend/src/components/payments/StripeElementsWrapper.tsx`

**Implementación:**
```typescript
import { Elements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';

// Carga Stripe.js
const stripe = loadStripe(stripeConfig.publicKey, {
  locale: stripeConfig.locale,
});

// Renderiza Elements provider
<Elements stripe={stripePromise} options={elementsOptions}>
  {children}
</Elements>
```

**Comentario en código (línea 6):**
> "IMPORTANT: This component is required for PCI-DSS compliance.
> All payment forms must be wrapped with this provider."

**Features:**
- Carga Stripe.js desde CDN
- Configura tema oscuro personalizado
- Manejo de errores y loading states
- HOC `withStripeElements()` para wrapping

**Estado:** ✅ **Infraestructura correcta para PCI-DSS**

---

#### 3. ❌ VIOLACIÓN CRÍTICA: PaymentMethodForm

**Archivo:** `apps/frontend/src/components/payments/PaymentMethodForm.tsx`

**🚨 VIOLACIONES DETECTADAS:**

**Violación 1: Estado local almacena datos sensibles (líneas 30-32)**
```typescript
const [cardNumber, setCardNumber] = useState('');  // ❌ PAN completo en memoria
const [expiry, setExpiry] = useState('');          // ❌ Fecha expiración
const [cvc, setCvc] = useState('');                // ❌ CVV/CVC
```

**Violación 2: Inputs directos para datos de tarjeta (líneas 151-202)**
```typescript
<input
  type="text"
  value={cardNumber}
  onChange={(e) => setCardNumber(formatCardNumber(e.target.value))}
  placeholder="1234 5678 9012 3456"
/>
```

**Violación 3: Envío de datos sensibles al backend (líneas 116-128)**
```typescript
const result = await addPaymentMethod({
  type: 'card',
  card: {
    number: cardNumber.replace(/\s/g, ''),           // ❌ Envía PAN completo
    exp_month: parseInt(expiry.split('/')[0]),       // ❌ Envía mes expiración
    exp_year: 2000 + parseInt(expiry.split('/')[1]), // ❌ Envía año expiración
    cvc,                                              // ❌ Envía CVV
  },
  billing_details: {
    name: cardholderName,
  },
  setAsDefault: makeDefault,
});
```

**Comentario en código (línea 114):**
> "In a real implementation, this would use Stripe.js to create a PaymentMethod"

**PCI-DSS Requirements Violated:**
- ❌ Requirement 3: Protect stored cardholder data
- ❌ Requirement 4: Encrypt transmission of cardholder data
- ❌ Requirement 6: Develop secure systems and applications

**Nivel de Severidad:** 🔴 **CRÍTICO**

**Estado de Uso:**
- ✅ **NO está siendo usado** en ninguna página actualmente
- Solo exportado en `components/payments/index.ts`
- Parece ser código legacy/demo

---

#### 4. ✅ CORRECTO: Billing Page

**Archivo:** `apps/frontend/src/modules/payments/pages/Billing.tsx`

**Gestión de métodos de pago:**
- ✅ Usa **Stripe Customer Portal** para agregar métodos de pago (líneas 214-220)
- ✅ Llama `openBillingPortal()` que redirige a portal hosted por Stripe
- ✅ NO usa PaymentMethodForm

```typescript
<button onClick={openBillingPortal}>
  <Plus className="w-4 h-4" />
  Agregar
</button>
```

**Estado:** ✅ **PCI-DSS compliant** (usa portal hosted por Stripe)

---

#### 5. Frontend payment.service.ts

**Archivo:** `apps/frontend/src/services/payment.service.ts`

**Problemas encontrados:**

**❌ Problema 1: No usa apiClient centralizado (líneas 29-43)**
```typescript
// ❌ PROBLEMA: Axios local, no usa apiClient de ST4.1
const api = axios.create({
  baseURL: API_BASE_URL,
  headers: {
    'Content-Type': 'application/json',
  },
});

// ❌ Interceptor duplicado
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('auth_token'); // ❌ Clave diferente: 'auth_token' vs 'token'
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});
```

**Impacto:**
- NO se beneficia del auto-refresh de ST4.1
- Puede causar 401 y logout inesperado
- Inconsistencia con otros servicios

**Prioridad:** ⚠️ ALTA (debe migrarse a apiClient como parte de ST4.1.3)

---

## A: ANÁLISIS - Gaps y Riesgos

### 1. Estado Actual PCI-DSS

| Componente | Estado | Compliance |
|------------|--------|------------|
| Backend Payment Intents | ✅ Implementado | ✅ Compliant |
| Backend Webhooks | ✅ Implementado | ✅ Compliant |
| Frontend DepositForm | ✅ Usa CardElement | ✅ Compliant |
| Frontend StripeElementsWrapper | ✅ Implementado | ✅ Compliant |
| Frontend Billing Page | ✅ Usa Customer Portal | ✅ Compliant |
| **Frontend PaymentMethodForm** | ❌ **Manejo directo datos** | ❌ **VIOLATION** |

**Nivel de Riesgo Global:** 🟡 **MEDIO-BAJO**

**Razón:**
- El componente inseguro **NO está en uso** actualmente
- Los flujos de producción usan implementaciones seguras
- Riesgo: Developer podría usar PaymentMethodForm sin saber que es inseguro

---

### 2. Gaps Identificados

#### Gap 1: Código Legacy Inseguro

**Descripción:** PaymentMethodForm existe pero no cumple PCI-DSS

**Impacto:**
- Riesgo de uso accidental por developers
- Confusión en código base (2 formas de agregar métodos de pago)

**Remediación:**
- ❌ ELIMINAR PaymentMethodForm.tsx
- ✅ DOCUMENTAR que debe usarse Stripe Customer Portal o CardElement

---

#### Gap 2: payment.service.ts no usa apiClient

**Descripción:** Usa axios local en lugar de apiClient centralizado de ST4.1

**Impacto:**
- NO se beneficia de auto-refresh tokens
- Inconsistencia con otros servicios
- Puede causar 401 inesperados

**Remediación:**
- ⚠️ Migrar a apiClient (parte de ST4.1.3)

---

#### Gap 3: Falta documentación PCI-DSS

**Descripción:** No hay documento técnico que explique arquitectura PCI-DSS

**Impacto:**
- Developers no saben qué componentes usar
- Riesgo de implementaciones inseguras futuras

**Remediación:**
- 📄 Crear ET-PAY-006: PCI-DSS Architecture & Guidelines

---

### 3. Arquitectura Actual vs Ideal

#### Arquitectura Actual (Flujos en Uso)

```
┌─────────────────────────────────────────────────────────────┐
│                    FLUJO ACTUAL (SEGURO)                     │
└─────────────────────────────────────────────────────────────┘

1. AGREGAR MÉTODO DE PAGO:
   User → Billing Page → openBillingPortal() → Stripe Customer Portal
                                                  ↓
                                           User adds card
                                                  ↓
                                           Webhook → Backend
                                                  ↓
                                           Store PaymentMethod

2. DEPOSIT TO WALLET:
   User → DepositForm → <CardElement> → stripe.confirmCardPayment()
                            ↓                      ↓
                    (Stripe.js handles)    Backend Payment Intent
                            ↓                      ↓
                       Success/Error         Webhook updates DB

✅ AMBOS FLUJOS SON PCI-DSS COMPLIANT
```

#### Código Legacy (No Usado)

```
┌─────────────────────────────────────────────────────────────┐
│              CÓDIGO LEGACY (INSEGURO - NO USADO)            │
└─────────────────────────────────────────────────────────────┘

PaymentMethodForm:
   User → <input type="text" cardNumber> → addPaymentMethod()
              ↓                                    ↓
       (PAN en memoria)                    POST /api con datos raw
              ↓                                    ↓
         ❌ VIOLATION                         ❌ VIOLATION

❌ NO DEBE USARSE - ELIMINAR
```

---

## P: PLANEACIÓN - Estrategia de Remediación

### Opción 1: ELIMINAR Código Inseguro (RECOMENDADA)

**Acción:**
1. Eliminar `PaymentMethodForm.tsx`
2. Remover export de `components/payments/index.ts`
3. Documentar en ET-PAY-006 que se debe usar:
   - Stripe Customer Portal (para métodos de pago)
   - CardElement (para pagos one-time)

**Ventajas:**
- ✅ Elimina riesgo de uso accidental
- ✅ Simplifica codebase
- ✅ Clarifica arquitectura PCI-DSS

**Desventajas:**
- Ninguna (código no está en uso)

**Esfuerzo:** 0.5h

---

### Opción 2: REFACTOR a Stripe Elements (No Recomendada)

**Acción:**
1. Refactorizar PaymentMethodForm para usar CardElement
2. Integrar con Payment Intents backend
3. Mantener ambas opciones (Portal + Form)

**Ventajas:**
- ✅ Permite agregar métodos sin salir de la app

**Desventajas:**
- ❌ Más código a mantener
- ❌ Stripe Customer Portal ya cubre este caso
- ❌ Duplicación de funcionalidad

**Esfuerzo:** 8h

**Decisión:** ❌ NO RECOMENDADA

---

### Opción 3: MANTENER Status Quo (No Recomendada)

**Acción:**
- No tocar PaymentMethodForm
- Confiar en que no se usará

**Ventajas:**
- Ninguna

**Desventajas:**
- ❌ Riesgo de uso accidental
- ❌ Código legacy confunde a developers
- ❌ No cumple con best practices

**Decisión:** ❌ NO RECOMENDADA

---

## Recomendación Final

### ✅ OPCIÓN 1: Eliminar Código Inseguro + Documentar Arquitectura

**Justificación:**
1. PaymentMethodForm **NO está en uso** → Eliminar es seguro
2. Flujos actuales (Customer Portal + CardElement) **YA cumplen** PCI-DSS
3. Eliminar código inseguro **previene** riesgos futuros
4. Documentación clara **guía** a developers

**Tareas ST4.2:**

| ID | Tarea | Esfuerzo | Prioridad |
|----|-------|----------|-----------|
| ST4.2.1 | ❌ ELIMINAR PaymentMethodForm.tsx | 0.25h | P0 |
| ST4.2.2 | 📄 CREAR ET-PAY-006 PCI-DSS Architecture | 4h | P0 |
| ST4.2.3 | ✅ Validar flujos actuales (tests) | 8h | P1 |
| ST4.2.4 | 🔍 Security audit PCI-DSS SAQ-A | 8h | P1 |
| ST4.2.5 | 📄 ACTUALIZAR developer guidelines | 2h | P2 |

**Total Esfuerzo Revisado:** ~22h (en lugar de 80h originales)

**Ahorro:** 58h (73% reducción) debido a que backend ya está completo y frontend usa implementaciones seguras

---

## Próximos Pasos

1. ✅ **Aprobar plan** (Opción 1)
2. 🔄 **Ejecutar ST4.2.1**: Eliminar PaymentMethodForm
3. 📄 **Ejecutar ST4.2.2**: Crear ET-PAY-006
4. ✅ **Ejecutar ST4.2.3**: Tests E2E de flujos de pago
5. 🔍 **Ejecutar ST4.2.4**: Security audit
6. ✅ **Marcar ST4.2 como COMPLETADO**

---

**Estado:** ⏳ Pendiente aprobación de usuario
**Última actualización:** 2026-01-26
**Autor:** Claude Opus 4.5
