Cart


Representa un carrito de compras: el borrador de un pedido antes de confirmarlo como Order.

Existe un único carrito por combinación de client_id + branch_id (+ admin_id). Un carrito con branch_id contiene productos de un comercio; un carrito sin branch_id es de envío directo / encomienda y describe el paquete en package_info, con origen y destino en delivery.locations.

El carrito se recalcula solo cuando hace falta: si updated_at tiene más de ~180 s, al consultarlo se recalculan precios y tarifas. Al confirmar (checkout con submit: true, o direct-checkout) se crea la Order y el carrito se elimina, salvo keep_cart: true.

Objetos del carrito

billing — datos de facturación personalizada. Opcionales; solo tienen efecto si se quiere una factura con datos distintos a los del cliente.

Campo Tipo Descripción
name string|null Nombre / razón social en la factura
dni string|null Documento fiscal
phone string|null Teléfono de facturación
email string|null Email de facturación
address string|null Dirección fiscal

delivery — datos de la entrega.

Campo Tipo Descripción
latitude_e6 / longitude_e6 int Coordenadas del destino (× 1e6). En PickUp ambos van en 0
address string|null Dirección de entrega en texto
pickup_latitude_e6 / pickup_longitude_e6 / pickup_address int / string|null Origen de la recolección (solo envío directo sin branch_id)
notes string|null Notas de entrega (ej. "Dejar con el vigilante"). Deprecado a favor de las notas por parada en locations
is_gift bool El pedido es un regalo; requiere receiver_name y receiver_phone
receiver_name / receiver_phone string|null Quién recibe, cuando is_gift
scheduled_at string|null Fecha/hora de entrega programada (UTC). null = ASAP
selected_delivery_id int|null Flota elegida para el envío. null = flota por defecto
locations array Paradas (origen → destino) para envío directo / multiparada
is_trip bool El pedido es un viaje (traslado de personas)
package_content string|null Descripción del contenido (envío directo)
preferences int Bitmask de preferencias de servicio para el matching de flota

payment_info — configuración del pago.

Campo Tipo Descripción
is_balance_in_use bool Usar el saldo del cliente para pagar
promo_code_id int|null Id del Coupon a aplicar
currency_iso string|null Moneda en la que se cobra (conversión de montos)
payment_method_type string|null gateway (integración) o form (pago manual)
payment_method_id string|null Id del PaymentMethod (gateway) o del Form (form)
cash_amount_e2 int|null Monto en efectivo declarado (× 100), para pago contra entrega

package_info — descripción del paquete a enviar. Solo en carritos sin branch_id.

Campo Tipo Descripción
content string|null Qué se envía
price_e2 int|null Valor declarado del paquete (× 100)
weight int|null Peso estimado (g)
volume int|null Volumen estimado
is_full_ticket_price bool El price_e2 es el total exacto a cobrar; el resto de tarifas se ajusta para cuadrar

Estructura de resume

resume son los valores calculados del carrito (derivado, no editable):

  • is_valid — si los datos de entrega y pago son válidos. No evalúa la configuración de los productos.
  • prices — subtotales de los productos. Si prices.errors no está vacío, esos productos ya no son válidos y hay que corregirlos antes de confirmar.
  • goods_type — tipo de los productos del carrito.
  • fees — desglose de tarifas y total:
Campo Descripción
subtotal / subtotal_e2 Total sumado de los productos elegidos
delivery_fee_e2 / delivery_details Monto y desglose del envío
selected_delivery_id Flota elegida para el envío
available_delivery_providers / available_delivery_selections Flotas disponibles entre las que elegir
delivery_error Motivo si no hay envío disponible
service_e2 / service_details Monto y desglose del cargo por servicio (si label = -, mostrar "Servicio")
discount / discount_details Monto y desglose de descuentos
discounts_progress Descuentos "en progreso" (aún faltan condiciones para aplicarse)
cashback_e2 Cashback a acreditar
taxes_e2 / taxes_details Ajustes adicionales sobre el monto del pedido
total_e2 / total Total del pedido
balance_payment Parte del total cubierta con el saldo
total_to_pay Total a pagar tras aplicar el saldo
estimated_route Ruta estimada de la entrega
estimated_weight / estimated_time Peso estimado y ETA (minutos)
available_balance Saldo disponible del cliente
payment Datos del cobro en la moneda del pago: payment.debt (deuda con saldo y descuentos aplicados), payment.payment_tax (comisiones del método), payment.payment_total (total con comisiones). Los campos con prefijo payment_ van en la moneda del pago, no en la de la Company
payment_error Motivo si el método de pago elegido no puede usarse

