Saltar al contenido principal
La mayoría de las tiendas que agregas son nuevas para Listo, y las creas con POST /v1/stores. Algunos restaurantes ya usan Listo con una cuenta propia: su propia consola, su propio saldo, su propio historial de envíos. Para uno de esos no creas una tienda. Le pides a su dueño que conecte la cuenta que ya tiene, la aprueba en una página de Listo y tú recibes la tienda. En el camino no se crea nada. Lo que terminas teniendo es una tienda común y corriente, y todos los demás endpoints la tratan exactamente igual que a una que tú creaste.
Solo con llaves realesEste flujo funciona con una llave real y con nada más. Una llave de prueba recibe 404 not_found de los dos endpoints de esta página, porque no existe una versión de prueba de la cuenta real de un restaurante real. Lo que ensayas en modo de prueba es POST /v1/stores, que es lo que corre siempre que el dueño no tiene una cuenta propia.

Crear o conectar

owner_email en POST /v1/stores es como sabes cuál de las dos cosas vas a hacer. Es una comprobación, no un campo de la tienda: mandas la dirección de quien lleva el local y, antes de escribir nada, Listo revisa si esa dirección ya tiene acceso a la cuenta propia de algún restaurante en Listo.
  • No hay ninguna. La tienda se crea, y de ahí el camino es el de siempre: ajusta su access_tier (ve el modelo de dinero) y, si alguien del restaurante va a entrar, dale un acceso.
  • Hay una. La creación responde 409 account_exists y no se crea nada. Una segunda cuenta para una misma cocina partiría en dos su saldo, su consola y su historial. Ese 409 es la puerta de entrada a esta página.
Nada guarda la dirección y ningún objeto de tienda la devuelve. Solo decide cuál de los dos caminos tomas.

Primero registra tu URI de redirección

Cuando el dueño decide, Listo manda su navegador de regreso contigo, y solo a una dirección que tú registraste. Agrega tu callback en la página URIs de redirección del panel de socios antes de tu primera petición.
  • Se compara la cadena completa. Registra la URL completa de tu callback, no solo el origen. No hay comodines ni prefijos, así que https://partner.example.com/listo/callback no permite también https://partner.example.com/listo/callback/done.
  • Tiene que ser https://, excepto en localhost y 127.0.0.1, donde se permite http:// para que puedas desarrollar en tu propia máquina.
  • La lista es de tu empresa, no de un modo, así que no cambia cuando cambias entre real y prueba en el panel. Registra tu callback de producción y el que usas para desarrollar.
  • Una dirección que no está en la lista se rechaza cuando pides el enlace, con 422 validation_failed y param: "redirect_uri". Falla en tu propio servidor, antes de que a cualquier restaurante se le muestre nada.

Pide el enlace

POST /v1/connect_requests recibe la dirección del dueño, una de tus URIs de redirección registradas y, si quieres, un state propio:
Responde 201 con un enlace y una fecha límite:
Es todo lo que te dice. Listo averigua a qué restaurante pertenece la dirección y se lo guarda: sabes qué cuenta conectaste solo si el dueño aprueba. El enlace sirve 30 minutos desde que lo pides. Pedir otro no cuesta nada, así que un enlace vencido es un botón que se vuelve a mostrar, no un error que manejar. Pídelo cuando el dueño esté por usarlo, no con horas de anticipación. state es tuyo: cualquier valor de hasta 255 caracteres, que Listo nunca lee y te regresa intacto. Es como amarras el navegador que regresa con la sesión que salió.
connect_request.rb
El enlace lleva un token que abre la página de consentimiento del dueño, y por eso el ejemplo imprime solo su host. Mándaselo al dueño y a nadie más, y déjalo fuera de tus logs.

Qué ve el dueño

Manda el navegador del dueño a url, con una redirección, un enlace o un botón en tu producto. Todo lo que sigue pasa en Listo. Esto es lo que lee, con las palabras de su pantalla, para que tú y el restaurante estén viendo las mismas frases si te llama. El inicio de sesión. A menos que ese navegador ya tenga abierta su sesión en la consola de Listo, llega a una página titulada “Conecta tu cuenta”, que dice “Inicia sesión para conectar su restaurante con tu empresa”. Escribe el correo y la contraseña que ya usa en Listo y presiona Iniciar sesión y continuar. Aquí no hay registro: la única otra salida de la página es “¿Sin cuenta de Listo? Regresa a tu empresa”, que lo trae de regreso contigo con error=access_denied. La pantalla de consentimiento. Se titula “Tu empresa quiere conectar con tu cuenta” y dice “Autoriza la conexión para que tu empresa envíe y administre entregas con tu cuenta de Listo.” Muestra su restaurante y la dirección con la que entró, y después lo que está otorgando, bajo “Al conectar, tu empresa podrá:”
  • Crear entregas a nombre de tu negocio
  • Ver tus entregas y su estado
  • Consultar tu saldo de entregas
Cierra con “Tus entregas se siguen pagando desde tu saldo de Listo, como hasta ahora.” y dos botones, Permitir conexión y Cancelar. Cuando el restaurante tiene más de una sucursal, la pantalla de consentimiento muestra también cada sucursal que se va a conectar, porque aprobar las conecta todas a la vez (ve qué es una tienda conectada). Los dos nombres de esas páginas vienen de Listo, nunca de tu petición: tu empresa es el nombre de tu empresa tal como Listo lo tiene en tu cuenta de socio, y su restaurante es el nombre del negocio del restaurante en Listo, la cuenta completa y no una de sus sucursales. Ningún campo de esta API pone palabras en esas páginas. Solo un dueño de la cuenta puede aprobar, y no tiene que ser la persona cuya dirección mandaste: esa dirección solo es como Listo encontró la cuenta. A alguien del personal que entra se le dice “Solo un propietario puede autorizar esta conexión.” Un enlace que venció o se canceló abre una página titulada “Esta solicitud ya no es válida”; un enlace que el dueño ya aprobó abre una titulada “Esta conexión ya está aprobada”. Las dos tienen un botón para regresar contigo, y ese botón los regresa contigo con el resultado que ese enlace ya tenía.

