# OQI-005: Contratos API - Especificación de Endpoints

**Módulo:** OQI-005 (pagos-stripe)
**Base URL:** `/api/v1/payments`
**Autenticación:** Bearer Token (header: Authorization)
**Formato:** JSON
**Fecha:** 2026-01-25

---

## 1. Planes Pricing

### GET /payments/plans
**Descripción:** Obtiene lista de planes activos
**Autenticación:** No requerida
**Parámetros Query:** Ninguno

**Response 200:**
```json
{
  "success": true,
  "data": [
    {
      "id": "plan_free",
      "name": "Free",
      "slug": "free",
      "tier": "free",
      "description": "Get started with basic features",
      "priceMonthly": 0,
      "priceYearly": 0,
      "stripePriceIdMonthly": null,
      "stripePriceIdYearly": null,
      "features": [
        {
          "id": "feat_api_calls",
          "name": "API Calls",
          "description": "Basic API access",
          "included": true,
          "value": "100"
        },
        {
          "id": "feat_paper_trades",
          "name": "Paper Trades",
          "description": "Practice trading",
          "included": true,
          "value": "50"
        }
      ],
      "limits": {
        "maxCourses": 3,
        "maxApiCalls": 100,
        "maxPaperTrades": 50,
        "maxWatchlistSymbols": 25,
        "mlSignalsAccess": false,
        "prioritySupport": false,
        "customAgents": false
      },
      "isPopular": false,
      "isActive": true
    },
    {
      "id": "plan_pro",
      "name": "Pro",
      "slug": "pro",
      "tier": "pro",
      "description": "Professional trader features",
      "priceMonthly": 49.99,
      "priceYearly": 499.99,
      "stripePriceIdMonthly": "price_1234567890",
      "stripePriceIdYearly": "price_0987654321",
      "features": [...],
      "limits": {
        "maxCourses": -1,
        "maxApiCalls": 10000,
        "maxPaperTrades": -1,
        "maxWatchlistSymbols": 500,
        "mlSignalsAccess": true,
        "prioritySupport": false,
        "customAgents": false
      },
      "isPopular": true,
      "isActive": true
    }
  ]
}
```

**Error 400:**
```json
{
  "success": false,
  "error": {
    "message": "Failed to fetch plans",
    "code": "FETCH_PLANS_ERROR"
  }
}
```

---

### GET /payments/plans/{slug}
**Descripción:** Obtiene detalle de plan específico
**Autenticación:** No requerida
**Parámetros Path:** `slug` (string, e.g., "pro", "free")

**Response 200:** (Mismo formato que GET /payments/plans, pero single item)

**Error 404:**
```json
{
  "success": false,
  "error": {
    "message": "Plan not found",
    "code": "PLAN_NOT_FOUND"
  }
}
```

---

## 2. Suscripciones

### GET /payments/subscription
**Descripción:** Obtiene suscripción actual del usuario
**Autenticación:** Requerida
**Parámetros Query:** Ninguno

**Response 200:**
```json
{
  "success": true,
  "data": {
    "id": "sub_1234567890",
    "userId": "user_abc123",
    "planId": "plan_pro",
    "planName": "Pro",
    "planTier": "pro",
    "status": "active",
    "interval": "month",
    "amount": 49.99,
    "currency": "USD",
    "currentPeriodStart": "2026-01-01T00:00:00Z",
    "currentPeriodEnd": "2026-02-01T00:00:00Z",
    "cancelAtPeriodEnd": false,
    "canceledAt": null,
    "trialEnd": null,
    "stripeSubscriptionId": "sub_stripe_id",
    "stripeCustomerId": "cus_stripe_id",
    "createdAt": "2026-01-01T00:00:00Z",
    "updatedAt": "2026-01-25T10:30:00Z",
    "plan": {
      "id": "plan_pro",
      "name": "Pro",
      "limits": { ... }
    }
  }
}
```

**Response 200 (sin suscripción):**
```json
{
  "success": true,
  "data": null
}
```

**Error 401:**
```json
{
  "success": false,
  "error": {
    "message": "Unauthorized",
    "code": "UNAUTHORIZED"
  }
}
```