Notas y gotchas

  • currency_iso se fija al crear el carrito (moneda local de la Company). Aunque es fillable, en la práctica no cambia.
  • status hoy vale siempre pending; otros estados están reservados para listas de compras.
  • order_id está deprecado y sin uso.
  • Cada items[].is_valid indica si la configuración de ese producto (variantes, stock, límites) es válida tras el último recálculo.

Estructura de Datos

Atributo Tipo Descripción
id int
company_id int Company dueña del carrito (oculto en la respuesta)
client_id int Cliente dueño del carrito
branch_id int\|null Comercio del carrito; null en carritos de envío directo / encomienda
resume CartResume\|null Valores calculados del carrito (precios, tarifas, validez). Derivado; ver "Estructura de resume"
delivery DeliveryInfo Datos de la entrega (destino, receptor, fecha, flota). Ver tabla de campos arriba
billing BillingInfo\|null Datos de facturación personalizada (opcionales). Ver tabla de campos arriba
payment_info PaymentInfo Configuración de pago (saldo, promo, moneda, método). Ver tabla de campos arriba
currency_iso string Moneda del carrito (moneda local de la Company). Se fija al crear y no cambia
status string Estado del carrito. Hoy siempre pending (otros estados reservados para listas de compras)
created_at datetime\|null
updated_at datetime\|null Última modificación; si supera ~180 s el carrito se recalcula al consultarlo
order_id int\|null Deprecado, sin uso
admin_id int 0 para el carrito propio del cliente; admin.id si lo gestiona un administrador (oculto)
package_info PackageInfo Descripción del paquete a enviar; solo en carritos sin branch_id. Ver tabla de campos arriba
allLogs Collection<int, ApiLog>
branch Branch\|null
client Client
company Company
items CartItem> Productos del carrito
logs Collection<int, ApiLog>
taxes array Reservado; hoy siempre []

Ver Json

Endpoints

Listar Cart

{info} Soporta: Paginación Filters Carga dinámica

Listar Carritos

Muestra la lista de carritos para el usuario autenticado.

Método URI Cabeceras
GET /companies/{companyId}/carts Authorization

Mostrar Cart

{info} Soporta: Carga dinámica

Mostrar Carrito

Muestra los detalles de un carrito por su id

Método URI Cabeceras
GET /companies/{companyId}/carts/{cartId} Authorization

Sugerencias del Carrito

{info} Soporta: Carga dinámica

Devuelve una lista de productos sugeridos para el carrito (limit, por defecto 4): productos frecuentemente comprados junto a los del carrito y, si no alcanzan, los más vendidos del comercio con stock. Si el carrito está vacío, devuelve directamente los más vendidos.

Método URI Cabeceras
GET /companies/{companyId}/carts/{cartId}/suggestions Authorization

Actualizar Cart

Modificar/Actualizar Carrito

Actualiza datos del carrito que no corresponden a los productos solicitados:

  • billing: Datos de facturación.
  • delivery: Dirección de entrega, Receptor y Fecha para el envío.
  • payment_info: Configuración para el pago, moneda y códigos promocionales.

{warning} Al enviar cualquiera de estos objetos, el objeto completo será reemplazado con los nuevos datos. Si se omite algún atributo de estos objetos, dicho atributo será reiniciado a su valor por defecto.

