# Ejemplos de Uso - API PedidosYa

Todos los endpoints requieren el header `X-PedidosYa-Token` con el token de autorización de PedidosYa.

---

## Tabla de Casos por Endpoint

| Endpoint | Caso | HTTP | Descripción |
|---|---|---|---|
| `POST /estimate` | Sin `deliveryTime` | 200 | Cotización ASAP (1 llamada) |
| `POST /estimate` | Con `deliveryTime` | 200 | Cotización programada (2 llamadas: ASAP + ajustada) |
| `POST /estimate/{id}/confirm` | Normal | 200 | Confirma y pide motorista ahora |
| `POST /estimate/{id}/confirm` | Expirado | 422 | Estimate con > 15 min → cliente debe re-cotizar |
| `POST /shipping` | Sin `deliveryTime` | 200 | Envío ASAP (1 llamada) |
| `POST /shipping` | Con `deliveryTime` | 200 | Envío programado (2 llamadas: ASAP cotización + envío ajustado) |

---

## 1. Estimate Shipping Order (Cotización)

Obtiene estimaciones de precio y tiempo para un envío.

### Endpoint
```
POST /api/pedidosya/estimate
```

### Headers
```
X-PedidosYa-Token: {tu_token_de_pedidosya}
Content-Type: application/json
```

### Request Body
```json
{
  "referenceId": "ORDER-12345",
  "isTest": true,
  "notificationMail": "cliente@example.com",
  "items": [
    {
      "type": "STANDARD",
      "value": 1250.60,
      "description": "Laptop Dell",
      "sku": "LAPTOP-001",
      "quantity": 1,
      "volume": 10.01,
      "weight": 2.5
    },
    {
      "type": "STANDARD",
      "value": 250.00,
      "description": "Mouse inalámbrico",
      "sku": "MOUSE-001",
      "quantity": 2,
      "volume": 5.50,
      "weight": 0.3
    }
  ],
  "waypoints": [
    {
      "type": "PICK_UP",
      "addressStreet": "Plaza Independencia 755",
      "addressAdditional": "Piso 6 Recepción",
      "city": "Montevideo",
      "latitude": -34.905988,
      "longitude": -56.199592,
      "phone": "+59898765432",
      "name": "Tienda Central",
      "instructions": "Llamar al llegar"
    },
    {
      "type": "DROP_OFF",
      "latitude": -34.9138414,
      "longitude": -56.1837661,
      "addressStreet": "La Cumparsita 1475",
      "addressAdditional": "Apartamento 301",
      "city": "Montevideo",
      "phone": "+59812345678",
      "name": "Juan Pérez",
      "instructions": "Entregar en mano",
      "collectMoney": 1500.60
    }
  ]
}
```

### Response 200 OK
```json
{
  "estimateId": "520644687847685483",
  "referenceId": "ORDER-12345",
  "isTest": true,
  "items": [
    {
      "type": "STANDARD",
      "value": 1250.60,
      "description": "Laptop Dell",
      "sku": "LAPTOP-001",
      "quantity": 1,
      "volume": 10.01,
      "weight": 2.5
    }
  ],
  "waypoints": [
    {
      "type": "PICK_UP",
      "addressStreet": "Plaza Independencia 755",
      "addressAdditional": "Piso 6 Recepción",
      "city": "Montevideo",
      "latitude": -34.905988,
      "longitude": -56.199592,
      "phone": "+59898765432",
      "name": "Tienda Central",
      "instructions": "Llamar al llegar"
    },
    {
      "type": "DROP_OFF",
      "latitude": -34.9138414,
      "longitude": -56.1837661,
      "addressStreet": "La Cumparsita 1475",
      "addressAdditional": "Apartamento 301",
      "city": "Montevideo",
      "phone": "+59812345678",
      "name": "Juan Pérez",
      "instructions": "Entregar en mano"
    }
  ],
  "route": {
    "distance": 2500
  },
  "deliveryOffers": [
    {
      "deliveryOfferId": "679db4989914e48805f3303eb29c4dec",
      "deliveryMode": "EXPRESS",
      "estimatedPickUpTime": "2023-04-08T17:00:00Z",
      "confirmationTimeLimit": "2023-04-08T17:12:40Z",
      "totalDeliveryTimeMinutes": 45,
      "pricing": {
        "subTotal": 131.36,
        "taxes": 0,
        "total": 131.36,
        "currency": "UYU"
      }
    },
    {
      "deliveryOfferId": "679db4989914e48805f3303eb29c4daa",
      "deliveryMode": "CROSS_DOCKING",
      "estimatedPickUpTime": "2023-04-04T13:00:00Z",
      "deliveryTimeFrom": "2023-04-04T15:00:00Z",
      "deliveryTimeTo": "2023-04-04T18:00:00Z",
      "confirmationTimeLimit": "2023-04-04T12:55:00Z",
      "totalDeliveryTimeMinutes": 300,
      "pricing": {
        "subTotal": 115.50,
        "taxes": 0,
        "total": 115.50,
        "currency": "UYU"
      }
    }
  ],
  "notificationMail": "cliente@example.com"
}
```