---

### GET /payments/subscription/history
**Descripción:** Obtiene historial de suscripciones
**Autenticación:** Requerida
**Parámetros Query:** Ninguno

**Response 200:**
```json
{
  "success": true,
  "data": [
    {
      "id": "sub_old",
      "planId": "plan_basic",
      "status": "canceled",
      "amount": 29.99,
      "currentPeriodEnd": "2025-12-01T00:00:00Z",
      "canceledAt": "2025-12-01T00:00:00Z",
      ...
    },
    {
      "id": "sub_current",
      "planId": "plan_pro",
      "status": "active",
      ...
    }
  ]
}
```

---

### POST /payments/subscriptions
**Descripción:** Crear suscripción nueva (uso interno, checkout crea esta)
**Autenticación:** Requerida
**Body:**
```json
{
  "planId": "plan_pro",
  "interval": "month",
  "paymentMethodId": "pm_123456",
  "couponCode": "WELCOME10"
}
```

**Response 201:**
```json
{
  "success": true,
  "data": {
    "subscription": {
      "id": "sub_new",
      "planId": "plan_pro",
      "status": "active",
      "amount": 49.99,
      ...
    },
    "clientSecret": "pi_1234567890_secret_abcdefg"
  }
}
```

**Error 400:**
```json
{
  "success": false,
  "error": {
    "message": "Invalid plan or payment method",
    "code": "INVALID_INPUT"
  }
}
```

---

### PUT /payments/subscriptions/{id}
**Descripción:** Actualizar suscripción (cambiar plan, intervalo)
**Autenticación:** Requerida
**Parámetros Path:** `id` (subscription ID)
**Body:**
```json
{
  "planId": "plan_enterprise",
  "billingCycle": "yearly"
}
```

**Response 200:**
```json
{
  "success": true,
  "data": {
    "id": "sub_updated",
    "planId": "plan_enterprise",
    "status": "active",
    "amount": 999.99,
    ...
  }
}
```

---

### DELETE /payments/subscriptions/{id}
**Descripción:** Cancelar suscripción
**Autenticación:** Requerida
**Parámetros Path:** `id` (subscription ID)
**Parámetros Query:** `immediately=true|false` (cancela al final período por default)

**Response 200:**
```json
{
  "success": true,
  "data": {
    "id": "sub_canceled",
    "status": "canceled",
    "canceledAt": "2026-01-25T10:30:00Z",
    "currentPeriodEnd": "2026-02-01T00:00:00Z",
    ...
  }
}
```

---

### POST /payments/subscription/change-plan
**Descripción:** Cambiar a plan diferente
**Autenticación:** Requerida
**Body:**
```json
{
  "planId": "plan_enterprise",
  "billingCycle": "yearly"
}
```

**Response 200:**
```json
{
  "success": true,
  "data": {
    "id": "sub_updated",
    "planId": "plan_enterprise",
    "status": "active",
    "amount": 999.99,
    "currentPeriodEnd": "2027-01-01T00:00:00Z",
    ...
  }
}
```

---

### POST /payments/subscription/cancel
**Descripción:** Cancelar suscripción actual
**Autenticación:** Requerida
**Body:**
```json
{
  "immediately": false
}
```

**Response 200:**
```json
{
  "success": true,
  "data": {
    "id": "sub_canceled",
    "status": "canceled",
    "cancelAtPeriodEnd": true,
    "currentPeriodEnd": "2026-02-01T00:00:00Z",
    "canceledAt": "2026-01-25T10:30:00Z",
    ...
  }
}
```

---

### POST /payments/subscription/resume
**Descripción:** Reactivar suscripción cancelada
**Autenticación:** Requerida
**Body:** Vacío

**Response 200:**
```json
{
  "success": true,
  "data": {
    "id": "sub_resumed",
    "status": "active",
    "cancelAtPeriodEnd": false,
    ...
  }
}
```

---

## 3. Checkout