Método URI Cabeceras
PATCH /companies/{companyId}/carts/{cartId} Authorization
{
    "billing": {
        "dni": "nullable|string|max:32",
        "phone": "nullable|string|max:32",
        "name": "nullable|string|max:80",
        "email": "nullable|string|email:rfc,filter",
        "address": "nullable|string|max:512"
    },
    "delivery": {
        "selected_delivery_id": "nullable|integer",
        "scheduled_at": "nullable|date",
        "receiver_name": "nullable|string|max:80",
        "receiver_phone": "nullable|string|max:32",
        "is_gift": "boolean",
        "latitude_e6": "integer|between:-90000000,90000000",
        "longitude_e6": "integer|between:-180000000,180000000",
        "address": "nullable|string|max:512",
        "notes": "nullable|string|max:255",
        "package_content": "nullable|string|max:512",
        "locations": [
            {
                "latitude_e6": "required|integer|between:-90000000,90000000",
                "longitude_e6": "required|integer|between:-180000000,180000000",
                "address": "nullable|string|max:512",
                "notes": "nullable|string|max:255"
            }
        ],
        "is_trip": "boolean",
        "ensure_providers_online": "boolean",
        "preferences": {
            "pref_is_enabled": "nullable|boolean",
            "pref_is_pet_allowed": "nullable|boolean",
            "pref_is_smoker": "nullable|boolean",
            "pref_has_cold_storage": "nullable|boolean",
            "pref_can_transport_liquids": "nullable|boolean",
            "pref_has_fragile_handling_experience": "nullable|boolean",
            "pref_has_large_backpack": "nullable|boolean",
            "pref_has_secure_lockbox": "nullable|boolean",
            "pref_accepts_cash_on_delivery": "nullable|boolean"
        }
    },
    "package_info": {
        "price_e2": "nullable|integer|min:0",
        "is_full_ticket_price": "nullable|boolean",
        "weight": "nullable|integer|min:0",
        "volume": "nullable|integer|min:0"
    },
    "payment_info": {
        "is_balance_in_use": "boolean",
        "promo_code_id": "nullable|integer",
        "currency_iso": "nullable|string|max:8",
        "payment_method_type": "nullable|string|in:gateway,form,post-payment,balance",
        "payment_method_id": "nullable",
        "cash_amount_e2": "nullable|integer|min:0"
    }
}

Eliminar Cart

Eliminar Carrito

Elimina el carrito y descarta todos los cambios realizados.

Método URI Cabeceras
DELETE /companies/{companyId}/carts/{cartId} Authorization

Errores de negocio

Código HTTP Cuándo ocurre
EC600 400 El carrito no está en estado pending.

Acciones de Cart

Mostrar último Carrito

{info} Soporta: Carga dinámica

Muestra el carrito donde el usuario haya echo su última interacción.

Este endpoint es útil (por ejemplo) para mostrar el carrito en el Home o cuando no se haya elegido ningún comercio en la interfaz de usuario.

Método URI Cabeceras
GET /companies/{companyId}/clients/{clientId}/carts/latest Authorization

Mostrar Carrito de Comercio

{info} Soporta: Carga dinámica

Muestra el Carrito del Usuario para un Comercio determinado.

Solo puede existir un único Carrito para un client_id y branch_id al mismo tiempo.

Si no existe Carrito para el client_id y branch_id elegido, se creará un nuevo Carrito vacío automáticamente antes de devolver una respuesta. Si eso ocurre, la única diferencia en la respuesta será que el status_code será de 201 (nuevo carrito creado) en vez de 200 (carrito existente devuelto).

Método URI Cabeceras
GET /companies/{companyId}/clients/{clientId}/branches/{branchId}/carts Authorization

Mostrar Carrito de Comercio

{info} Soporta: Carga dinámica

Muestra el Carrito del Usuario para un Comercio determinado.

Solo puede existir un único Carrito para un client_id y branch_id al mismo tiempo.

Si no existe Carrito para el client_id y branch_id elegido, se creará un nuevo Carrito vacío automáticamente antes de devolver una respuesta. Si eso ocurre, la única diferencia en la respuesta será que el status_code será de 201 (nuevo carrito creado) en vez de 200 (carrito existente devuelto).

Método URI Cabeceras
GET /companies/{companyId}/clients/{clientId}/branches/{branchId}/carts Authorization

Vaciar Carrito