### cURL Example
```bash
curl -X POST http://localhost/api/pedidosya/estimate \
  -H "X-PedidosYa-Token: tu_token_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "referenceId": "ORDER-12345",
    "isTest": true,
    "items": [
      {
        "type": "STANDARD",
        "value": 1250.60,
        "description": "Laptop Dell",
        "quantity": 1,
        "volume": 10.01,
        "weight": 2.5
      }
    ],
    "waypoints": [
      {
        "type": "PICK_UP",
        "addressStreet": "Plaza Independencia 755",
        "city": "Montevideo",
        "latitude": -34.905988,
        "longitude": -56.199592,
        "phone": "+59898765432",
        "name": "Tienda Central"
      },
      {
        "type": "DROP_OFF",
        "addressStreet": "La Cumparsita 1475",
        "city": "Montevideo",
        "latitude": -34.9138414,
        "longitude": -56.1837661,
        "phone": "+59812345678",
        "name": "Juan Pérez"
      }
    ]
  }'
```

---

## 2. Confirm Estimate Order (Confirmar Cotización)

Confirma una estimación y pide el motorista de inmediato. Si el estimate tiene más de 15 minutos retorna 422 para que el cliente vuelva a cotizar.

### Endpoint
```
POST /api/pedidosya/estimate/{estimateId}/confirm
```

### Headers
```
X-PedidosYa-Token: {tu_token_de_pedidosya}
Content-Type: application/json
```

### URL Parameters
- `estimateId`: ID de la estimación obtenido en el endpoint anterior (solo dígitos)

### Campos del Request

| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| `deliveryOfferId` | string | No | ID de la oferta elegida. Sin él, PedidosYa usa la primera disponible |

---

### Caso A — Confirmar (dentro de los 15 min)

El sistema confirma ahora y retorna el `shippingId` directamente.

**Request:**
```json
{
  "deliveryOfferId": "679db4989914e48805f3303eb29c4dec"
}
```

**Response 200 OK** *(respuesta directa de PedidosYa)*:
```json
{
  "estimateId": "520644687847685483",
  "shippingId": "15562302281527156893283",
  "confirmationCode": "9869921368",
  "isTest": true,
  "referenceId": "ORDER-12345",
  "status": "CONFIRMED",
  "proofOfDelivery": false,
  "shareLocationUrl": "https://example.pedidosya.com.uy/tracking/MTUE1NTY=",
  "notificationMail": "cliente@example.com",
  "items": [
    {
      "categoryId": 11986575,
      "categoryName": "Envío",
      "value": 1250.60,
      "description": "Laptop Dell",
      "sku": "LAPTOP-001",
      "volume": 10.01,
      "weight": 2.5,
      "quantity": 1,
      "type": "STANDARD"
    }
  ],
  "waypoints": [
    {
      "type": "PICK_UP",
      "addressStreet": "Plaza Independencia 755",
      "city": "Montevideo",
      "latitude": -34.905988,
      "longitude": -56.199592,
      "phone": "+59898765432",
      "name": "Tienda Central",
      "instructions": "Llamar al llegar"
    },
    {
      "type": "DROP_OFF",
      "addressStreet": "La Cumparsita 1475",
      "city": "Montevideo",
      "latitude": -34.9138414,
      "longitude": -56.1837661,
      "phone": "+59812345678",
      "name": "Juan Pérez"
    }
  ],
  "route": {
    "deliveryMode": "CROSS_DOCKING",
    "estimatedPickUpTime": "2023-04-04T13:00:00Z",
    "estimatedDeliveryTime": "2023-04-04T18:00:00Z",
    "distance": 2500,
    "pricing": {
      "subTotal": 115.50,
      "taxes": 0,
      "total": 115.50,
      "currency": "UYU"
    }
  },
  "createdAt": "2023-04-04T12:00:00Z"
}
```

