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.
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 |
resumeresume 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 |
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.items[].is_valid indica si la configuración de ese producto (variantes, stock, límites)
es válida tras el último recálculo.| 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 [] |
{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 |
{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 |
{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 |
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 Carrito
Elimina el carrito y descarta todos los cambios realizados.
| Método | URI | Cabeceras |
|---|---|---|
| DELETE | /companies/{companyId}/carts/{cartId} |
Authorization |
| Código | HTTP | Cuándo ocurre |
|---|---|---|
EC600 |
400 | El carrito no está en estado pending. |
{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 |
{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 |
{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 |
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 |
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"
}
| 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. |
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"
}
| Código | HTTP | Cuándo ocurre |
|---|---|---|
EC601 |
400 | Un producto de la orden no existe en el comercio del 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"
}
| 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. |
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"
}
| 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. |
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"
}
| 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). |
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"
}
| 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). |