# Pedidos y recuperación

Referencias estables, estados explícitos y reintentos sin duplicados.

## Cotización y reserva temporal

POST /checkout-commands acepta commandId UUID, checkoutId y action quote/payment/status/cancel. quote incluye order con el formato de /orders y externalId igual a checkoutId. El POS calcula precio de la lista publicada e ITBMS configurado con Decimal; sólo retiro pickup, productos simples y un impuesto explícito (incluido exento). No aplica promociones ni convenios.

La reserva dura cinco minutos desde que la procesa el POS. Gateway devuelve pending hasta recibir el resultado. Si lleva más de 45 segundos sin catálogo del POS rechaza nuevas cotizaciones; si el POS recibe una solicitud nueva de más de 60 segundos, la rechaza sin reservar.

GET /checkout-commands/{commandId} recupera el recibo histórico e inmutable. Para saber el estado actual enviar action=status con otro commandId. Un recibo antiguo con stockReserved=true no acredita una reserva actual: comprobar expiresAt y obtener un estado reciente. Conservar commandId y cuerpo ante timeout. La misma referencia no renueva una reserva vencida.

action=cancel libera unidades; sin conexión queda pendiente hasta el POS. Las reservas vencidas se liberan por el proceso POS, incluso sin gateway. Las salidas físicas que invaden reservas activas reciben 409 COMMERCE_STOCK_RESERVED. La conversión a venta requiere transferencia conciliada y emisión administrativa; checkoutEnabled=false no habilita cobros automáticos.







## ACH / transferencia con conciliación

El administrador habilita PUT /admin/integrations/{id}/payment-settings con instrumento ACH/TRANSFERENCIA USD activo e instrucciones para el comprador. Consultar en GET settings. Estas instrucciones son públicas para la tienda: no incluir contraseñas ni credenciales bancarias. Configuración → Tienda online → Administrar pagos y entregas permite guardar instrucciones, conciliar, emitir y registrar devoluciones verificadas.

Tras cotizar, enviar action=payment con UUID nuevo para obtener instrucciones y amount calculado por POS. paymentStatus=pending NO acredita un pago. Ante timeout consultar el mismo commandId. Para el estado actual enviar action=status con otro UUID. Una captura o declaración del comprador nunca confirma el cobro.

El administrador comprueba el banco y llama confirm-transfer con amount exacto, reference y reason. Requiere admin.configuracion y admin.bancos vigentes. La referencia se normaliza a mayúsculas y no se reutiliza entre checkouts Commerce de la empresa. Esto registra evidencia de conciliación; no importa ni concilia movimientos del módulo Bancos.

Con pago confirmed, POST invoice con terceroId consumidor final 02 requiere admin.configuracion y pos.documentos. Reutiliza facturación y FE existentes, sin caja/turno. La reserva, salida de inventario y factura se confirman juntas. Reintentos devuelven la misma factura. applied significa vinculado a factura; fiscalStatus no implica aceptación automática del PAC.

Si la reserva venció, se verifica stock disponible al emitir. Falta de stock, cambio de precio/impuesto o receptor incompatible dejan el pago confirmed sin factura: no pedir otro pago. Listar /admin/integrations/{id}/checkouts para resolver. Se puede reponer/corregir y repetir emisión, o devolver el dinero por el banco y registrar record-refund con evidencia. Ese endpoint sólo registra una devolución ya realizada, completa y sin factura; no envía fondos.

Una venta emitida usa los flujos existentes de anulación/nota de crédito y devolución; no se borra ni se vuelve a facturar desde el checkout. Cancelar por API de tienda no está permitido después de conciliar. Hay preparación y retiro auditados; aún no hay envíos, notificaciones, tarjeta ni Yappy web.







## Preparación y retiro del pedido

Después de emitir, POST /admin/integrations/{id}/checkouts/{checkoutId}/fulfillment avanza de pendiente a preparando, listo y entregado. Requiere admin.configuracion y pos.documentos; no acepta la clave pública de tienda. El mismo commandId/cuerpo recupera el resultado anterior sin duplicar eventos. Se rechazan saltos, retrocesos, factura anulada o pago sin aplicar.

Entregado requiere el nombre de quien retira y un comprobante fiscal disponible cuando corresponde. La pantalla pide confirmación y registra al operador; no envía correos ni mensajes. Ante timeout bloquea cambios hasta actualizar el estado. La nota interna y el nombre de quien retira no se publican al sitio.

El backend Vue obtiene fulfillment.status y updatedAt en el resultado de un comando status nuevo, a través del mismo gateway local. GET de un comando viejo conserva su estado histórico. No suponer entrega por factura emitida, reserva vencida o pago aplicado.







## Crear una solicitud

POST /orders requiere orders:write. Generar externalId una sola vez antes del primer envío y conservarlo junto al contenido. El mismo ID es único por integración.



```json
{
  "externalId": "web-7d2f4c9a",
  "customer": {
    "name": "María González",
    "email": "maria@example.com",
    "phone": "6000-1234"
  },
  "fulfillment": {
    "method": "pickup"
  },
  "lines": [
    {
      "productId": "22222222-2222-4222-8222-222222222222",
      "quantity": "2.0000"
    }
  ],
  "note": "Retiro por la tarde"
}
```

Sustituir el UUID por un producto publicado. Para entrega: fulfillment.method=delivery y address. No calcula ni acepta un costo de envío. No enviar precios, totales ni afirmaciones de pago.



## Interpretar HTTP 202

202 significa solicitud persistida pendiente de confirmación, no venta confirmada. La respuesta declara paymentStatus=not_requested, stockReserved=false e invoiced=false.

Estados disponibles: recibido_gateway (sólo gateway), pendiente_confirmacion y rechazado. El administrador puede rechazar con motivo; repetir el mismo rechazo es idempotente. /orders no reserva. checkout-commands permite cotizar y reservar por separado; el flujo ACH y su emisión administrativa se realizan sobre checkouts.







## Recuperar una respuesta perdida



- Consultar GET /orders/{externalId} con orders:read o reenviar el mismo cuerpo con el mismo externalId.
- Una respuesta 404 significa que no hay solicitud visible con esa referencia en esa integración en ese momento. Mantener el mismo ID para el reintento.
- Mismo contenido devuelve el mismo pedido y su estado actual. Otro contenido con la referencia utilizada devuelve 409.
- Cantidades equivalentes (2 y 2.0000) y distinto orden de líneas son equivalentes; repetir un producto en varias líneas se rechaza.
- La referencia se conserva mientras exista el pedido, incluso rechazado. No reciclarla para otra compra.





## Errores y límites

Las respuestas usan {success:true,data} o {success:false,error:{code,message}}. Los pedidos y las respuestas administrativas no deben almacenarse en cachés públicas.

- 400: revisar esquema, cantidades, productos publicados o parámetros.
- 401: credencial ausente, inválida, rotada o revocada.
- 403: scope, licencia, permisos o contexto de empresa/sucursal/almacén.
- 409: conciliar referencia/contenido o estado; no crear otro ID automáticamente.
- 429: esperar; limitador básico por IP y proceso: 240 solicitudes/minuto en POS, 600 en gateway. No es una cuota distribuida.
- Red/5xx: resultado incierto; conservar referencia y comprobar antes de repetir.




