Guía Rápida para Integraciones de Socios
El único flujo lineal y canónico para conectar una integración de terceros a tiendas Whatalo con OAuth 2.1 — desde el registro del cliente hasta tu primer webhook verificado.
Esta página es la referencia única del flujo de conexión para integraciones de socios — productos SaaS, plataformas de logística/envíos, herramientas de marketing y cualquier servicio externo que necesite actuar en nombre de uno o más comerciantes de Whatalo. Enlaza las páginas de referencia de esta sección en un solo camino lineal para que exista exactamente una forma correcta de implementarlo.
El patrón en una frase
Tú, como socio integrador, registras UN solo cliente OAuth para todo tu producto. No está vinculado a ninguna tienda en particular. Cada comerciante que quiere conectar tu integración hace clic en un botón "Autorizar", elige su tienda en la pantalla de consentimiento de Whatalo, y eso genera un access token independiente vinculado a su tienda — todo bajo tu único client_id.
Un comerciante nunca crea un Client ID/Secret de OAuth ni nunca ve o pega credenciales OAuth dentro de tu producto. Si tu flujo de onboarding le pide a un comerciante que pegue un Client ID o Client Secret, ese es el patrón incorrecto — detente y sigue el flujo descrito abajo.
El flujo completo
-
Registra tu cliente OAuth una sola vez. Esto lo haces tú, como socio — no por cada comerciante. Recibes un
client_idy unclient_secretque identifican tu integración para todos los comerciantes que se conecten alguna vez. Ver Registro de clientes. -
Guarda
client_idyclient_secreten el entorno de tu backend. Viven en configuración del lado del servidor (gestor de secretos, variables de entorno) — nunca en el bundle de una app móvil, un bundle de navegador, ni en ningún código del lado del cliente.El client secret, y cada
refresh_tokenque recibas después, nunca deben exponerse en código frontend ni enviarse al navegador. Son secretos del lado del servidor durante toda la vida de la integración. -
Agrega un botón "Conectar con Whatalo" que redirija al comerciante a
GET /oauth/authorizecon los scopes que tu integración necesita (incluyewrite:webhookssi vas a autorregistrar un webhook en el paso 6). Ver Flujo de autorización para la forma completa de la solicitud, incluyendo PKCE, y Scopes para la tabla completa de scopes.El comerciante no crea ni provee ninguna credencial en este paso. Inicia sesión en Whatalo, elige la tienda que quiere conectar, revisa los scopes solicitados y hace clic en Autorizar. Esa es toda su participación en el flujo OAuth.
-
Maneja el callback en tu servidor e intercambia el
coderecibido por tokens enPOST /oauth/token. La respuesta contieneaccess_token,refresh_token,expires_inyscope. Este intercambio ocurre servidor a servidor, usando tuclient_secret— nunca en el navegador del comerciante. Ver Token endpoint. -
Persiste los tokens en tu propia base de datos, asociados a la tienda del comerciante. Guarda el
access_token, elrefresh_tokeny la expiración que derivas deexpires_in— los necesitas para cada llamada posterior a la API en nombre de ese comerciante. Elrefresh_tokenrota: cada renovación enPOST /oauth/tokendevuelve unrefresh_tokennuevo, así que sobrescribe el valor guardado cada vez. Elrefresh_tokentambién tiene su propia expiración, y presentar uno ya rotado revoca toda la familia de tokens del comerciante — así que conserva siempre solo el más reciente. Ver Token endpoint para los detalles de renovación y rotación. -
Autorregistra tu webhook con
POST /v1/webhooks, usando elaccess_tokenque acabas de recibir. Esto requiere el scopewrite:webhooksconcedido en el paso 3. La respuesta incluye unsecretde firma (endata.secret) que se devuelve una sola vez — guárdalo de inmediato junto a los tokens de ese comerciante. Ver Webhooks. -
Verifica la firma de cada webhook entrante antes de confiar en su payload. Ver Seguridad de Webhooks para el esquema HMAC-SHA256 exacto y un ejemplo completo de verificación.
¿Por qué el intercambio de tokens ocurre en tu backend y nunca en el navegador del comerciante?
Tu client_secret le prueba a Whatalo que una solicitud de token viene genuinamente de tu integración y no de un impostor. Si alguna vez se enviara a un navegador o a un cliente móvil, cualquiera podría extraerlo y emitir tokens haciéndose pasar por ti. Mantener el intercambio del lado del servidor es exactamente lo que hace imposible el antipatrón: nunca existe un momento en el que un comerciante necesite conocer, generar o pegar una credencial OAuth — la credencial pertenece a tu integración, no a él.
Ejemplo de callback handler
El ejemplo de abajo muestra los pasos 4 a 6 juntos: intercambiar el código de autorización por tokens, persistirlos para el comerciante y luego autorregistrar de inmediato un webhook con el access token resultante.
// Handler estilo Express montado en el redirect_uri que registraste en el paso 1.
// `codeVerifier` y `expectedState` se guardaron del lado del servidor (sesión o
// caché de corta duración) al construir la redirección a /oauth/authorize en el paso 3.
export async function handleOAuthCallback(req, res) {
const { code, state } = req.query;
const { codeVerifier, expectedState, merchant } =
await getPendingAuthorization(state);
if (state !== expectedState) {
return res.status(400).send("Invalid state");
}
// Paso 4 — intercambia el código por tokens, servidor a servidor.
const tokenResponse = await fetch("https://app.whatalo.com/oauth/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization:
"Basic " +
Buffer.from(
`${process.env.WHATALO_CLIENT_ID}:${process.env.WHATALO_CLIENT_SECRET}`
).toString("base64"),
},
body: new URLSearchParams({
grant_type: "authorization_code",
code,
redirect_uri: process.env.WHATALO_REDIRECT_URI,
client_id: process.env.WHATALO_CLIENT_ID,
code_verifier: codeVerifier,
}),
});
const { access_token, refresh_token, expires_in } =
await tokenResponse.json();
// Paso 5 — persiste los tokens en TU base de datos, asociados a la tienda de
// este comerciante, antes que nada. Con ellos llamas a la API después y
// renuevas cuando el access_token expira. El refresh_token rota en cada
// renovación, así que sobrescribe siempre el valor guardado con el más nuevo.
await saveTokensForMerchant(merchant, {
access_token,
refresh_token,
expires_at: Date.now() + expires_in * 1000,
});
// Paso 6 — autorregistra tu webhook con el access token que acabas de recibir.
// Requiere el scope write:webhooks solicitado en el paso 3.
const webhookResponse = await fetch("https://api.whatalo.com/v1/webhooks", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${access_token}`,
},
body: JSON.stringify({
url: "https://your-app.com/webhooks/whatalo",
events: ["order.created", "order.updated"],
}),
});
const { data: webhook } = await webhookResponse.json();
// data.secret se devuelve SOLO en esta respuesta — persístelo ahora, junto a
// los tokens de este comerciante, para la verificación de firma del paso 7.
await saveWebhookSecretForMerchant(merchant, webhook.secret);
return res.redirect("/connected");
}Páginas relacionadas
| Página | Cuándo leerla |
|---|---|
| OAuth Visión general | Modelo conceptual — tokens opacos, API Key vs. OAuth |
| Registro de clientes | Referencia completa de request/response para el paso 1 |
| Flujo de autorización | Referencia completa de PKCE y pantalla de consentimiento para el paso 3 |
| Token endpoint | Referencia completa de grant types para el paso 4, más rotación de refresh |
| Scopes | La tabla completa de scopes para decidir qué solicitar en el paso 3 |
| Webhooks | Referencia completa de endpoints y formas de payload para los pasos 6–7 |
| Errores OAuth | Códigos de error que puedes encontrar en cualquier paso de este flujo |