---
id: ET-MKT-004
title: Especificacion de Productos - Marketplace
type: technical-spec
status: Active
priority: High
epic: OQI-009
project: trading-platform
version: 1.0.0
dates:
  created: 2026-01-28
  updated: 2026-01-28
tags:
  - marketplace
  - products
  - business-logic
  - pricing
---

# ET-MKT-004: Especificacion de Productos del Marketplace

## Resumen

Este documento define la logica de negocio, estructura y comportamiento de los productos del Marketplace. Complementa ET-MKT-001 (schema de base de datos) con reglas de negocio detalladas, ciclos de vida de productos y flujos de activacion.

---

## 1. Taxonomia de Productos

### 1.1 Tipos de Producto

```typescript
enum ProductType {
  SIGNAL_PACK = 'signal_pack',        // Paquetes de senales ML
  ADVISORY_SESSION = 'advisory_session', // Sesiones de asesoria
  VISUALIZATION_ADDON = 'visualization_addon', // Indicadores premium
  COURSE = 'course',                  // Cursos educativos
  EBOOK = 'ebook'                     // Libros digitales
}
```

### 1.2 Tipos de Facturacion

```typescript
enum BillingType {
  ONE_TIME = 'one_time',       // Pago unico
  SUBSCRIPTION = 'subscription' // Suscripcion recurrente
}

enum SubscriptionInterval {
  MONTHLY = 'monthly',   // Renovacion mensual
  QUARTERLY = 'quarterly', // Renovacion trimestral
  YEARLY = 'yearly'      // Renovacion anual
}
```

---

## 2. Catalogo de Productos Base

### 2.1 Signal Packs

#### Basic Signal Pack
| Atributo | Valor |
|----------|-------|
| ID | `basic-signal-pack` |
| Nombre | Basic Signal Pack |
| Precio | $9.00 USD |
| Tipo Facturacion | One-time |
| Creditos | 50 senales |
| Validez | 30 dias |
| Confidence Minimo | 80% |
| Canales Entrega | Push, Email |

#### Pro Signal Pack
| Atributo | Valor |
|----------|-------|
| ID | `pro-signal-pack` |
| Nombre | Pro Signal Pack |
| Precio | $29.00 USD |
| Tipo Facturacion | One-time |
| Creditos | 200 senales |
| Validez | 60 dias |
| Confidence Minimo | 80% |
| Canales Entrega | Push, Email |
| Feature Extra | Prioridad en cola |

#### Unlimited Signals
| Atributo | Valor |
|----------|-------|
| ID | `unlimited-signals` |
| Nombre | Unlimited Signals |
| Precio | $49.00 USD/mes |
| Tipo Facturacion | Subscription (monthly) |
| Creditos | Ilimitados |
| Confidence Minimo | 75% |
| Canales Entrega | Push, Email |
| Features Extra | Prioridad maxima, Senales exclusivas |

### 2.2 Advisory Sessions

#### Sesion 30 Minutos
| Atributo | Valor |
|----------|-------|
| ID | `advisory-30` |
| Nombre | Asesoria 30 min |
| Precio | $49.00 USD |
| Duracion | 30 minutos |
| Incluye | Video llamada, Notas post-sesion |
| Politica Cancelacion | 24h antes: 100% reembolso |

#### Sesion 60 Minutos
| Atributo | Valor |
|----------|-------|
| ID | `advisory-60` |
| Nombre | Asesoria 60 min |
| Precio | $89.00 USD |
| Duracion | 60 minutos |
| Incluye | Video llamada, Notas, Plan de accion |
| Politica Cancelacion | 24h antes: 100% reembolso |

#### Sesion 90 Minutos
| Atributo | Valor |
|----------|-------|
| ID | `advisory-90` |
| Nombre | Asesoria 90 min |
| Precio | $119.00 USD |
| Duracion | 90 minutos |
| Incluye | Video llamada, Notas, Plan de accion, Seguimiento 1 semana |
| Politica Cancelacion | 24h antes: 100% reembolso |

### 2.3 Visualization Premium

#### Visualization Premium Subscription
| Atributo | Valor |
|----------|-------|
| ID | `visualization-premium` |
| Nombre | Visualizacion Premium |
| Precio | $19.00 USD/mes |
| Tipo Facturacion | Subscription (monthly) |
| Features | ml_indicators, unlimited_backtest, unlimited_alerts, multi_chart_4 |