### POST /payments/checkout
**Descripción:** Crear sesión de checkout Stripe
**Autenticación:** Requerida
**Body:**
```json
{
  "planId": "plan_pro",
  "billingCycle": "monthly",
  "successUrl": "https://myapp.com/checkout/success?session_id={CHECKOUT_SESSION_ID}",
  "cancelUrl": "https://myapp.com/checkout/cancel"
}
```

**Response 201:**
```json
{
  "success": true,
  "data": {
    "sessionId": "cs_1234567890",
    "url": "https://checkout.stripe.com/pay/cs_1234567890",
    "clientSecret": null
  }
}
```

**Error 400:**
```json
{
  "success": false,
  "error": {
    "message": "Invalid plan or billing cycle",
    "code": "INVALID_CHECKOUT_PARAMS"
  }
}
```

---

### POST /payments/billing-portal
**Descripción:** Crear sesión de portal de facturación Stripe
**Autenticación:** Requerida
**Body:**
```json
{
  "returnUrl": "https://myapp.com/settings/billing"
}
```

**Response 201:**
```json
{
  "success": true,
  "data": {
    "url": "https://billing.stripe.com/b/aHU...",
    "sessionId": "bps_1234567890"
  }
}
```

---

## 4. Métodos de Pago

### GET /payments/methods
**Descripción:** Obtiene métodos de pago guardados
**Autenticación:** Requerida

**Response 200:**
```json
{
  "success": true,
  "data": [
    {
      "id": "pm_123456",
      "userId": "user_abc",
      "type": "card",
      "brand": "Visa",
      "last4": "4242",
      "expiryMonth": 12,
      "expiryYear": 2026,
      "isDefault": true,
      "stripePaymentMethodId": "pm_stripe_123",
      "createdAt": "2026-01-01T00:00:00Z"
    },
    {
      "id": "pm_789012",
      "type": "card",
      "brand": "Mastercard",
      "last4": "5555",
      "expiryMonth": 8,
      "expiryYear": 2025,
      "isDefault": false,
      "createdAt": "2026-01-15T00:00:00Z"
    }
  ]
}
```

---

### POST /payments/methods
**Descripción:** Agregar nuevo método de pago
**Autenticación:** Requerida
**Body:**
```json
{
  "paymentMethodId": "pm_stripe_token_from_client",
  "setAsDefault": true
}
```

**Response 201:**
```json
{
  "success": true,
  "data": {
    "id": "pm_newmethod",
    "userId": "user_abc",
    "type": "card",
    "brand": "Visa",
    "last4": "1234",
    "expiryMonth": 6,
    "expiryYear": 2027,
    "isDefault": true,
    "createdAt": "2026-01-25T10:30:00Z"
  }
}
```

**Error 400:**
```json
{
  "success": false,
  "error": {
    "message": "Invalid payment method",
    "code": "INVALID_PAYMENT_METHOD"
  }
}
```

---

### POST /payments/methods/default
**Descripción:** Establecer método de pago como predeterminado
**Autenticación:** Requerida
**Body:**
```json
{
  "paymentMethodId": "pm_123456"
}
```

**Response 200:**
```json
{
  "success": true,
  "data": {
    "id": "pm_123456",
    "isDefault": true,
    "brand": "Visa",
    "last4": "4242",
    ...
  }
}
```

---

### DELETE /payments/methods/{id}
**Descripción:** Eliminar método de pago
**Autenticación:** Requerida
**Parámetros Path:** `id` (payment method ID)

**Response 204:** (Sin contenido)

**Error 400:**
```json
{
  "success": false,
  "error": {
    "message": "Cannot delete default payment method",
    "code": "INVALID_OPERATION"
  }
}
```

---

## 5. Facturas

### GET /payments/invoices
**Descripción:** Obtiene lista de facturas
**Autenticación:** Requerida
**Parámetros Query:**
- `page` (number, default: 1)
- `limit` (number, default: 20)
- `status` (string, optional: "draft", "open", "paid", "void")