El navegador regresa

Cuando el dueño decide, Listo manda su navegador a tu redirect_uri: error=access_denied significa que no se conectó nada y que ese enlace ya terminó, ya sea que el dueño haya presionado Cancelar en la pantalla de consentimiento, se haya regresado desde la página de inicio de sesión o haya abierto un enlace que ya no era válido. No se puede reabrir, así que si el dueño cambia de opinión, pide uno nuevo. Si el dueño vuelve a abrir un enlace que ya aprobó, su navegador regresa contigo con el mismo code otra vez; canjearlo dos veces es seguro. Listo no le manda ningún correo al dueño por esto, apruebe o cancele. Si quieres confirmarle la conexión, ese mensaje te toca a ti. state regresa exactamente como lo mandaste, en los dos casos. Compáralo con el valor que guardaste para esa sesión antes de actuar sobre la redirección, e ignora una cuyo state no reconozcas: es tu protección contra un callback falsificado. Si no mandaste state, no regresa ninguno. Si la URI que registraste ya trae parámetros propios, estos se le agregan.

El webhook no espera al navegador

Cuando el dueño aprueba, un evento store.connected va a tu endpoint de webhooks real, y data es el objeto de tienda completo, id incluido. No depende del navegador del dueño, así que puede llegarte antes que la redirección, y te sigue llegando cuando la redirección nunca llega: un dueño puede aprobar en el teléfono y cerrar la pestaña antes de que su navegador regrese contigo. Da la tienda por conectada cuando te llegue cualquiera de los dos, lo que llegue primero, y deduplica por el id del evento como con todos los eventos. Cómo configurar el endpoint está en recibe webhooks.

Canjea el código

POST /v1/connections recibe el code de la redirección:
Responde 201 con el objeto de tienda, exactamente la forma que regresa POST /v1/stores, con el id st_... incluido. A partir de ahí es una tienda común en todos los demás endpoints. El canje responde con una tienda, y un restaurante con varias sucursales llega como varias tiendas. Las demás llegan como sus propios eventos store.connected y están en GET /v1/stores, así que arma tu lista de tiendas conectadas con eso, nunca solo con el canje.
exchange_connection.rb
El código sirve 24 horas desde la aprobación. Es mucho más que el enlace, a propósito: lo que el código tiene que aguantar es una llamada entre dos servidores, que quizá haya que reintentar durante una caída de cualquiera de los dos lados. Pasado eso es 400 code_expired, pero la conexión en sí ocurrió cuando el dueño aprobó: la tienda está en GET /v1/stores y su store.connected se mandó en ese momento, así que tómala de ahí. No inicies una nueva solicitud de conexión para ella, porque la cuenta ya está conectada. Canjear un código por segunda vez regresa la misma tienda. No hay un rechazo de “ya usado”, así que un canje cuya respuesta nunca te llegó simplemente se repite. Solo tu llave puede canjear tus códigos: cualquier otra recibe 404 not_found por uno.

Qué es una tienda conectada

  • Está en access_tier: "full" y billing: "store". El restaurante conserva toda su consola, su saldo y su historial, y los envíos que creas para él se pagan de su propio saldo, como decía su pantalla de consentimiento. Los pedidos que despacha desde su propia consola te llegan como cualquier otro; ve de dónde viene un envío.
  • Desde la siguiente renovación del restaurante, su Plan Listo se te cobra a ti. Hasta entonces sigue pagando su propio plan, y nada se cobra dos veces. Si no tenía un plan vigente, el plan empieza el día en que se conecta y ese mes se te cobra ese mismo día. Si su plan está sin pagar cuando se conecta, tu plan también empieza ese día: el mes que no había pagado se anula, así que no se le cobra a nadie, y ese mismo día se te cobra un mes completo. El bloque plan de la tienda dice cuál es; ve plan y cobro. Si tu acuerdo hace que cada restaurante pague su propio plan, nada de esto aplica: el restaurante sigue pagando su propio plan, nada pasa a ti y no se te cobra nada; ve cuando cada restaurante paga su propio plan.
  • Todas las sucursales se conectan a la vez. El dueño da su consentimiento por la cuenta, no por un local, y nada en el flujo pregunta por sucursales. Un restaurante con tres sucursales se vuelve tres tiendas, cada una con su propio id st_...: el canje responde con una de ellas, cada una de las tres manda su propio store.connected, y todas están en GET /v1/stores desde ese momento. Un canje no significa una sola tienda. Cada una lleva el nombre del negocio y de la sucursal, Tortería La Mera Mera, Zapopan, así que tu lista sigue leyéndose como un solo restaurante. Una sucursal cuyo nombre ya empieza con el del negocio, como Tortería La Mera Mera Centro, se publica tal cual.
  • No tiene external_id, porque nunca mandaste uno. Ponle uno con PATCH /v1/stores/{id} si identificas tus tiendas con tus propios ids.
  • Conectar no es transferir. Una cuenta que ya está conectada, contigo o con cualquier otro, no se puede volver a conectar, y esta API no tiene forma de mover una tienda de un socio a otro. Es a propósito: el restaurante cuyo saldo paga tendría que estar de acuerdo.

Cuándo se rechaza

Los dos rechazos de owner_email comparten código, y error.message los distingue para quien lea tus logs. Ramifica según el código, y muéstrale el mensaje a la persona que está configurando la conexión.