---

## 3. Reglas de Negocio

### 3.1 Reglas de Precio

```
RN-PRD-001: Todo producto activo debe tener precio > 0
RN-PRD-002: El precio se almacena siempre en USD
RN-PRD-003: Los cambios de precio no afectan compras existentes
RN-PRD-004: Los precios de suscripcion aplican en la proxima renovacion
RN-PRD-005: Descuentos no pueden reducir precio a 0
```

### 3.2 Reglas de Activacion

```
RN-ACT-001: Signal Packs se activan inmediatamente tras pago exitoso
RN-ACT-002: Subscriptions se activan inmediatamente y renuevan automaticamente
RN-ACT-003: Advisory Sessions requieren agendamiento post-compra
RN-ACT-004: Creditos de senales expiran segun validez del producto
RN-ACT-005: Creditos se consumen en orden FIFO (primero los que expiran antes)
```

### 3.3 Reglas de Visibilidad

```
RN-VIS-001: Solo productos con is_active=true aparecen en catalogo
RN-VIS-002: Productos featured aparecen primero en listados
RN-VIS-003: Productos de categoria inactiva no aparecen en catalogo
RN-VIS-004: Usuarios con suscripcion activa ven badge "Activo" en producto
```

---

## 4. Ciclo de Vida de Productos

### 4.1 Estados de Producto

```
┌─────────────────────────────────────────────────────┐
│                                                     │
│  DRAFT ──────────> ACTIVE ──────────> ARCHIVED     │
│    │                  │                   ▲        │
│    │                  │                   │        │
│    └──────────────────┴───────────────────┘        │
│                                                     │
└─────────────────────────────────────────────────────┘
```

| Estado | Descripcion | Visible | Comprable |
|--------|-------------|---------|-----------|
| DRAFT | En desarrollo | No | No |
| ACTIVE | Disponible para compra | Si | Si |
| ARCHIVED | Descontinuado | No | No |

### 4.2 Transiciones Permitidas

| Desde | Hacia | Condicion |
|-------|-------|-----------|
| DRAFT | ACTIVE | Precio definido, categoria asignada |
| ACTIVE | ARCHIVED | Sin ordenes pendientes |
| ARCHIVED | ACTIVE | Sin restricciones |
| DRAFT | ARCHIVED | Sin restricciones |

---

## 5. Flujos de Activacion por Tipo

### 5.1 Signal Pack (One-time)

```
Usuario compra
    │
    ▼
Pago procesado (Stripe)
    │
    ▼
Crear registro en purchases
    │
    ▼
Crear registro en signal_credits
    ├── initial_amount = producto.metadata.credits
    ├── remaining_amount = producto.metadata.credits
    ├── expires_at = NOW() + producto.metadata.validity_days
    └── is_unlimited = false
    │
    ▼
Enviar email de confirmacion
    │
    ▼
Usuario puede consumir senales
```

### 5.2 Subscription (Unlimited Signals)

```
Usuario suscribe
    │
    ▼
Crear customer en Stripe (si no existe)
    │
    ▼
Crear subscription en Stripe
    │
    ▼
Webhook: subscription.created
    │
    ▼
Crear registro en subscriptions
    ├── status = 'active'
    ├── current_period_start = NOW()
    └── current_period_end = NOW() + 1 month
    │
    ▼
Crear registro en signal_credits
    ├── initial_amount = -1 (ilimitado)
    ├── remaining_amount = -1
    ├── expires_at = subscription.current_period_end
    └── is_unlimited = true
    │
    ▼
Enviar email de bienvenida
    │
    ▼
[Cada renovacion]
Webhook: invoice.paid
    │
    ▼
Actualizar subscription.current_period_end
    │
    ▼
Extender signal_credits.expires_at
```

### 5.3 Advisory Session

```
Usuario compra sesion
    │
    ▼
Pago procesado (Stripe)
    │
    ▼
Crear registro en purchases
    │
    ▼
Usuario redirigido a agendamiento
    │
    ▼
Usuario selecciona asesor
    │
    ▼
Usuario selecciona fecha/hora (Cal.com)
    │
    ▼
Webhook: booking_created
    │
    ▼
Crear registro en advisory_sessions
    ├── status = 'scheduled'
    ├── scheduled_at = fecha seleccionada
    └── cal_booking_uid = booking.uid
    │
    ▼
Crear sala Daily.co
    │
    ▼
Actualizar advisory_sessions.daily_room_url
    │
    ▼
Enviar confirmacion con link
    │
    ▼
[Dia de la sesion]
Ambos se unen a Daily.co
    │
    ▼
Sesion completada
    │
    ▼
Asesor crea notas
    │
    ▼
Usuario puede dejar review
```