**Response 200:**
```json
{
  "success": true,
  "data": {
    "invoices": [
      {
        "id": "inv_123456",
        "subscriptionId": "sub_abc",
        "number": "INV-2026-001",
        "amount": 49.99,
        "currency": "USD",
        "status": "paid",
        "periodStart": "2026-01-01T00:00:00Z",
        "periodEnd": "2026-02-01T00:00:00Z",
        "pdfUrl": "https://stripe.com/pdf/inv_123456",
        "hostedInvoiceUrl": "https://invoice.stripe.com/i/...",
        "dueDate": "2026-02-01T00:00:00Z",
        "paidAt": "2026-01-01T10:00:00Z",
        "createdAt": "2026-01-01T00:00:00Z"
      }
    ],
    "total": 15,
    "page": 1
  }
}
```

---

### GET /payments/invoices/{id}
**Descripción:** Obtiene detalle de factura específica
**Autenticación:** Requerida
**Parámetros Path:** `id` (invoice ID)

**Response 200:**
```json
{
  "success": true,
  "data": {
    "id": "inv_123456",
    "number": "INV-2026-001",
    "status": "paid",
    "amount": 49.99,
    "subtotal": 49.99,
    "tax": 0,
    "discount": 0,
    "currency": "USD",
    "periodStart": "2026-01-01T00:00:00Z",
    "periodEnd": "2026-02-01T00:00:00Z",
    "pdfUrl": "https://stripe.com/pdf/inv_123456",
    "hostedInvoiceUrl": "https://invoice.stripe.com/i/...",
    "dueDate": "2026-02-01T00:00:00Z",
    "paidAt": "2026-01-01T10:00:00Z",
    "lineItems": [
      {
        "id": "li_123",
        "description": "Pro Plan - Monthly",
        "quantity": 1,
        "unitPrice": 49.99,
        "amount": 49.99
      }
    ],
    "billingDetails": {
      "name": "John Doe",
      "email": "john@example.com",
      "company": "Acme Corp",
      "address": {
        "line1": "123 Main St",
        "city": "New York",
        "state": "NY",
        "postalCode": "10001",
        "country": "US"
      }
    },
    "paymentMethod": {
      "type": "card",
      "brand": "Visa",
      "last4": "4242"
    },
    "createdAt": "2026-01-01T00:00:00Z"
  }
}
```

---

### GET /payments/invoices/{id}/pdf
**Descripción:** Descarga PDF de factura
**Autenticación:** Requerida
**Parámetros Path:** `id` (invoice ID)

**Response 200:** Binary PDF file
**Content-Type:** application/pdf

---

## 6. Información de Facturación

### GET /payments/billing-info
**Descripción:** Obtiene información de facturación guardada
**Autenticación:** Requerida

**Response 200:**
```json
{
  "success": true,
  "data": {
    "name": "John Doe",
    "email": "john@example.com",
    "phone": "+1-555-123-4567",
    "company": "Acme Corp",
    "taxId": "12-3456789",
    "address": {
      "line1": "123 Main St",
      "line2": "Suite 100",
      "city": "New York",
      "state": "NY",
      "postalCode": "10001",
      "country": "US"
    }
  }
}
```

**Response 200 (sin datos):**
```json
{
  "success": true,
  "data": null
}
```

---

### PUT /payments/billing-info
**Descripción:** Actualiza información de facturación
**Autenticación:** Requerida
**Body:**
```json
{
  "name": "John Doe",
  "email": "john@example.com",
  "phone": "+1-555-123-4567",
  "company": "Acme Corp",
  "taxId": "12-3456789",
  "address": {
    "line1": "123 Main St",
    "line2": "Suite 100",
    "city": "New York",
    "state": "NY",
    "postalCode": "10001",
    "country": "US"
  }
}
```

**Response 200:** (Mismo formato que GET)

---

## 7. Uso de Plan

### GET /payments/usage
**Descripción:** Obtiene estadísticas de uso del plan actual
**Autenticación:** Requerida

**Response 200:**
```json
{
  "success": true,
  "data": {
    "apiCalls": {
      "used": 7250,
      "limit": 10000,
      "percentage": 72.5
    },
    "paperTrades": {
      "used": 450,
      "limit": -1,
      "percentage": 0
    },
    "coursesEnrolled": {
      "used": 8,
      "limit": -1,
      "percentage": 0
    },
    "watchlistSymbols": {
      "used": 350,
      "limit": 500,
      "percentage": 70
    }
  }
}
```

