> ## Documentation Index
> Fetch the complete documentation index at: https://firespark.cloud/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Validar pedido

> Valida líneas y totales del carrito contra un menú fresco del scope antes del checkout.

Recalcula los precios esperados a partir de un **menú fresco** del mismo alcance de venta y los compara con los `lines` y `totals` enviados. Úsalo antes del checkout para mostrar problemas por línea (cambio de precio, producto no disponible, menú cerrado, etc.) sin confiar en los montos del cliente.

> [!NOTE]
>
> Requiere un access token de la API Storefront obtenido mediante [intercambio
> de token](https://firespark.cloud/docs/es/storefront-api/oauth/exchange/post.md). Envía el **borrador
> actual** (scope + lines + totals), no solo el id persistido — también se
> validan ediciones aún no sincronizadas.

En `@fire-spark/storefront`, llama `order.validateOrder()`. Si falla, lanza
`OrderValidationError` con el mismo payload `issues` y `lines` descrito abajo.

## Cuerpo de la solicitud

| Campo | Requerido | Tipo | Descripción |
| - | - | - | - |
| `store_id` | Sí | string | Id externo de tienda (scope) |
| `channel_id` | Sí | string | Id externo de canal (scope) |
| `fulfillment_id` | Sí | string | Id externo de fulfillment (scope) |
| `brand_id` | No | string \| null | Id externo de marca cuando aplica |
| `lines` | Sí | array | Filas `OrderLine` actuales del carrito (incluye hijos/modificadores) |
| `totals` | Sí | object | Totales actuales (`currency`, `subtotal`, `discount_*`, `tax_*`, `total`) |

## Solicitud

```bash theme={null}
curl -X POST "https://firespark.cloud/api/storefront/v1/orders/validate" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "downtown",
    "channel_id": "app",
    "fulfillment_id": "delivery",
    "brand_id": null,
    "lines": [],
    "totals": {
      "currency": "USD",
      "subtotal": 0,
      "discount_rate": 0,
      "discount_total": 0,
      "tax_rate": 0,
      "tax_total": 0,
      "total": 0
    }
  }'
```

## Respuesta exitosa

Devuelve `200` cuando todas las comprobaciones pasan.

```json theme={null}
{
  "data": {
    "ok": true
  }
}
```

## Fallo de validación (`422`)

Si al menos una comprobación falla, la API responde `422` con:

```json theme={null}
{
  "error": "Order validation failed",
  "details": {
    "issues": [
      { "code": "MENU_CLOSED" },
      { "code": "SCOPE_INACTIVE", "field": "store_id" }
    ],
    "lines": {
      "line-root-1": [
        {
          "code": "PRICE_MISMATCH",
          "field": "unit_subtotal",
          "expected": "10.99",
          "actual": "9.99"
        }
      ]
    }
  }
}
```

Las comparaciones de dinero usan igualdad exacta con Big.js (sin epsilon). `expected` / `actual` son strings decimales cuando aplican.

### Códigos a nivel de pedido (`details.issues`)

| Código | Significado |
| - | - |
| `NO_MENU` | No hay menú ACTIVE para el scope (o ninguno tras filtrar por horario). |
| `MENU_CLOSED` | Hay menú pero su horario no está activo ahora. |
| `SCOPE_INACTIVE` | Store, channel, fulfillment y/o brand falta o no está `ACTIVE`. `field` es `store_id`, `channel_id`, `fulfillment_id` o `brand_id` (un issue por parte inactiva). |
| `EMPTY_ORDER` | No hay líneas raíz (`parent_uid == null`). |
| `TOTALS_MISMATCH` | Los `totals` enviados no coinciden con la suma de los group totals de las raíces. `field` indica la clave. |

### Códigos de canjes

Llegan en `details.issues` para los canjes del pedido (`field` es `redeem.N.…`) y en `details.lines[uid]` para los canjes de línea.

| Código | Significado |
| - | - |
| `REDEEM_PAYMENT_REQUIRED` | El canje no tiene `payment_id` (`field` termina en `payment_id`) o no tiene `amount` mayor que 0 (`field` termina en `amount`). |
| `REDEEM_PAYMENT_METHOD_NOT_CONFIGURED` | `payment_id` no es un pago `REDEMPTION` activo del merchant. `actual` es `NOT_FOUND` (no existe o no está activo) o `NOT_REDEMPTION`. |
| `REDEEM_AMOUNT_MISMATCH` | `amount` supera su tope: el valor antes de impuestos de la línea o del pedido con `before_taxes` (por defecto), o el total completo, y nunca más que el total menos propina y envío. `expected` es el tope. |
| `REDEEM_EXPIRED` | Ya pasó `expires_at`. |

### Códigos a nivel de línea (`details.lines[uid]`)

| Código | Significado |
| - | - |
| `PRODUCT_NOT_FOUND` | El id del producto raíz no está en el menú fresco. |
| `HIDDEN` | `availability.status` del producto es `HIDDEN`. |
| `OUT_OF_STOCK` | `availability.status` del producto es `OUT_OF_STOCK`. |
| `MODIFIER_NOT_FOUND` | Un hijo no es un modificador válido bajo su padre. |
| `MODIFIER_RULES_INVALID` | La selección incumple min/max u opciones obligatorias del catálogo. |
| `VARIANT_BASE_NOT_ORDERABLE` | La línea es un producto con `variant_group_ids`. Solo sirve para elegir opciones; envía el producto variante resultante. |
| `VARIANT_NOT_OFFERED` | El producto es una variante, pero ya ninguna base activa y visible la resuelve. En una línea de modificador, la base listada en el grupo de modificadores del padre no resuelve a esta variante. |
| `PRICE_MISMATCH` | Un campo de precio difiere del rebuild del menú. |
| `TAX_MISMATCH` | Tasa o totales de impuesto difieren del rebuild. |
| `DISCOUNT_MISMATCH` | Tasa o totales de descuento difieren del rebuild. |
| `QUANTITY_MISMATCH` | La cantidad (o unit×qty vs totales en fees) no coincide. |
| `STRUCTURE_MISMATCH` | La forma del árbol difiere (hijos de más/menos, id incorrecto). |

> [!TIP]
>
> Muestra `details.lines[uid]` junto a la fila del carrito para que el cliente
> pueda quitar o re-personalizar el ítem. Usa `details.issues` como banners del
> carrito (tienda cerrada, totales incorrectos, carrito vacío).

## Otras respuestas de error

| Status | Descripción |
| - | - |
| `400` | Cuerpo malformado (validación Zod) |
| `401` | Access token ausente o inválido |
| `422` | Falló la validación del pedido (arriba), o el scope de venta es inválido/inactivo al resolver el menú |

## Relacionado

> [card] [Obtener menús](https://firespark.cloud/docs/es/storefront-api/menus/get.md)
>
> Menú fresco del mismo scope

> [card] [Actualizar pedido](https://firespark.cloud/docs/es/storefront-api/orders/\[id]/patch)
>
> Sincronizar líneas tras corregir el carrito

> [card] [Realizar pedidos](https://firespark.cloud/docs/es/storefront-api/guides/placing-orders.md)
>
> Flujo completo de checkout


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.