### 5.4 Visualization Premium

```
Usuario suscribe
    │
    ▼
Crear subscription en Stripe
    │
    ▼
Webhook: subscription.created
    │
    ▼
Crear registro en subscriptions
    │
    ▼
Crear registro en visualization_subscriptions
    ├── features = producto.metadata.features
    └── is_active = true
    │
    ▼
Frontend consulta subscription
    │
    ▼
Habilitar indicadores premium en charts
    │
    ▼
[Cancelacion]
Subscription cancelada
    │
    ▼
visualization_subscriptions.is_active = false
    │
    ▼
Frontend oculta indicadores premium
```

---

## 6. Consumo de Creditos de Senales

### 6.1 Algoritmo de Consumo

```sql
-- Funcion: Decrementar credito de senal
CREATE OR REPLACE FUNCTION marketplace.consume_signal_credit(
    p_user_id UUID,
    p_signal_id UUID
) RETURNS BOOLEAN AS $$
DECLARE
    v_credit_id UUID;
    v_remaining INTEGER;
    v_is_unlimited BOOLEAN;
BEGIN
    -- Buscar credito valido (orden: primero no-ilimitados que expiran antes)
    SELECT id, remaining_amount, is_unlimited
    INTO v_credit_id, v_remaining, v_is_unlimited
    FROM marketplace.signal_credits
    WHERE user_id = p_user_id
      AND (remaining_amount > 0 OR is_unlimited = true)
      AND (expires_at IS NULL OR expires_at > NOW())
    ORDER BY
        is_unlimited ASC,  -- Primero consumibles
        expires_at ASC NULLS LAST  -- Primero los que expiran antes
    LIMIT 1
    FOR UPDATE;

    IF v_credit_id IS NULL THEN
        RETURN FALSE;  -- Sin creditos disponibles
    END IF;

    -- Decrementar solo si no es ilimitado
    IF NOT v_is_unlimited THEN
        UPDATE marketplace.signal_credits
        SET remaining_amount = remaining_amount - 1,
            updated_at = NOW()
        WHERE id = v_credit_id;
    END IF;

    -- Registrar entrega
    INSERT INTO marketplace.signal_deliveries
        (user_id, signal_id, credit_id, delivery_channel)
    VALUES
        (p_user_id, p_signal_id, v_credit_id, 'both');

    RETURN TRUE;
END;
$$ LANGUAGE plpgsql;
```

### 6.2 Consulta de Balance

```typescript
interface CreditBalance {
  totalAvailable: number;      // -1 si ilimitado
  hasUnlimited: boolean;
  credits: CreditDetail[];
  usage: {
    today: number;
    thisWeek: number;
    thisMonth: number;
  };
}

interface CreditDetail {
  id: string;
  productName: string;
  initialAmount: number;
  remainingAmount: number;
  expiresAt: Date | null;
  isUnlimited: boolean;
}
```

---

## 7. Metadata de Productos

### 7.1 Estructura por Tipo

#### Signal Pack Metadata
```json
{
  "credits": 200,
  "validity_days": 60,
  "min_confidence": 80,
  "delivery_channels": ["push", "email"],
  "priority": "high"
}
```

#### Advisory Session Metadata
```json
{
  "duration_minutes": 60,
  "includes": ["video", "notes", "action_plan"],
  "cancellation_policy": {
    "full_refund_hours": 24,
    "partial_refund_hours": 12,
    "partial_refund_percent": 50
  }
}
```

#### Visualization Addon Metadata
```json
{
  "features": [
    "ml_indicators",
    "unlimited_backtest",
    "unlimited_alerts",
    "multi_chart_4"
  ],
  "indicators": [
    "ml_range_predictor",
    "ml_amd_detector",
    "ml_signal_overlay"
  ]
}
```

---

## 8. Validaciones

### 8.1 Validacion de Compra