Elimina todos los productos agregados al carrito. Los datos de delivery, pagos, etc., no se ven afectados.

Método URI Cabeceras
POST /companies/{companyId}/carts/{cartId}/clear Authorization

Recalcular Carrito.

Se validan que los datos del carrito sigan siendo correctos (productos aún con stock, dirección de entrega, etc). Útil cuando ha pasado un tiempo sin realizar cambios en el carrito (atributo updated_at) y se desea comprobar que los datos sigan siendo válidos. Respuesta: Cart

Si se envía submit: true, entonces el carrito será validado y enviado. El carrito es eliminado en el proceso. Respuesta: Order

Método URI Cabeceras
POST /companies/{companyId}/carts/{cartId}/checkout Authorization
{
    "submit": "nullable|boolean",
    "keep_cart": "nullable|boolean",
    "cart_identifier": "nullable|integer"
}

Errores de negocio

Código HTTP Cuándo ocurre
EC604 400 Con submit: true, el total del carrito cambió entre el cálculo previo y el envío; revisar los cambios y reintentar.
EC603 400 El contenido del carrito no es válido (productos sin stock, variantes inválidas, etc.); ver resume.prices.errors.
EC605 400 El método de pago seleccionado no puede usarse.

Reemplazar Carrito desde Orden

Reemplaza todos los productos del carrito con los productos solicitados en una orden, coincidiendo las cantidades de la orden y las variantes elegidas. Útil para repetir una orden. Los demás datos del carrito (como dirección) no se ven afectados.

Esta acción es equivalente a variar el carrito y luego agregar todos los productos desde una orden. Los productos que existan agregados al carrito antes de esta acción serán removidos. Se recomienda solicitar una confirmación antes de reemplazar los productos del carrito si el carrito ya posee algún producto agregado.

Nótese que solo se puede utilizar este endpoint si el branch_id de la Orden y el Carrito coinciden.

Método URI Cabeceras
POST /companies/{companyId}/carts/{cartId}/replace-items-from-order Authorization
{
    "order_id": "required|integer|exists:orders,id"
}

Errores de negocio

Código HTTP Cuándo ocurre
EC601 400 Un producto de la orden no existe en el comercio del carrito.

Crear, Actualizar y Confirmar Carrito

Realiza en una sola llamada lo que normalmente requiere tres pasos: obtener (o crear) el carrito del Cliente, actualizarlo con los datos enviados (mismo formato que @see UpdateCartController::update()) y finalmente confirmarlo, tal como lo hace @see CheckoutCartController::checkout() con submit: true.

El carrito es eliminado en el proceso, salvo que se envíe keep_cart: true.

Respuesta: Order

Método URI Cabeceras
POST /companies/{companyId}/clients/{clientId}/branches/{branchId}/carts/checkout Authorization
{
    "billing": {
        "dni": "nullable|string|max:32",
        "phone": "nullable|string|max:32",
        "name": "nullable|string|max:80",
        "email": "nullable|string|email:rfc,filter",
        "address": "nullable|string|max:512"
    },
    "delivery": {
        "selected_delivery_id": "nullable|integer",
        "scheduled_at": "nullable|date",
        "receiver_name": "nullable|string|max:80",
        "receiver_phone": "nullable|string|max:32",
        "is_gift": "boolean",
        "latitude_e6": "integer|between:-90000000,90000000",
        "longitude_e6": "integer|between:-180000000,180000000",
        "address": "nullable|string|max:512",
        "notes": "nullable|string|max:255",
        "package_content": "nullable|string|max:512",
        "locations": [
            {
                "latitude_e6": "required|integer|between:-90000000,90000000",
                "longitude_e6": "required|integer|between:-180000000,180000000",
                "address": "nullable|string|max:512",
                "notes": "nullable|string|max:255"
            }
        ],
        "is_trip": "boolean",
        "ensure_providers_online": "boolean",
        "preferences": {
            "pref_is_enabled": "nullable|boolean",
            "pref_is_pet_allowed": "nullable|boolean",
            "pref_is_smoker": "nullable|boolean",
            "pref_has_cold_storage": "nullable|boolean",
            "pref_can_transport_liquids": "nullable|boolean",
            "pref_has_fragile_handling_experience": "nullable|boolean",
            "pref_has_large_backpack": "nullable|boolean",
            "pref_has_secure_lockbox": "nullable|boolean",
            "pref_accepts_cash_on_delivery": "nullable|boolean"
        }
    },
    "package_info": {
        "price_e2": "nullable|integer|min:0",
        "is_full_ticket_price": "nullable|boolean",
        "weight": "nullable|integer|min:0",
        "volume": "nullable|integer|min:0"
    },
    "payment_info": {
        "is_balance_in_use": "boolean",
        "promo_code_id": "nullable|integer",
        "currency_iso": "nullable|string|max:8",
        "payment_method_type": "nullable|string|in:gateway,form,post-payment,balance",
        "payment_method_id": "nullable"
    },
    "keep_cart": "nullable|boolean",
    "cart_identifier": "nullable|integer"
}

