Cómo integrar AnnA Chat con un usuario corporativo V2
Esta guía muestra cómo integrar AnnA Chat para un usuario corporativo autenticado mediante la API V2. El navegador recibe únicamente la URL generada de AnnA Chat. Mantén el hash de la empresa, las claves de cifrado y descifrado, los datos del usuario y todas las llamadas a la API V2 en tu backend.
Antes de comenzar
Sección titulada «Antes de comenzar»Necesitas la siguiente información de tu entorno AnnA:
- El hash de la empresa.
- Las claves de cifrado y descifrado del usuario corporativo.
- El ID del usuario corporativo.
- La URL base de tu entorno AnnA.
La integración V2 usa cifrado 3DES en modo CBC. Consulta Cifrado para conocer los requisitos y los métodos auxiliares usados en los ejemplos.
Flujo de integración
Sección titulada «Flujo de integración»- Tu backend envía una solicitud
GENERATE_USER_HASHcifrada aaannacorporateuser.aspx. - Tu backend establece
GET_ANNA_CHAT_LINKenSpara que AnnA devuelva los enlaces del chat. - Tu backend descifra la respuesta y lee la propiedad
AnnAChatURL. - Tu código del cliente usa esa URL generada como origen del iframe.
La URL generada puede caducar. Genérala en el servidor para el usuario autenticado y no coloques el hash de la empresa ni las claves de cifrado en el código del navegador.
Respuesta de AnnA
Sección titulada «Respuesta de AnnA»Después de descifrar la respuesta, analiza el JSON devuelto por AnnA. La propiedad AnnAChatURL es la URL que debes usar en el iframe.
{ "Status": "OK", "Message": "User Hash generated successfully", "Hash": "USER_HASH", "ChatURL": "CHAT_URL_WITH_USER_HASH", "EmbeddedURL": "EMBEDDED_URL_WITH_USER_HASH", "AnnAChatURL": "ANNACHAT_URL_FOR_IFRAME", "Expires": "EXPIRATION_DATETIME"}Si la respuesta no indica éxito, gestiona el valor de Message en el backend y no envíes una URL inválida al navegador.
Ejemplo completo
Sección titulada «Ejemplo completo»Reemplaza ANNACHAT_URL_FROM_BACKEND por el valor de AnnAChatURL devuelto por tu backend. El botón crea la ventana del chat solamente después de que el usuario hace clic.
<!doctype html><html lang="es"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Demo de AnnA Chat integrado</title> </head> <body> <h1>Sitio del cliente corporativo</h1> <p> El widget autenticado de AnnA Chat aparece en la esquina inferior derecha. </p>
<script> (function () { const ANNA_URL = "ANNACHAT_URL_FROM_BACKEND"; // Usa el valor AnnAChatURL generado por el backend const CHAT_W = 400, CHAT_H = 600, BTN_SIZE = 60; const sideOffset = "clamp(16px, 2.2vw, 30px)"; const bottomOffset = "clamp(16px, 2.2vw, 30px)";
const btn = document.createElement("div"); btn.id = "anna-launcher"; Object.assign(btn.style, { position: "fixed", bottom: bottomOffset, right: sideOffset, width: BTN_SIZE + "px", height: BTN_SIZE + "px", borderRadius: "50%", cursor: "pointer", zIndex: "9999", background: "#7c3aed", display: "flex", alignItems: "center", justifyContent: "center", boxShadow: "0 4px 12px rgba(0,0,0,0.25)", transition: "transform 0.2s", }); btn.innerHTML = '<svg width="28" height="28" fill="#fff" viewBox="0 0 24 24"><path d="M20 2H4a2 2 0 0 0-2 2v18l4-4h14a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z"/></svg>'; btn.onmouseenter = function () { btn.style.transform = "scale(1.1)"; }; btn.onmouseleave = function () { btn.style.transform = "scale(1)"; };
const chat = document.createElement("div"); chat.id = "anna-chat"; Object.assign(chat.style, { position: "fixed", bottom: `calc(${bottomOffset} + ${BTN_SIZE}px + 10px)`, right: sideOffset, width: `min(${CHAT_W}px, calc(100vw - 32px))`, height: CHAT_H + "px", maxHeight: `min(${CHAT_H}px, calc(100vh - 116px))`, borderRadius: "12px", overflow: "hidden", boxShadow: "0 8px 30px rgba(0,0,0,0.3)", display: "none", zIndex: "9998", }); chat.innerHTML = '<iframe src="' + ANNA_URL + '" style="width:100%;height:100%;border:none" allow="camera *; microphone *; geolocation *"></iframe>';
btn.addEventListener("click", function () { var open = chat.style.display === "none"; chat.style.display = open ? "block" : "none"; });
document.body.appendChild(chat); document.body.appendChild(btn); })(); </script> </body></html>Generar la URL del chat en el backend
Sección titulada «Generar la URL del chat en el backend»Envía la solicitud a YOUR_ANNA_URL/aannacorporateuser.aspx con ACTION=GENERATE_USER_HASH y GET_ANNA_CHAT_LINK=S. Cifra los campos requeridos por la API V2 usando la clave de cifrado y el IV de la solicitud. Descifra el payload devuelto con el IV de la respuesta y lee AnnAChatURL en el JSON.
Los auxiliares Encrypt, Decrypt, GenerateIV y DecryptAnnAResponse de los ejemplos representan los métodos 3DES descritos en Cifrado. Deben ejecutarse en el backend.
var requestIv = GenerateIV();var form = new NameValueCollection{ ["HASH"] = "YOUR_COMPANY_HASH", ["ANNAEXEC"] = requestIv, ["ACTION"] = Encrypt("GENERATE_USER_HASH", "ENCRYPTION_KEY", requestIv), ["USER_ID"] = Encrypt("USER_ID", "ENCRYPTION_KEY", requestIv), ["START_SERVICE"] = Encrypt("", "ENCRYPTION_KEY", requestIv), ["GET_ANNA_CHAT_LINK"] = "S"};using (var client = new WebClient()){ client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; var raw = Encoding.UTF8.GetString(client.UploadValues("https://YOUR_ANNA_URL/aannacorporateuser.aspx", "POST", form)); var json = DecryptAnnAResponse(raw, requestIv, "ENCRYPTION_KEY", "DECRYPTION_KEY"); var response = JObject.Parse(json); if ((string)response["Status"] != "OK") throw new InvalidOperationException((string)response["Message"]); var annaChatUrl = (string)response["AnnAChatURL"];}using var http = new HttpClient();var requestIv = GenerateIV();var form = new Dictionary<string, string>{ ["HASH"] = "YOUR_COMPANY_HASH", ["ANNAEXEC"] = requestIv, ["ACTION"] = Encrypt("GENERATE_USER_HASH", "ENCRYPTION_KEY", requestIv), ["USER_ID"] = Encrypt("USER_ID", "ENCRYPTION_KEY", requestIv), ["START_SERVICE"] = Encrypt("", "ENCRYPTION_KEY", requestIv), ["GET_ANNA_CHAT_LINK"] = "S"};var result = await http.PostAsync("https://YOUR_ANNA_URL/aannacorporateuser.aspx", new FormUrlEncodedContent(form));var json = DecryptAnnAResponse(await result.Content.ReadAsStringAsync(), requestIv, "ENCRYPTION_KEY", "DECRYPTION_KEY");using var response = JsonDocument.Parse(json);var annaChatUrl = response.RootElement.GetProperty("AnnAChatURL").GetString();var requestIv = generateInitializationVector();var form = "HASH=" + encode("YOUR_COMPANY_HASH") + "&ANNAEXEC=" + encode(requestIv) + "&ACTION=" + encode(encrypt3DES("GENERATE_USER_HASH", "ENCRYPTION_KEY", requestIv)) + "&USER_ID=" + encode(encrypt3DES("USER_ID", "ENCRYPTION_KEY", requestIv)) + "&START_SERVICE=" + encode(encrypt3DES("", "ENCRYPTION_KEY", requestIv)) + "&GET_ANNA_CHAT_LINK=S";var request = HttpRequest.newBuilder().uri(URI.create("https://YOUR_ANNA_URL/aannacorporateuser.aspx")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(form)).build();var result = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());var json = decryptAnnAResponse(result.body(), requestIv, "ENCRYPTION_KEY", "DECRYPTION_KEY");var annaChatUrl = new ObjectMapper().readTree(json).get("AnnAChatURL").asText();<?php$requestIv = generateIV();$fields = [ "HASH" => "YOUR_COMPANY_HASH", "ANNAEXEC" => $requestIv, "ACTION" => encrypt3DES("GENERATE_USER_HASH", "ENCRYPTION_KEY", $requestIv), "USER_ID" => encrypt3DES("USER_ID", "ENCRYPTION_KEY", $requestIv), "START_SERVICE" => encrypt3DES("", "ENCRYPTION_KEY", $requestIv), "GET_ANNA_CHAT_LINK" => "S",];$curl = curl_init("https://YOUR_ANNA_URL/aannacorporateuser.aspx");curl_setopt_array($curl, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => http_build_query($fields)]);$raw = curl_exec($curl);curl_close($curl);$response = json_decode(decryptAnnAResponse($raw, $requestIv, "ENCRYPTION_KEY", "DECRYPTION_KEY"), true, 512, JSON_THROW_ON_ERROR);if (($response["Status"] ?? "") !== "OK") throw new RuntimeException($response["Message"] ?? "AnnA request failed");echo $response["AnnAChatURL"] . PHP_EOL;&requestIv = GenerateIV()&body = !"HASH=" + UrlEncode(!"YOUR_COMPANY_HASH")&body += !"&ANNAEXEC=" + UrlEncode(&requestIv)&body += !"&ACTION=" + UrlEncode(Encrypt(!"GENERATE_USER_HASH", !"ENCRYPTION_KEY", &requestIv))&body += !"&USER_ID=" + UrlEncode(Encrypt(!"USER_ID", !"ENCRYPTION_KEY", &requestIv))&body += !"&START_SERVICE=" + UrlEncode(Encrypt(!"", !"ENCRYPTION_KEY", &requestIv))&body += !"&GET_ANNA_CHAT_LINK=S"&httpClient.Host = !"YOUR_ANNA_URL"&httpClient.Execute(!"POST", !"/aannacorporateuser.aspx", &body)&jsonText = DecryptAnnAResponse(&httpClient.ToString(), &requestIv, !"ENCRYPTION_KEY", !"DECRYPTION_KEY")&response.FromJson(&jsonText)&annaChatUrl = &response.AnnAChatURLimport jsonimport requests
request_iv = generate_iv()form = { "HASH": "YOUR_COMPANY_HASH", "ANNAEXEC": request_iv, "ACTION": encrypt_3des("GENERATE_USER_HASH", "ENCRYPTION_KEY", request_iv), "USER_ID": encrypt_3des("USER_ID", "ENCRYPTION_KEY", request_iv), "START_SERVICE": encrypt_3des("", "ENCRYPTION_KEY", request_iv), "GET_ANNA_CHAT_LINK": "S",}result = requests.post("https://YOUR_ANNA_URL/aannacorporateuser.aspx", data=form, timeout=30)result.raise_for_status()response = json.loads(decrypt_anna_response(result.text, request_iv, "ENCRYPTION_KEY", "DECRYPTION_KEY"))if response.get("Status") != "OK": raise RuntimeError(response.get("Message", "AnnA request failed"))anna_chat_url = response["AnnAChatURL"]print(anna_chat_url)Usa el valor devuelto en AnnAChatURL como ANNACHAT_URL_FROM_BACKEND en el ejemplo del cliente. No devuelvas el hash de la empresa, las claves de cifrado ni el hash descifrado del usuario al navegador.
Ver también
Sección titulada «Ver también»- Cómo acceder — cómo se comporta el enlace corporativo para quien lo usa.
- Requisitos y límites — requisitos del navegador y permisos.