---

## 8. Wallet

### GET /payments/wallet
**Descripción:** Obtiene wallet actual
**Autenticación:** Requerida

**Response 200:**
```json
{
  "success": true,
  "data": {
    "wallet": {
      "id": "wal_123456",
      "userId": "user_abc",
      "walletType": "trading",
      "status": "active",
      "balance": 1500.75,
      "availableBalance": 1250.75,
      "pendingBalance": 250.00,
      "reservedBalance": 0,
      "currency": "USD",
      "dailyLimit": 5000,
      "monthlyLimit": 50000,
      "dailyUsed": 1250,
      "monthlyUsed": 15000,
      "totalDeposited": 5000,
      "totalWithdrawn": 3500,
      "lastActivityAt": "2026-01-25T10:30:00Z",
      "createdAt": "2025-12-01T00:00:00Z",
      "updatedAt": "2026-01-25T10:30:00Z"
    },
    "recentTransactions": [
      {
        "id": "txn_123",
        "type": "deposit",
        "amount": 500,
        "status": "completed",
        "description": "Deposit via Stripe",
        "createdAt": "2026-01-25T10:00:00Z"
      }
    ]
  }
}
```

---

### POST /payments/wallet/deposit
**Descripción:** Depositar fondos a wallet
**Autenticación:** Requerida
**Body:**
```json
{
  "amount": 500.00,
  "paymentMethodId": "pm_123456"
}
```

**Response 201:**
```json
{
  "success": true,
  "data": {
    "transaction": {
      "id": "txn_deposit_123",
      "walletId": "wal_123456",
      "type": "deposit",
      "amount": 500.00,
      "status": "processing",
      "balanceBefore": 1000.75,
      "balanceAfter": 1500.75,
      "fee": 0,
      "netAmount": 500.00,
      "currency": "USD",
      "description": "Deposit via Stripe",
      "createdAt": "2026-01-25T10:30:00Z"
    },
    "clientSecret": "pi_1234567890_secret_..."
  }
}
```

---

### POST /payments/wallet/withdraw
**Descripción:** Retirar fondos de wallet
**Autenticación:** Requerida
**Body:**
```json
{
  "amount": 250.00,
  "destination": {
    "type": "bank_account",
    "accountId": "ba_stripe_connected_account"
  }
}
```

**Response 201:**
```json
{
  "success": true,
  "data": {
    "id": "txn_withdraw_456",
    "walletId": "wal_123456",
    "type": "withdrawal",
    "amount": 250.00,
    "status": "pending",
    "balanceBefore": 1500.75,
    "balanceAfter": 1250.75,
    "fee": 2.50,
    "netAmount": 247.50,
    "currency": "USD",
    "description": "Withdrawal to bank account",
    "processedAt": null,
    "createdAt": "2026-01-25T10:30:00Z"
  }
}
```

---

### GET /payments/wallet/transactions
**Descripción:** Obtiene historial de transacciones wallet
**Autenticación:** Requerida
**Parámetros Query:**
- `page` (number, default: 1)
- `limit` (number, default: 20)
- `type` (string, optional: "deposit", "withdrawal", "refund", etc)

**Response 200:**
```json
{
  "success": true,
  "data": {
    "transactions": [
      {
        "id": "txn_123",
        "type": "deposit",
        "amount": 500,
        "status": "completed",
        "balanceBefore": 500,
        "balanceAfter": 1000,
        "fee": 0,
        "netAmount": 500,
        "currency": "USD",
        "description": "Deposit via Stripe",
        "reference": "pm_123456",
        "createdAt": "2026-01-25T10:00:00Z"
      }
    ],
    "total": 25,
    "page": 1
  }
}
```

---

## 9. Cupones de Descuento

### POST /payments/coupons/validate
**Descripción:** Valida código de cupón/descuento
**Autenticación:** Requerida
**Body:**
```json
{
  "code": "WELCOME10",
  "planId": "plan_pro"
}
```