---

### Caso B — Cotización expirada (> 15 min)

**Response 422 Unprocessable Entity:**
```json
{
  "status": 422,
  "code": "ESTIMATE_EXPIRED",
  "message": "La cotización expiró. Por favor vuelva a cotizar."
}
```

---

### cURL — Confirmar
```bash
curl -X POST http://localhost/api/pedidosya/estimate/520644687847685483/confirm \
  -H "X-PedidosYa-Token: tu_token_aqui" \
  -H "Content-Type: application/json" \
  -d '{"deliveryOfferId": "679db4989914e48805f3303eb29c4dec"}'
---

## 3. Create Shipping Order (Crear Envío Directo)

Crea un envío en un solo paso sin estimación previa. Si se envía `deliveryTime`, se interpreta como la hora en que el moto debe llegar al local — el sistema ajusta automáticamente el tiempo de viaje hacia el cliente.

### Endpoint
```
POST /api/pedidosya/shipping
```

### Headers
```
X-PedidosYa-Token: {tu_token_de_pedidosya}
Content-Type: application/json
```

### Campos del Request

| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| `referenceId` | string (≤255) | **Sí** | Referencia interna del pedido |
| `deliveryTime` | date-time UTC | No | Hora en que el moto debe llegar al LOCAL (pickup). Sin este campo: lo antes posible |
| `isTest` | boolean | No | Default: `false` |
| `notificationMail` | email | No | Email para notificaciones de estado |
| `items` | array (1–100) | **Sí** | Lista de productos |
| `waypoints` | array (exactamente 2) | **Sí** | PICK_UP y DROP_OFF |
| `requirements.includeDeliveryFee` | boolean | No | Incluir costo de envío en cobro al cliente |

---

### Caso A — Envío ASAP (sin `deliveryTime`)

**Request:**
```json
{
  "referenceId": "ORDER-67890",
  "isTest": true,
  "notificationMail": "cliente@example.com",
  "items": [
    {
      "type": "STANDARD",
      "value": 500.00,
      "description": "Celular Samsung",
      "sku": "PHONE-001",
      "quantity": 1,
      "volume": 8.50,
      "weight": 0.5
    }
  ],
  "waypoints": [
    {
      "type": "PICK_UP",
      "addressStreet": "Plaza Independencia 755",
      "addressAdditional": "Piso 6 Recepción",
      "city": "Montevideo",
      "latitude": -34.905988,
      "longitude": -56.199592,
      "phone": "+59898765432",
      "name": "Oficina Ciudad Vieja",
      "instructions": "El ascensor está roto"
    },
    {
      "type": "DROP_OFF",
      "latitude": -34.9138414,
      "longitude": -56.1837661,
      "addressStreet": "La Cumparsita 1475",
      "addressAdditional": "Piso 1, Oficina Delivery",
      "city": "Montevideo",
      "phone": "+59812345678",
      "name": "María González",
      "instructions": "Entregar en mano",
      "collectMoney": 650.00
    }
  ],
  "requirements": {
    "includeDeliveryFee": true
  }
}
```

**Response 200 OK** *(respuesta directa de PedidosYa)*:
```json
{
  "shippingId": "15562302281527156893283",
  "confirmationCode": "9869921368",
  "isTest": true,
  "referenceId": "ORDER-67890",
  "status": "CONFIRMED",
  "proofOfDelivery": false,
  "shareLocationUrl": "https://example.pedidosya.com.uy/tracking/MTUE1NTY=",
  "notificationMail": "cliente@example.com",
  "items": [
    {
      "categoryId": 11986575,
      "categoryName": "Envío",
      "value": 500.00,
      "description": "Celular Samsung",
      "sku": "PHONE-001",
      "volume": 8.50,
      "weight": 0.5,
      "quantity": 1,
      "type": "STANDARD"
    }
  ],
  "waypoints": [
    {
      "type": "PICK_UP",
      "addressStreet": "Plaza Independencia 755",
      "addressAdditional": "Piso 6 Recepción",
      "city": "Montevideo",
      "latitude": -34.905988,
      "longitude": -56.199592,
      "phone": "+59898765432",
      "name": "Oficina Ciudad Vieja",
      "instructions": "El ascensor está roto"
    },
    {
      "type": "DROP_OFF",
      "latitude": -34.9138414,
      "longitude": -56.1837661,
      "addressStreet": "La Cumparsita 1475",
      "addressAdditional": "Piso 1, Oficina Delivery",
      "city": "Montevideo",
      "phone": "+59812345678",
      "name": "María González",
      "instructions": "Entregar en mano"
    }
  ],
  "route": {
    "deliveryMode": "CROSS_DOCKING",
    "estimatedPickUpTime": "2023-04-05T13:00:00Z",
    "estimatedDrivingTime": 120,
    "deliveryTimeFrom": "2023-04-05T15:00:00Z",
    "deliveryTimeTo": "2023-04-05T18:00:00Z",
    "distance": 2500,
    "pricing": {
      "subTotal": 130.00,
      "taxes": 0,
      "total": 130.00,
      "currency": "UYU"
    }
  },
  "createdAt": "2023-04-05T12:00:00Z"
}
```

---

### Caso B — Envío con hora programada (`deliveryTime`)

El sistema realiza primero una cotización ASAP para calcular el tiempo de viaje, luego crea el envío con el `deliveryTime` ajustado (hora de llegada al cliente = hora de pickup + tiempo de viaje).

**Request:**
```json
{
  "referenceId": "ORDER-67890",
  "isTest": true,
  "deliveryTime": "2026-04-01T18:30:00Z",
  "items": [
    {
      "type": "STANDARD",
      "value": 500.00,
      "description": "Celular Samsung",
      "sku": "PHONE-001",
      "quantity": 1,
      "volume": 8.50,
      "weight": 0.5
    }
  ],
  "waypoints": [
    {
      "type": "PICK_UP",
      "addressStreet": "Plaza Independencia 755",
      "city": "Montevideo",
      "latitude": -34.905988,
      "longitude": -56.199592,
      "phone": "+59898765432",
      "name": "Oficina Ciudad Vieja"
    },
    {
      "type": "DROP_OFF",
      "addressStreet": "La Cumparsita 1475",
      "city": "Montevideo",
      "latitude": -34.9138414,
      "longitude": -56.1837661,
      "phone": "+59812345678",
      "name": "María González"
    }
  ]
}
```

**Response 200 OK** *(respuesta directa de PedidosYa con motorista ya asignado)*:
```json
{
  "shippingId": "15562302281527156893283",
  "confirmationCode": "9869921368",
  "isTest": true,
  "referenceId": "ORDER-67890",
  "status": "CONFIRMED",
  "route": {
    "deliveryMode": "EXPRESS",
    "estimatedPickUpTime": "2026-04-01T18:30:00Z",
    "estimatedDeliveryTime": "2026-04-01T18:47:00Z",
    "distance": 2500,
    "pricing": {"subTotal": 131.36, "taxes": 0, "total": 131.36, "currency": "UYU"}
  }
}
```

> **Nota**: el sistema calculó internamente `drivingMinutes = 17` y envió a PedidosYa
> `deliveryTime = 2026-04-01T18:47:00Z` (18:30 + 17 min). El `deliveryTime` guardado en BD
> refleja siempre la hora original del cliente (18:30).

---

### cURL — Envío ASAP
```bash
curl -X POST http://localhost/api/pedidosya/shipping \
  -H "X-PedidosYa-Token: tu_token_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "referenceId": "ORDER-67890",
    "isTest": true,
    "items": [
      {
        "type": "STANDARD",
        "value": 500.00,
        "description": "Celular Samsung",
        "sku": "PHONE-001",
        "quantity": 1,
        "volume": 8.50,
        "weight": 0.5
      }
    ],
    "waypoints": [
      {
        "type": "PICK_UP",
        "addressStreet": "Plaza Independencia 755",
        "city": "Montevideo",
        "latitude": -34.905988,
        "longitude": -56.199592,
        "phone": "+59898765432",
        "name": "Oficina Ciudad Vieja"
      },
      {
        "type": "DROP_OFF",
        "addressStreet": "La Cumparsita 1475",
        "city": "Montevideo",
        "latitude": -34.9138414,
        "longitude": -56.1837661,
        "phone": "+59812345678",
        "name": "María González",
        "collectMoney": 650.00
      }
    ],
    "requirements": {
      "includeDeliveryFee": true
    }
  }'