Errores de negocio

Código HTTP Cuándo ocurre
EC604 400 El total del carrito cambió durante la confirmación; revisar los cambios y reintentar.
EC603 400 El contenido del carrito no es válido (productos sin stock, variantes inválidas, etc.); ver resume.prices.errors.
EC605 400 El método de pago seleccionado no puede usarse.

Crear, Actualizar y Confirmar Carrito

Realiza en una sola llamada lo que normalmente requiere tres pasos: obtener (o crear) el carrito del Cliente, actualizarlo con los datos enviados (mismo formato que @see UpdateCartController::update()) y finalmente confirmarlo, tal como lo hace @see CheckoutCartController::checkout() con submit: true.

El carrito es eliminado en el proceso, salvo que se envíe keep_cart: true.

Respuesta: Order

Método URI Cabeceras
POST /companies/{companyId}/clients/{clientId}/branches/{branchId}/carts/checkout Authorization
{
    "billing": {
        "dni": "nullable|string|max:32",
        "phone": "nullable|string|max:32",
        "name": "nullable|string|max:80",
        "email": "nullable|string|email:rfc,filter",
        "address": "nullable|string|max:512"
    },
    "delivery": {
        "selected_delivery_id": "nullable|integer",
        "scheduled_at": "nullable|date",
        "receiver_name": "nullable|string|max:80",
        "receiver_phone": "nullable|string|max:32",
        "is_gift": "boolean",
        "latitude_e6": "integer|between:-90000000,90000000",
        "longitude_e6": "integer|between:-180000000,180000000",
        "address": "nullable|string|max:512",
        "notes": "nullable|string|max:255",
        "package_content": "nullable|string|max:512",
        "locations": [
            {
                "latitude_e6": "required|integer|between:-90000000,90000000",
                "longitude_e6": "required|integer|between:-180000000,180000000",
                "address": "nullable|string|max:512",
                "notes": "nullable|string|max:255"
            }
        ],
        "is_trip": "boolean",
        "ensure_providers_online": "boolean",
        "preferences": {
            "pref_is_enabled": "nullable|boolean",
            "pref_is_pet_allowed": "nullable|boolean",
            "pref_is_smoker": "nullable|boolean",
            "pref_has_cold_storage": "nullable|boolean",
            "pref_can_transport_liquids": "nullable|boolean",
            "pref_has_fragile_handling_experience": "nullable|boolean",
            "pref_has_large_backpack": "nullable|boolean",
            "pref_has_secure_lockbox": "nullable|boolean",
            "pref_accepts_cash_on_delivery": "nullable|boolean"
        }
    },
    "package_info": {
        "price_e2": "nullable|integer|min:0",
        "is_full_ticket_price": "nullable|boolean",
        "weight": "nullable|integer|min:0",
        "volume": "nullable|integer|min:0"
    },
    "payment_info": {
        "is_balance_in_use": "boolean",
        "promo_code_id": "nullable|integer",
        "currency_iso": "nullable|string|max:8",
        "payment_method_type": "nullable|string|in:gateway,form,post-payment,balance",
        "payment_method_id": "nullable"
    },
    "keep_cart": "nullable|boolean",
    "cart_identifier": "nullable|integer"
}