**Response 200:**
```json
{
  "success": true,
  "data": {
    "code": "WELCOME10",
    "valid": true,
    "discountType": "percent",
    "discountValue": 10,
    "expiresAt": "2026-12-31T23:59:59Z",
    "minPurchase": 0,
    "maxUses": 1000,
    "usedCount": 234
  }
}
```

**Response 200 (inválido):**
```json
{
  "success": true,
  "data": {
    "code": "INVALID123",
    "valid": false,
    "message": "Coupon not found or expired"
  }
}
```

---

## 10. Resumen Completo

### Matriz de Endpoints

| Endpoint | Método | Descripción | Auth |
|----------|--------|-------------|------|
| `/payments/plans` | GET | Lista planes | ❌ |
| `/payments/plans/{slug}` | GET | Detalle plan | ❌ |
| `/payments/subscription` | GET | Suscripción actual | ✅ |
| `/payments/subscription/history` | GET | Historial suscripciones | ✅ |
| `/payments/subscriptions` | POST | Crear suscripción | ✅ |
| `/payments/subscription/change-plan` | POST | Cambiar plan | ✅ |
| `/payments/subscription/cancel` | POST | Cancelar suscripción | ✅ |
| `/payments/subscription/resume` | POST | Reactivar suscripción | ✅ |
| `/payments/checkout` | POST | Crear sesión checkout | ✅ |
| `/payments/billing-portal` | POST | Portal Stripe | ✅ |
| `/payments/methods` | GET | Listar métodos pago | ✅ |
| `/payments/methods` | POST | Agregar método pago | ✅ |
| `/payments/methods/default` | POST | Set default method | ✅ |
| `/payments/methods/{id}` | DELETE | Eliminar método pago | ✅ |
| `/payments/invoices` | GET | Listar facturas | ✅ |
| `/payments/invoices/{id}` | GET | Detalle factura | ✅ |
| `/payments/invoices/{id}/pdf` | GET | Descargar PDF | ✅ |
| `/payments/billing-info` | GET | Información facturación | ✅ |
| `/payments/billing-info` | PUT | Actualizar facturación | ✅ |
| `/payments/usage` | GET | Estadísticas uso | ✅ |
| `/payments/wallet` | GET | Wallet actual | ✅ |
| `/payments/wallet/deposit` | POST | Depositar | ✅ |
| `/payments/wallet/withdraw` | POST | Retirar | ✅ |
| `/payments/wallet/transactions` | GET | Historial transacciones | ✅ |
| `/payments/coupons/validate` | POST | Validar cupón | ✅ |

---

## 11. Códigos de Error Comunes

| Código | HTTP | Descripción |
|--------|------|-------------|
| `UNAUTHORIZED` | 401 | Token ausente o inválido |
| `FORBIDDEN` | 403 | Usuario no autorizado para recurso |
| `NOT_FOUND` | 404 | Recurso no existe |
| `INVALID_INPUT` | 400 | Parámetros inválidos |
| `PAYMENT_FAILED` | 402 | Pago rechazado |
| `PLAN_NOT_FOUND` | 404 | Plan no existe |
| `SUBSCRIPTION_ALREADY_ACTIVE` | 409 | Usuario ya tiene suscripción activa |
| `INSUFFICIENT_BALANCE` | 402 | Saldo wallet insuficiente |
| `INVALID_PAYMENT_METHOD` | 400 | Método de pago inválido |
| `COUPON_EXPIRED` | 400 | Cupón expirado |
| `COUPON_NOT_FOUND` | 404 | Cupón no existe |
| `RATE_LIMIT_EXCEEDED` | 429 | Demasiadas solicitudes |

---

## 12. Rate Limits (Recomendado)

```
- GET /payments/plans: 100 req/min
- GET /payments/subscription: 100 req/min
- POST /payments/checkout: 10 req/min per user
- POST /payments/wallet/deposit: 5 req/min per user
- POST /payments/wallet/withdraw: 5 req/min per user
- POST /payments/coupons/validate: 20 req/min per user
```