```

### cURL — Envío con deliveryTime
```bash
curl -X POST http://localhost/api/pedidosya/shipping \
  -H "X-PedidosYa-Token: tu_token_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "referenceId": "ORDER-67890",
    "isTest": true,
    "deliveryTime": "2026-04-01T18:30:00Z",
    "items": [
      {
        "type": "STANDARD",
        "value": 500.00,
        "description": "Celular Samsung",
        "sku": "PHONE-001",
        "quantity": 1,
        "volume": 8.50,
        "weight": 0.5
      }
    ],
    "waypoints": [
      {
        "type": "PICK_UP",
        "addressStreet": "Plaza Independencia 755",
        "city": "Montevideo",
        "latitude": -34.905988,
        "longitude": -56.199592,
        "phone": "+59898765432",
        "name": "Oficina Ciudad Vieja"
      },
      {
        "type": "DROP_OFF",
        "addressStreet": "La Cumparsita 1475",
        "city": "Montevideo",
        "latitude": -34.9138414,
        "longitude": -56.1837661,
        "phone": "+59812345678",
        "name": "María González"
      }
    ]
  }'
```

---

## Errores Comunes

### 400 - Token Faltante
```json
{
  "status": 400,
  "code": "MISSING_TOKEN",
  "message": "El token de autorización de PedidosYa es requerido en el header X-PedidosYa-Token"
}
```

### 400 - Validación Fallida
```json
{
  "status": 400,
  "code": "VALIDATION_ERROR",
  "message": "Error en los datos de entrada",
  "errors": {
    "items.0.value": ["The items.0.value must be a number."],
    "waypoints": ["The waypoints must have 2 items."]
  }
}
```

### 400 - Error de PedidosYa
```json
{
  "status": 400,
  "message": "We were not able to find lat/long for waypoint/s in order: 1",
  "code": "WAYPOINTS_NOT_FOUND"
}
```

### 403 - Forbidden
```json
{
  "status": 403,
  "message": "Invalid access token"
}
```

### 500 - Error Interno
```json
{
  "status": 500,
  "code": "INTERNAL_ERROR",
  "message": "Error al procesar la solicitud: Connection timeout"
}
```

---

## Notas Importantes

1. **Token de PedidosYa**: Siempre debe enviarse en el header `X-PedidosYa-Token`
2. **Modo Test**: Usa `"isTest": true` para pruebas sin costo real
3. **Coordenadas**: Se recomienda enviar `latitude` y `longitude` para evitar errores de geocodificación
4. **`estimatedPickupTimeUTC`**: Campo agregado por nuestro sistema en cada `deliveryOffer`. Si la oferta trae `estimatedPickUpTime` se usa directamente; si se envió `deliveryTime`, se retorna el valor original del cliente (hora de pickup en el local)
5. **Confirmación**: Guarda el `shippingId` y `confirmationCode` para futuras referencias
6. **URL de tracking**: Usa `shareLocationUrl` para que el cliente siga el envío en tiempo real
7. **Estimate expirado**: Si pasan más de 15 min sin confirmar, el sistema retorna **422 ESTIMATE_EXPIRED**. El cliente debe volver a cotizar
8. **`deliveryTime` semántica**: El cliente siempre envía la hora de llegada al LOCAL. El sistema ajusta internamente sumando el tiempo de viaje antes de enviarlo a PedidosYa

---

## Flujos Recomendados

### Flujo 1: Con estimación — despacho ASAP
1. `POST /api/pedidosya/estimate` → obtener cotizaciones y mostrar opciones al usuario
2. `POST /api/pedidosya/estimate/{estimateId}/confirm` → **200** con `shippingId`

### Flujo 2: Con estimación — despacho programado
1. `POST /api/pedidosya/estimate` con `deliveryTime` (hora de llegada al local) → cotizaciones con tiempo ajustado
2. `POST /api/pedidosya/estimate/{estimateId}/confirm` → **200** con `shippingId` (moto llegará en el horario pedido)

### Flujo 3: Directo — despacho ASAP
1. `POST /api/pedidosya/shipping` sin `deliveryTime` → **200** con `shippingId`

### Flujo 4: Directo — despacho programado
1. `POST /api/pedidosya/shipping` con `deliveryTime` → **200** con `shippingId` (moto llegará en el horario pedido)
