Club externo
Un motor de reservas envía al huésped a un inicio de sesión de GuestMaker con la marca del hotel y lo recibe autenticado, sin biblioteca OIDC ni registro de URI de redirección por página de búsqueda. Úsalo si la URL de retorno es dinámica (incluye fechas, hotel y ocupación) y una coincidencia exacta de redirect_uri no permite representarla. Si tu URL de retorno es un único callback fijo, usa preferentemente el inicio rápido de OIDC.
- Proporcionamos un
client_idy unclient_secret(se muestra una vez) y registramos una lista de hosts de retorno permitidos. Se acepta cualquier ruta y consulta en un host permitido, para conservar el contexto de búsqueda; no hace falta registrar cada URL exacta. http://localhostse acepta como host de retorno para desarrollo. Es el único host sin HTTPS que permitimos, para que puedas ejecutar una integración real en local.- Las llamadas de intercambio y perfil se ejecutan solo en tu backend. El
client_secretnunca llega a un navegador. - Te indicamos el modo de entrega:
code,fragmentobothdurante la migración. Gestiona el que tengas configurado; el paso 2 cubre los tres.
1. Envía al huésped al inicio de sesión
Codifica la URL de retorno completa. De lo contrario, sus propios ? y & se interpretarían como parámetros de la URL de inicio de sesión y truncarían sin aviso la dirección de retorno del huésped. redirectUrl y locale también se aceptan como domain y lang.
const returnUrl = "https://book.hotel.com/rates?checkin=2026-10-01&adults=2";
const login = new URL("https://www.guestmaker.ai/loyalty/club/login");
login.searchParams.set("client_id", process.env.GM_CLIENT_ID);
login.searchParams.set("redirectUrl", returnUrl); // encoded for you by searchParams
login.searchParams.set("locale", "pt"); // pt | es | en | fr | de | it
redirect(login.toString());2. Vuelve con un código o un token
El huésped vuelve a tu URL de retorno con la transferencia adjunta según el modo de entrega. Un fragmento nunca se envía al servidor: si usas fragment, solo el navegador puede leerlo. Nunca lo copies a una cadena de consulta como solución: todos los proxies y registros de acceso la guardan. Por eso existe hlCode.
// ?hlCode=<32 chars> → your server reads it (delivery: code)
// #hlToken=<jwt> → only the browser sees it (delivery: fragment)
// both → prefer the CODE and ignore the fragment
const code = new URL(req.url).searchParams.get("hlCode");
if (code) return exchange(code); // step 33. Intercambia el código
Un solo uso, cinco minutos y vinculado al cliente para el que se emitió. Todos los rechazos devuelven el mismo invalid_grant: desconocido, caducado, ya canjeado y cliente incorrecto son indistinguibles por diseño. Si falla, repite el inicio de sesión en lugar de reintentar.
const res = await fetch("https://www.guestmaker.ai/api/loyalty/club/exchange", {
method: "POST",
headers: {
"X-Client-Id": process.env.GM_CLIENT_ID,
"X-Client-Secret": process.env.GM_CLIENT_SECRET,
"Content-Type": "application/json",
},
body: JSON.stringify({ code }),
});
const { token, expires_in, member } = await res.json();4. Lee el perfil del socio
El intercambio ya devuelve el socio, así que esto sirve para lecturas posteriores mientras dure el token. Requiere el Bearer y las credenciales del cliente: un token robado por sí solo no permite obtener datos del huésped.
const me = await fetch("https://www.guestmaker.ai/api/loyalty/club/me", {
headers: {
"X-Client-Id": process.env.GM_CLIENT_ID,
"X-Client-Secret": process.env.GM_CLIENT_SECRET,
Authorization: `Bearer ${token}`,
},
}).then((r) => r.json());
// { id, email, firstName, lastName, tier, language, marketingConsent, ... }El POST de inicio de sesión puede completarse con un 303 y una transferencia válida, mientras el navegador rechaza la redirección final. Ninguna de las partes muestra un error. Comprueba, en este orden:
- Tu host de retorno está en la lista permitida. Si no está, la entrada de inicio de sesión devuelve
403antes de iniciar sesión. Ese caso muestra un error y es fácil de detectar. - La consola del navegador al pulsar iniciar sesión. Una infracción de
form-actionen Content-Security-Policy indica la URL de envío, no la redirección bloqueada. Parece que ha fallado el POST, aunque se ha completado correctamente. Avísanos para que lo corrijamos. - El inicio de sesión dura 15 minutos. Un huésped que deja la pestaña y vuelve empieza de nuevo, sin una sesión antigua.
examples/loyalty-club-rp/server.mjs: un archivo, sin dependencias.Un motor de reservas ficticio que implementa todo lo anterior: ambas formas de los parámetros, los tres modos de entrega, el fragmento devuelto desde el navegador y la lectura del perfil. Se ejecuta en localhost, que incluimos en la lista permitida para este fin.
GM_CLIENT_ID=… GM_CLIENT_SECRET=… node server.mjs # → http://localhost:4321
Al arrancar, realiza una entrada de inicio de sesión real con tu configuración e indica si la transferencia puede completarse, antes de que pulses nada. Solicítalo con tus credenciales de sandbox.