```typescript
interface PurchaseValidation {
  isValid: boolean;
  errors: PurchaseError[];
}

enum PurchaseError {
  PRODUCT_NOT_FOUND = 'Producto no encontrado',
  PRODUCT_INACTIVE = 'Producto no disponible',
  ALREADY_SUBSCRIBED = 'Ya tienes suscripcion activa',
  PAYMENT_METHOD_INVALID = 'Metodo de pago invalido',
  INSUFFICIENT_FUNDS = 'Fondos insuficientes',
  USER_NOT_VERIFIED = 'Usuario no verificado'
}

function validatePurchase(userId: string, productId: string): PurchaseValidation {
  const product = getProduct(productId);

  if (!product) {
    return { isValid: false, errors: [PurchaseError.PRODUCT_NOT_FOUND] };
  }

  if (!product.isActive) {
    return { isValid: false, errors: [PurchaseError.PRODUCT_INACTIVE] };
  }

  if (product.billingType === 'subscription') {
    const existing = getActiveSubscription(userId, productId);
    if (existing) {
      return { isValid: false, errors: [PurchaseError.ALREADY_SUBSCRIBED] };
    }
  }

  return { isValid: true, errors: [] };
}
```

### 8.2 Validacion de Consumo de Senal

```typescript
function canConsumeSignal(userId: string): boolean {
  const balance = getCreditBalance(userId);
  return balance.hasUnlimited || balance.totalAvailable > 0;
}
```

---

## 9. Integracion con Stripe

### 9.1 Mapeo de Productos

| Producto Local | Stripe Product | Stripe Price |
|----------------|----------------|--------------|
| basic-signal-pack | prod_BasicSignals | price_9USD_once |
| pro-signal-pack | prod_ProSignals | price_29USD_once |
| unlimited-signals | prod_UnlimitedSignals | price_49USD_monthly |
| advisory-30 | prod_Advisory30 | price_49USD_once |
| advisory-60 | prod_Advisory60 | price_89USD_once |
| advisory-90 | prod_Advisory90 | price_119USD_once |
| visualization-premium | prod_VisPremium | price_19USD_monthly |

### 9.2 Webhooks Relevantes

| Evento | Accion |
|--------|--------|
| payment_intent.succeeded | Activar producto comprado |
| customer.subscription.created | Crear registro de suscripcion |
| customer.subscription.updated | Actualizar periodo |
| customer.subscription.deleted | Marcar cancelada |
| invoice.paid | Renovar creditos/acceso |
| invoice.payment_failed | Notificar usuario, reintentar |

---

## 10. Metricas y Analytics

### 10.1 KPIs por Producto

```sql
-- Ventas por producto (ultimo mes)
SELECT
    p.name,
    p.type,
    COUNT(pu.id) as total_sales,
    SUM(pu.total_price) as revenue,
    AVG(pu.total_price) as avg_order_value
FROM marketplace.products p
JOIN marketplace.purchases pu ON p.id = pu.product_id
WHERE pu.status = 'completed'
  AND pu.completed_at > NOW() - INTERVAL '30 days'
GROUP BY p.id, p.name, p.type
ORDER BY revenue DESC;
```

### 10.2 Metricas de Senales

```sql
-- Utilizacion de creditos
SELECT
    u.id as user_id,
    SUM(CASE WHEN sc.is_unlimited THEN 0 ELSE sc.initial_amount END) as credits_purchased,
    SUM(CASE WHEN sc.is_unlimited THEN 0 ELSE sc.remaining_amount END) as credits_remaining,
    COUNT(sd.id) as signals_consumed
FROM auth.users u
LEFT JOIN marketplace.signal_credits sc ON u.id = sc.user_id
LEFT JOIN marketplace.signal_deliveries sd ON u.id = sd.user_id
GROUP BY u.id;
```

---

## 11. Referencias

- [ET-MKT-001: Database Schema](./ET-MKT-001-database.md)
- [ET-MKT-002: API REST](./ET-MKT-002-api.md)
- [ET-MKT-003: Frontend](./ET-MKT-003-frontend.md)
- [RF-MKT-001: Catalogo](../requerimientos/RF-MKT-001-catalogo.md)
- [RF-MKT-002: Senales Premium](../requerimientos/RF-MKT-002-senales-premium.md)

---

**Documento Generado:** 2026-01-28
**Estado:** Activo
**Proxima Revision:** Tras inicio de implementacion