Errores de negocio

Código HTTP Cuándo ocurre
EC604 400 El total del carrito cambió durante la confirmación; revisar los cambios y reintentar.
EC603 400 El contenido del carrito no es válido (productos sin stock, variantes inválidas, etc.); ver resume.prices.errors.
EC605 400 El método de pago seleccionado no puede usarse.

Verificar Disponibilidad de Repartidores

Consulta, de forma independiente al flujo de checkout, si existen flotas capaces de atender un pedido desde una tienda (o punto de recogida, para envíos) hacia un destino dado. Reutiliza el mismo matching de flotas que usa el checkout real (@see ServiceFeesCalculator::calculateFees() con ensureProvidersEnabled: true), de modo que el resultado es consistente con lo que ocurriría al confirmar el pedido.

Las flotas de proveedores externos (tipo webhook, ej. Ridery) se consideran siempre disponibles: hoy no existe una verificación síncrona en tiempo real contra su API, solo la flota propia (repartidores en BD) se valida en tiempo real vía conteo de repartidores online dentro del radio de servicio de la flota.

Si la verificación falla de forma inesperada (no por falta de cobertura, sino por un error al calcularla), se responde EC245 (503) para no permitir el pedido bajo contingencia.

Método URI Cabeceras
POST /companies/{companyId}/clients/{clientId}/branches/{branchId}/carts/check-availability Authorization
{
    "delivery": {
        "scheduled_at": "nullable|date",
        "is_trip": "boolean",
        "latitude_e6": "integer|between:-90000000,90000000",
        "longitude_e6": "integer|between:-180000000,180000000",
        "address": "nullable|string|max:512",
        "locations": [
            {
                "latitude_e6": "required|integer|between:-90000000,90000000",
                "longitude_e6": "required|integer|between:-180000000,180000000"
            }
        ]
    },
    "search_radius": "nullable|sometimes|integer|min:0|max:30000"
}

Errores de negocio

Código HTTP Cuándo ocurre
EC225 400 No se envió un destino válido (delivery.latitude_e6 / delivery.longitude_e6 en 0).
EC245 503 No se pudo verificar la disponibilidad de repartidores por un error al calcularla (respuesta bajo contingencia).

Verificar Disponibilidad de Repartidores

Consulta, de forma independiente al flujo de checkout, si existen flotas capaces de atender un pedido desde una tienda (o punto de recogida, para envíos) hacia un destino dado. Reutiliza el mismo matching de flotas que usa el checkout real (@see ServiceFeesCalculator::calculateFees() con ensureProvidersEnabled: true), de modo que el resultado es consistente con lo que ocurriría al confirmar el pedido.

Las flotas de proveedores externos (tipo webhook, ej. Ridery) se consideran siempre disponibles: hoy no existe una verificación síncrona en tiempo real contra su API, solo la flota propia (repartidores en BD) se valida en tiempo real vía conteo de repartidores online dentro del radio de servicio de la flota.

Si la verificación falla de forma inesperada (no por falta de cobertura, sino por un error al calcularla), se responde EC245 (503) para no permitir el pedido bajo contingencia.

Método URI Cabeceras
POST /companies/{companyId}/clients/{clientId}/branches/{branchId}/carts/check-availability Authorization
{
    "delivery": {
        "scheduled_at": "nullable|date",
        "is_trip": "boolean",
        "latitude_e6": "integer|between:-90000000,90000000",
        "longitude_e6": "integer|between:-180000000,180000000",
        "address": "nullable|string|max:512",
        "locations": [
            {
                "latitude_e6": "required|integer|between:-90000000,90000000",
                "longitude_e6": "required|integer|between:-180000000,180000000"
            }
        ]
    },
    "search_radius": "nullable|sometimes|integer|min:0|max:30000"
}

Errores de negocio

Código HTTP Cuándo ocurre
EC225 400 No se envió un destino válido (delivery.latitude_e6 / delivery.longitude_e6 en 0).
EC245 503 No se pudo verificar la disponibilidad de repartidores por un error al calcularla (respuesta bajo contingencia).

Relaciones