Dos números
balance_cents es todo lo que tiene la cuenta. available_cents es la parte
que puede salir hoy.
Hay dos motivos por los que se retiene dinero fuera de available_cents, y en
los dos casos ese dinero se puede gastar en envíos mientras espera:
- El efectivo que cobraron tus repartidores. Cuando se entrega un pedido en efectivo, el valor del pedido se abona a tu saldo de inmediato. Se vuelve retirable una vez que ese efectivo ya se le liquidó a Listo, en un ciclo semanal.
- El dinero que metiste con tarjeta. Una recarga con tarjeta se retiene 180 días desde el cargo, que es la ventana en la que un pago con tarjeta todavía se puede disputar. Una transferencia SPEI no tiene esa ventana y es retirable en cuanto cae.
balance_cents; retirar gasta contra
available_cents. Una parte pequeña del efectivo es la excepción de la
primera mitad.
Cada respuesta trae también in_review_cents. Cuando surge una duda sobre un
pedido en efectivo mientras liquidamos la semana, el efectivo de ese pedido se
queda retenido mientras lo revisamos, y este es el monto de balance_cents que
eso representa. Está dentro del efectivo retenido de arriba, así que nunca entra
en available_cents y no se puede adelantar, y a diferencia del resto del
efectivo retenido no se puede gastar en envíos: crear un envío responde 402 insufficient_balance cuando balance_cents menos in_review_cents no alcanza.
Es 0 en casi todas las cuentas, no trae fecha y se libera cuando tenemos una
respuesta.
Vale la pena planearlo: una cuenta fondeada solo con tarjeta se puede gastar
completa y no se puede retirar nada hasta que venza la primera retención,
seis meses después. Si piensas retirar, fondea por SPEI.
balance_and_withdrawal.rb
Todos los movimientos, en orden
GET /v1/ledger es el
historial detrás de esos dos números: una fila por
movimiento de la cuenta, del más reciente al más viejo, en los mismos centavos
con signo. Los abonos son positivos y los cargos negativos, así que las filas se
suman sin tener que deducir la dirección a partir del tipo.
Esta lista crece. Aparecen valores nuevos de
type en cualquier versión, porque
los cambios a v1 son aditivos y un movimiento que todavía no nombramos sigue
siendo dinero que se movió. Haz tu lógica sobre los valores que conoces y deja
pasar el resto: cuenta un type desconocido dentro del saldo, muéstralo, y no lo
descartes ni falles con él. Las filas que tires son justo la diferencia entre tus
libros y GET /v1/balance.
Una fila que viene de uno de tus pedidos trae su delivery_id, que es como se le
atribuye un costo a un pedido. Eso incluye los pedidos que tus tiendas
despacharon desde su propia consola de Listo: también son pedidos tuyos y traen
el mismo id dlv_, sin external_id porque tú nunca lo enviaste. null
significa que el movimiento no pertenece a ningún pedido: una recarga, un retiro,
el plan de una tienda o un ajuste. test dice a qué mundo pertenece la fila,
aunque una llave solo ve el suyo.
store_id dice a qué tienda pertenece un movimiento, y viene en cada fila: la
tienda del pedido en una fila que viene de uno, la tienda cuyo plan se pagó en un
plan_charge. Viene en null cuando el movimiento no pertenece a una sola
tienda, como una recarga o un retiro de tu propio saldo.
Dos filtros, y responden las dos preguntas que vale la pena hacer:
GET /v1/ledger?delivery_id=dlv_...es todo lo que se movió por un solo pedido. Esto es lo que le facturas a tu propio comercio.GET /v1/ledger?type=provider_chargees cada cargo por viaje desperdiciado que se te ha cobrado, de todos los pedidos.
type que no reconocemos no
empata con nada y devuelve una página vacía, en vez de devolverte calladamente
todo, y un delivery_id que no es un pedido tuyo en este modo devuelve
404 not_found y no una página vacía, porque una página vacía es una respuesta
verdadera a “ese pedido no costó nada extra” y se leería como tal.
Cada uno recibe exactamente un valor. No hay lista separada por comas ni llave
repetida, y mandar cualquiera de las dos no amplía el filtro: es un valor que no
empata con nada, así que type regresa vacío y delivery_id se rechaza como
cualquier otro id que no sea tuyo.
Se pagina como cualquier otro listado de aquí: hasta 50 filas, un booleano
has_more y un cursor opaco que regresas para pedir la siguiente página. El
cursor es null en la última página, así que nunca puede contradecir a
has_more sobre si vale la pena pedir otra.
Solo camina hacia atrás, hacia el historial. Para agarrar los movimientos que se
registraron desde tu último recorrido, vuelve a leer la primera página en lugar
de reenviar el cursor con el que terminaste.
Cómo cobrarle un costo al pedido que lo causó
Esta es la conciliación para la que existe el historial: recorres cada cargo por viaje desperdiciado y le cobras cada uno al pedido que lo provocó.reconcile_charges.rb
cursor hasta que has_more sea falso, porque un recorrido que
lee una sola página nada más le cobra a tus pedidos más recientes. Los montos se
suman tal como llegan, porque un cargo es negativo y un valor absoluto
convertiría cada cargo en un abono. Y una fila cuyo delivery_id es null se
señala en vez de atribuirse, porque adivinar a qué pedido pertenecía es la forma
de cobrarle a un comercio el viaje de alguien más.
El saldo de una sola tienda
Todo lo de arriba es tu cuenta. Una tienda puede ser dueña de su dinero, que es lo que decide elaccess_tier del objeto de tienda. En
el modelo de dinero está qué significa ese arreglo;
aquí está cómo leerlo.
Dos endpoints responden por una tienda, y responden por todas las tiendas y
no solo por las que son dueñas de su dinero:
GET /v1/balance. El cuarto dice de quién es el saldo que estás viendo:
"store" es el de la tienda, "partner" es el tuyo, que es lo que reporta una
tienda en el tier por defecto porque ese sí es el saldo del que gastan sus
envíos. Dos tiendas que gastan de tu saldo reportan la misma cifra, y es la misma
que da GET /v1/balance.
El historial es la misma ventana con la misma forma: los mismos campos por fila,
el mismo vocabulario de type, las mismas páginas de 50 filas y el mismo cursor
opaco, y los dos filtros funcionan igual. Lo que hayas escrito para recorrer
GET /v1/ledger recorre este sin cambiarle nada.
Una tienda que gasta de tu saldo muestra tus movimientosEn una tienda cuyo
billing es "partner", estos dos endpoints están leyendo tu
cuenta, así que las filas incluyen movimientos de tus otras tiendas. Esa es la
respuesta honesta a “qué se ha movido en el saldo del que gasta esta tienda”, y
son los mismos datos que GET /v1/ledger ya le da a la misma llave.?delivery_id= sigue la misma regla: nombra cualquier pedido tuyo en este modo,
no solo los de esta tienda. Uno que se cobró a otra cuenta regresa como una
página vacía, y uno que no es tuyo es 404 not_found.Cómo darle acceso al dueño de una tienda
Una tienda dueña de su dinero necesita a alguien que pueda verlo, así quePOST /v1/stores/{id}/owner
crea el acceso de esa persona a Listo.
422 console_access_requireden una tienda cuyoaccess_tieresapi_only. Esa tienda no tiene consola a la cual entrar, así que no hay a qué invitar a nadie. Cambia el tier primero y vuelve a llamar.422 validation_failedconparam: "email"cuando la tienda ya tiene un dueño con otra dirección. Elmessagedice quién es. Una tienda tiene un solo dueño y este endpoint no puede cambiarlo. Para mandarle una liga nueva a ese dueño, llama con la dirección que aparece en elmessage.422 validation_failedconparam: "email"cuando otra petición está dando de alta esa misma dirección para esta tienda en ese mismo momento. Repite la llamada: para entonces la primera ya terminó y la tuya reenvía la liga.
Cómo fondearlo
Las recargas no son una llamada a la API. Fondea el saldo desde el panel de socios, con tarjeta o transfiriendo a la CLABE permanente que ahí aparece. Esa CLABE no cambia, así que la puedes guardar como beneficiario sin problema. Las dos rutas se diferencian en más que la comodidad, y vale la pena elegir:
La comisión de tarjeta es 3.6% más MX$3.00, ambos más IVA, calculados hacia
arriba para que el monto que pediste abonar sea el que llega, y no el monto al
que se le descontó la comisión. La recarga mínima con tarjeta es de MX$10.
Deja un colchón. Una creación cuya parte del negocio no alcanza a cubrir el
saldo se rechaza con
402 insufficient_balance y no se despacha nada, así que
un saldo vacío detiene pedidos nuevos.
Lo que no detiene es todo, y esta es la única forma en que un saldo se va a
negativo. El cargo por una cancelación tardía o por una devolución se registra
alcance o no alcance el saldo, porque ese viaje ya se manejó y no registrarlo no
lo desharía. Así que un pedido cancelado cuando ya hay un
repartidor encima, o uno que se regresa tras una entrega fallida, te puede dejar
abajo de cero hasta que recargues, y un saldo negativo bloquea la siguiente
creación.
Es más acotado de lo que suena: no se cobra nada si no hubo un repartidor
realmente comprometido, y un pedido que terminó en no_courier no cuesta.
Lo que sí se cobra todavía puede regresar. Un viaje cancelado por el repartidor,
o por una falla del lado de Listo, es un costo que reclamamos por tu cuenta
después del hecho. Cuando eso prospera, llega como un provider_charge_reversal:
el mismo monto, abonado completo, contra el mismo pedido. Es una fila aparte y no un
encogimiento retroactivo del cargo, así que siempre puedes ver tanto lo que se te
cobró como lo que regresó, y puede registrarse semanas después del cargo. No
dejes una factura abierta esperándolo, ni des por hecho que un cargo se va a
revertir. Cobra el cargo, y traslada el abono a tu propio comercio si llega y
cuando llegue.
Un saldo que bajó sin que se creara un envío siempre tiene explicación.
GET /v1/ledger?type=provider_charge lista cada uno de esos cargos con el pedido
del que salió, y ?type=provider_charge_reversal lista los que regresaron.
Cómo retirar
POST /v1/withdrawals con un amount_cents. El dinero queda apartado en cuanto
la llamada responde: sale de available_cents de inmediato, así que un segundo
retiro no puede gastarlo también.
pending mientras se gestiona la transferencia, y luego se resuelve
en paid o, si la transferencia rebota, en failed, lo cual regresa el monto
apartado a tu saldo disponible. Te enteras de cuál por el webhook
withdrawal.updated, o leyéndolo de vuelta con
GET /v1/withdrawals/{id}.
Cómo recibirlo el mismo día
kind es opcional y vale standard cuando lo omites, que es todo lo anterior:
solo tu saldo ya liberado, sin comisión, enviado en 1 a 3 días hábiles. Nada de
ese camino cambió, así que una integración escrita antes de que esto existiera
sigue pidiendo exactamente lo que siempre pidió.
Manda "kind": "same_day" y la transferencia sale el mismo día.
fee_cents. La comisión es un SEGUNDO cargo y no un descuento, así que tu banco
recibe completo el amount_cents y tu saldo pierde los dos juntos. Concilia un
depósito contra amount_cents en los dos tipos; concilia tu saldo contra la
suma. fee_cents es 0 en todo retiro standard, así que puedes sumar los dos
sin ramificar.
Alcanza el efectivo que todavía está en espera, cosa que el camino sin costo
no puede, así que aquí el límite no es available_cents. Lo que puede tomar es
un tope que calculamos por cuenta y que ya viene con la comisión descontada, y
ese tope lo puedes leer antes de pedir: viene en GET /v1/balance.
Sale el mismo día, entre las 8 am y las 8 pm del centro de México. Puedes
pedirlo a cualquier hora: una solicitud hecha dentro de ese horario vence tres
horas después, y una hecha fuera sale a la mañana siguiente. due_at es la
fecha límite que tu solicitud realmente lleva, sellada cuando la hiciste y nunca
recalculada, para que puedas exigirnos la promesa que te dimos y no una regla
que pudo haber cambiado después. Es null en todo retiro standard, porque el
camino sin costo promete un rango de días hábiles y no un instante.
Hay un mínimo por solicitud y un máximo diario por cuenta, y cada rechazo de
abajo nombra su propio límite en message.
Tres rechazos pertenecen a este camino:
Solo el primero se trata de falta de dinero, y por eso los otros dos no son
402: a quien rechaza el máximo del día puede sobrarle saldo, y a quien rechaza
el mínimo le sobra también, solo que está pidiendo muy poco.
same_day_cap_exceeded pone param en amount_cents mientras quede espacio en
el día y lo deja en null cuando el día ya se agotó, porque en ese estado ningún
monto pasa antes de mañana por la mañana. same_day_below_minimum siempre lo
pone, porque un número más grande siempre funciona.
Cómo leer la oferta antes de pedir
GET /v1/balance trae un objeto same_day junto a sus tres cifras. Describe la
OFERTA y no un cuarto saldo, así que puedes dibujar la opción en lugar de
descubrirla en un rechazo.
ceiling_cents es el amount_cents más grande que aceptaríamos ahora mismo en
un retiro del mismo día. No es otro nombre para available_cents. El tope
ya trae la comisión descontada, ya está limitado por lo que queda del día y
alcanza efectivo en espera que available_cents deja fuera por definición. En
una cuenta con efectivo cobrado en la puerta es el número mayor; en una cuenta
cuyos cargos rebasaron el saldo es el menor, o cero. Lee el que responde tu
pregunta y nunca lo deduzcas del otro.
reach_cents es todo lo que el retiro podría tomar antes de la comisión, y
advanceable_cents es la parte de eso que sigue en espera. minimum_cents es
el mínimo por solicitud, y cap_cents, cap_used_cents y cap_remaining_cents
son el máximo diario por cuenta y en dónde vas hoy contra él, para que un tope
chico junto a un saldo grande se lea como un límite y no como una falla.
offerable es falso cuando ninguna solicitud del mismo día sería aceptada, y
unavailable_reason dice en cuál de los dos estados estás: cap_reached, que
se libera solo mañana por la mañana, o below_minimum, que no se libera hasta
que entre más dinero. Son los mismos dos estados detrás de
same_day_cap_exceeded y same_day_below_minimum, así que puedes mostrar el
motivo sin gastar una solicitud en averiguarlo. Es null siempre que offerable
sea verdadero.
window_open dice si estamos enviando en este momento. due_at es la fecha
límite que cargaría una solicitud hecha en ese instante y
next_window_opens_at es cuándo se reanudan los envíos. La fecha límite que tu
solicitud realmente carga es el due_at del retiro que regresa, sellado en ese
momento y nunca recalculado.
Cómo casar un pago con tu estado de cuenta
clave_de_rastreo es la clave de rastreo SPEI de la transferencia, el
identificador con el que tu propio banco localiza el depósito. Es con lo que se
concilia contra un estado de cuenta: el monto se repite entre solicitudes,
requested_at es nuestro reloj y no el del banco, y el id wd_ no significa
nada fuera de Listo.
Siempre viene en el objeto y es null hasta que la transferencia se envía de
verdad, así que llega junto con el estado paid en el webhook
withdrawal.updated y en cualquier lectura posterior. En un retiro failed se
queda en null, porque nunca hubo transferencia.
available_cents devuelve 402 insufficient_balance y no aparta
nada.
No hay verbo para cancelar ni modificar un retiro. Una vez solicitado termina
pagado o fallido, y los dos desenlaces se te avisan.
La transferencia se hace a la cuenta bancaria registrada, que configuras en el
panel. Regístrala antes de solicitar tu primer retiro: sin ella esta llamada
responde 422 payout_account_not_ready y no aparta nada.
El mismo rechazo cubre el día siguiente a que CAMBIES esa cuenta. Un destino
recién movido no puede recibir dinero durante 24 horas, que es la ventana en la
que todavía se puede detener a alguien que no seas tú moviéndolo, y el message
del rechazo dice el instante exacto en que vuelve a poder. Esperar es el único
remedio: volver a guardar la misma cuenta reinicia la ventana.
A dónde va el dinero de las tarjetas
Los pedidos con tarjeta no se liquidan por este saldo en absoluto. Lo que el cliente paga por la mercancía se va directo a quien figure como comerciante de registro de esa tienda, la parte de Listo de la tarifa de envío se toma de ese mismo cargo, y nada de eso aparece enbalance_cents.
El único movimiento de un pedido con tarjeta que vas a ver en el saldo es tu
propia parte de la tarifa de envío, cargada al crear como en cualquier otro
pedido.