# Exchange Validation – Frontend Guide (App + Web)

Fecha: 2025-09-03

Esta guía describe el flujo de validación de intercambio, los endpoints disponibles y los casos límite. Aplica tanto para la app móvil como para la web (proyecto multiplataforma).

## Resumen del flujo

- El receptor del objeto genera un código y se lo envía por chat al entregador.
- Si el artículo es de tipo "lost" y ofrece recompensa, el publicador debe pagar la recompensa antes de poder usar el código (el código queda bloqueado hasta el pago).
- El entregador introduce el código para validarlo; si es correcto, se cierra el chat definitivamente y se oculta la publicación.

Roles por tipo de artículo:
- lost: receptor = publicador; entregador = el otro participante.
- found: receptor = no publicador; entregador = publicador.

## Requisitos generales

- Autenticación: Bearer token (Sanctum) en el header Authorization.
- Localización: Accept-Language (ej.: es | en | ca ...).
- Content-Type: application/json (salvo upload de imagen, que usa multipart o base64 ya soportado).

## Endpoints del intercambio

### 1) Generar código de intercambio

- Método: POST
- URL: /app/conversations/{conversationId}/exchange/generate
- Quién: Receptor del objeto (según reglas anteriores)
- Body: {}
- Respuesta 200
{
  "success": true,
  "message": "Código de intercambio generado" | "El código de intercambio está bloqueado hasta pagar la recompensa",
  "data": {
    "code": "ABC123",
    "locked": true | false
  }
}

- Errores
  - 403: { success: false, message: "Unauthorized" }
  - 400: { success: false, message: "Esta conversación ya está cerrada" }

### 2) Consultar estado de intercambio

- Método: GET
- URL: /app/conversations/{conversationId}/exchange/status
- Respuesta 200
{
  "success": true,
  "message": "Estado del intercambio",
  "data": {
    "code_exists": true,
    "locked": true,
    "verified_at": "2025-09-03T12:00:00Z" | null,
    "is_closed": false
  }
}

### 3) Validar código de intercambio (cierre definitivo)

- Método: POST
- URL: /app/conversations/{conversationId}/exchange/validate
- Quién: Entregador (según reglas anteriores)
- Body
{
  "code": "ABC123"
}
- Respuesta 200
{
  "success": true,
  "message": "Intercambio validado correctamente. Chat cerrado y publicación ocultada."
}

- Errores
  - 400: { success: false, message: "Código de intercambio inválido" | "Esta conversación ya está cerrada" }
  - 403: { success: false, message: "El código de intercambio está bloqueado hasta pagar la recompensa" | "Unauthorized" }

## Pago de recompensa (solo artículos lost con recompensa)

Hay dos opciones: móvil (SDK de Stripe con PaymentIntent) y web (Checkout embebido).

### Opción A – App móvil (PaymentIntent)

1. Crear intent

- Método: POST
- URL: /app/payment/reward-intent/{conversationId}
- Respuesta 200
{
  "success": true,
  "tokenRef": "pi_123...",
  "paymentIntent": "pi_client_secret_...",
  "customer": { ...Stripe.Customer... },
  "ephemeralKey": "ek_test_...",
  "amount": 25
}

- El cliente móvil confirma el pago con el client_secret.

1. Confirmar pago y desbloquear código

- Método: POST
- URL: /app/payment/reward-confirm
- Body
{
  "conversation_id": 123,
  "ref": "pi_123...",
  "isWebCheckout": false
}

- Respuesta 200: { success: true, msg: "reward_payment_confirmed" }

### Opción B – Web (Checkout embebido)

1. Crear sesión de Checkout embebido

- Método: POST
- URL: /app/payment/reward-checkout-session/{conversationId}
- Respuesta 200
{
  "success": true,
  "session": {
    "id": "cs_test_...",
    "client_secret": "cs_test_...",
    // ...otros campos estándar de Stripe Checkout Session
  }
}

- Frontend (JS): inicializar Stripe y montar Embedded Checkout con `client_secret`.

1. Confirmar pago y desbloquear código

- Método: POST
- URL: /app/payment/reward-confirm
- Body
{
  "conversation_id": 123,
  "ref": "cs_test_...",
  "isWebCheckout": true
}

- Respuesta 200: { success: true, msg: "reward_payment_confirmed" }

Notas:
- Mientras el pago está pendiente, el código permanece bloqueado (`locked = true`).
- Tras confirmación, el código se desbloquea y el entregador ya podrá validarlo.

## Estados y UX sugerida

- Generación de código:
  - Mostrar el código y si está bloqueado.
  - Si está bloqueado por recompensa, guiar al publicador al pago.
- Validación:
  - Mostrar formulario para introducir código al entregador.
  - Al validar, cerrar la conversación localmente y ocultar el anuncio.
- Chat cerrado:
  - Cualquier intento de enviar mensajes devolverá 403 con `message = "Conversation is closed"`.

## Errores y casos límite

- 401 No autenticado: falta o token inválido.
- 403 Unauthorized: usuario no pertenece a la conversación o no cumple el rol (receptor/entregador).
- 403 Código bloqueado: intento de validar antes de pagar recompensa.
- 404 No encontrado: conversación o artículo inexistente.
- 400 Código inválido o conversación ya cerrada.
- 500 Errores de servidor/Stripe: mostrar mensaje genérico y permitir reintento.
- Usuarios bloqueados: el chat ya evita mensajería si hay bloqueo mutuo (no afecta generación/estado si pertenece a la conversación, pero no podrán chatear).
- Reintentos: es seguro consultar `exchange/status` y reintentar confirmaciones; no existe contador de intentos de validación por ahora.

## Resumen de headers

- Authorization: Bearer `TOKEN`
- Accept-Language: es | en | ca | fr | de | it | pt
- Content-Type: application/json

## Ejemplos breves (web)

- Crear sesión embebida y montar:
  - POST /app/payment/reward-checkout-session/{conversationId} → session.client_secret
  - Stripe.js → `stripe.initEmbeddedCheckout({ clientSecret })`
- Al completar (webhook opcional si se quiere robustez) o tras callback del front:
  - POST /app/payment/reward-confirm { conversation_id, ref: session.id, isWebCheckout: true }

## Compatibilidad

- App móvil: usar PaymentIntent (se proporciona client_secret y ephemeralKey).
- Web: usar Stripe Checkout UI embebido con `ui_mode = embedded`.
