Vai al contenuto

Widget — Integracja techniczna

Przewodnik dla programistów: skrypt, payload init(), powiązanie i tworzenie firmy, cykl życia, uwierzytelnione przywrócenie (restore_id), CSP i weryfikacja.

Strony ścieżki: Przegląd · B2C · B2B.

Wydanie odniesienia: 26 października 2026. Przywrócenie między urządzeniami dochodzi do payloadu firmy. Zanim je włączysz, sprawdź, czy provisionRestoreId jest dostępne w Twojej instalacji.

Interfejs działa w iframe. Strona gospodarza nie potrzebuje Angulara, klienta Supabase ani dostępu do bazy Quoting24.


Wybór typu integracji

Przypadek Implementacja Ciągłość
Strona publiczna, nieznany odwiedzający Tylko skrypt. Formularz kontaktu Sesja gościa w tej samej przeglądarce
Strona z danymi klienta Skrypt i init() z profilem i firmą Wstępnie wypełniona sesja gościa i powiązanie firmy
Portal z osobistą historią między urządzeniami Także backend dla external_user_id i restore_id Osobista historia na uprawnionym kanale widget

Powiązanie firmy i przywrócenie historii to osobne operacje. external_id, company_name, imię, e-mail i telefon nie dają rozmów innej osoby. Użytkownik zalogowany na stronie pozostaje gościem widgetu, dopóki nie podasz ważnego poświadczenia przywrócenia.


1. Konfiguracja z Quoting24

W Ustawienia → Kanały otwórz kanał widget i pobierz:

  • pełny URL skryptu i klucz publiczny
  • dozwolone domeny, łącznie z testami i rozwojem lokalnym
  • logo, kolor, pozycję, powitanie, wiadomość powitalną i godziny

Użyj URL i klucza tego samego środowiska. W przykładach zastąp HOST_WIDGET i KLUCZ_PUBLICZNY_WIDGET. Klucz w URL jest publiczny i wybiera kanał: to nie poświadczenie osobiste ani sekret integracji backendu.

Domeny to nazwy hostów (shop.example.com, localhost), bez https://, portu, ścieżki i prefiksu *.. example.com dopuszcza też swoje subdomeny. Pusta lista nie nakłada ograniczenia. Do testu lokalnego serwuj stronę przez HTTP, nie jako plik.

Wartość Gdzie ją trzymać Cel
Klucz publiczny URL skryptu Wybiera kanał widget
Prywatny sekret integracji Tylko sekrety backendu Pozwala tworzyć i ponownie używać restore_id
restore_id Prywatny magazyn na użytkownika; na stronie tylko przy sesji uwierzytelnionej Wskazuje, którą historię przywrócić
Poświadczenie sesji gościa localStorage strony, według klucza widgetu, zarządzane przez loader Ciągłość gościa w tej samej przeglądarce

Przywrócenie wymaga integracji backendu, prywatnego endpointu i sekretu, osobno od kodu publicznego. Zobacz sekcję 6.


2. Instalacja

Pływający przycisk

Raz, przed </body>:

<script
  src="https://HOST_WIDGET/assets/widget/script.js?key=KLUCZ_PUBLICZNY_WIDGET"
  async
></script>

Przycisk pojawia się po załadowaniu ustawień graficznych. Nowy odwiedzający otwiera formularz z telefonem i opcjonalnym imieniem. Loader przechowuje poświadczenie gościa w localStorage, osobno dla klucza. Gdy pamięć jest zablokowana, ciągłość może ograniczyć się do bieżącej strony.

Loader udostępnia jedno window.Quoting24Widget. Drugi skrypt nie tworzy drugiego widgetu.

Czat osadzony

Kontener przed skryptem, jawna wysokość:

<section aria-labelledby="tytul-wsparcia">
  <h2 id="tytul-wsparcia">Wsparcie</h2>
  <div id="chat-wsparcia" style="width: 100%; height: 600px;"></div>
</section>

<script
  src="https://HOST_WIDGET/assets/widget/script.js?key=KLUCZ_PUBLICZNY_WIDGET&amp;mode=embedded&amp;target=chat-wsparcia"
  async
></script>

target to ID, bez #. Iframe wypełnia kontener. Nie ma pływającego przycisku. open(), close() i toggle() nie działają.

Czekanie na loader

Przy danych wstępnych użyj tego modułu zamiast statycznego znacznika, importowanego raz w głównym układzie. Przykłady to moduły JavaScript: bundler albo <script type="module" src="/assets/wsparcie.js"></script>.

// /assets/quoting24-loader.js
const widgetScriptUrl =
  "https://HOST_WIDGET/assets/widget/script.js?key=KLUCZ_PUBLICZNY_WIDGET";

let widgetPromise;

export function loadWidget() {
  if (window.Quoting24Widget) {
    return Promise.resolve(window.Quoting24Widget);
  }
  if (widgetPromise) return widgetPromise;

  widgetPromise = new Promise((resolve, reject) => {
    const script = document.createElement("script");
    script.id = "quoting24-widget-script";
    script.src = widgetScriptUrl;
    script.async = true;
    script.onload = () => {
      if (window.Quoting24Widget) resolve(window.Quoting24Widget);
      else {
        script.remove();
        reject(new Error("API widgetu jest niedostępne"));
      }
    };
    script.onerror = () => {
      script.remove();
      reject(new Error("Nie udało się załadować widgetu"));
    };
    document.body.appendChild(script);
  }).catch((error) => {
    widgetPromise = undefined;
    throw error;
  });

  return widgetPromise;
}
import { loadWidget } from "/assets/quoting24-loader.js";

const widget = await loadWidget();
widget.init({
  external_id: "52",
  company_name: "Ricambi Stella S.r.l.",
  name: "mario.rossi",
  phone: "+393331234567",
  email: "amministrazione@ricambi-stella.example",
});

W trybie osadzonym dodaj &mode=embedded&target=chat-wsparcia do URL i najpierw zamontuj kontener. Nie dubluj tego ładowania statycznym znacznikiem script.

loadWidget() zgłasza, że API jest dostępne. init() zwraca void: kolejkuje profil, gdy iframe nie jest gotowy, i nie czeka na sesję. Wynik sprawdzaj przez isReady() i hasSessionError().


3. Pola init()

Wszystkie wartości są łańcuchami. Pomiń nieznane. Nie wysyłaj null, liczbowych ID ani pustego e-maila. Gdy pole przekracza limity albo e-mail jest źle zbudowany, żądanie jest odrzucane.

Pole Limit i zachowanie
external_id Najwyżej 100 znaków. Odniesienie firmy porównywane dokładnie z web_code, w organizacji widgetu. Spacje nie są usuwane
company_name Najwyżej 250 znaków po usunięciu spacji na początku i na końcu. Tworzy brakującą firmę tylko razem z external_id. Pusty łańcuch albo same spacje nie włączają tworzenia
name Najwyżej 250 znaków. Imię kontaktu; może je zaktualizować w bieżącej sesji
email Ważny adres. Przy publicznej inicjalizacji jest zapisywany tylko przy nowej firmie. Nie jest zapisywany przy kontakcie ani przy firmie już istniejącej
phone Najwyżej 80 znaków. Numer kontaktu; wolisz format międzynarodowy, na przykład +393331234567. Konflikt z istniejącym numerem nie scala kontaktów ani czatów
external_user_id Opcjonalne w przypadku podstawowym. Stabilne ID osoby, niepuste, najwyżej 100 znaków po trim. Samo nie przywraca historii
restore_id Prywatne poświadczenie, najwyżej 200 znaków, uzyskane z backendu. Wymaga odpowiadającego external_user_id. Użyj zwróconej wartości bez zmian

Przykład, gdy strona zna konto. Nazwy pól w ERP mogą się różnić: odwzoruj odniesienie firmy na external_id, nazwę firmy na company_name, osobę na name, a e-mail konta na email.

widget.init({
  external_id: "52",
  company_name: "Ricambi Stella S.r.l.",
  name: "mario.rossi",
  phone: "+393331234567",
  email: "amministrazione@ricambi-stella.example",
});

Etykiety Imię i Telefon z formularza wysyła się jako name i phone. E-mail to email. Nie wysyłaj Imię, Telefon ani Email.

widget.init({
  name: "Mario Rossi",
  phone: "+393331234567",
});
widget.init({
  email: "mario.rossi@ricambi-stella.example",
});

Pierwsze wywołanie otwiera czat z imieniem i telefonem i nie szuka firmy. Drugie wystarcza z poprawnym e-mailem. W pełnym profilu powyżej phone to numer kontaktu, a email trafia do firmy tylko wtedy, gdy to wywołanie ją tworzy.

Trzymaj name i company_name osobno. Nie doklejaj nazwy firmy do nazwy użytkownika, żeby uzyskać powiązanie. Nie wysyłaj pola web_code: publiczna nazwa to external_id. Nie używaj wewnętrznego UUID Quoting24 ani companies.code zamiast wartości w web_code.

init(profile) zastępuje profil zapisany przez loader. Nie scala: przekaż pola, które mają zostać.

Nowy gość może wystartować z przynajmniej imieniem, telefonem, ważnym e-mailem albo external_user_id, albo z firmą znalezioną albo utworzoną przez external_id. init({}) i samo company_name nie zaczynają nowej sesji. Gdy wyślesz tylko nieznane external_id, bez company_name, widget prosi o dane przez formularz.

init({ restoreId: "..." }) nie przekazuje poświadczenia. Pole to restore_id.


4. Powiązanie albo utworzenie firmy

Przebieg startuje, gdy Quoting24 przetwarza inicjalizację, nawet bez wiadomości:

  1. Gdy dostaje niepuste external_id, szuka firmy tej samej organizacji, której web_code jest równy tej wartości
  2. Gdy ją znajdzie, wiąże kontakt. Nazwa i e-mail istniejącej firmy zostają
  3. Gdy jej nie znajdzie i dostaje też niepuste company_name, tworzy firmę z name = company_name, web_code = external_id i e-mailem, jeśli jest, a potem wiąże kontakt
  4. Gdy nie może znaleźć ani utworzyć firmy z braku danych, idzie dalej z dostępnym profilem. Gdy to nie wystarcza nowemu gościowi, prosi o formularz. Błędy bazy albo limit żądań kończą inicjalizację niepowodzeniem
Wysłane dane Sytuacja w Quoting24 Wynik
external_id: "52", company_name: "Ricambi Stella S.r.l." Istnieje web_code = "52" Używa istniejącej firmy; nie zmienia jej nazwy
Te same dane plus ważny e-mail Nie istnieje web_code = "52" Tworzy firmę z kodem web 52, nazwą i e-mailem oraz wiąże kontakt
external_id: "52", name: "mario.rossi" Brak dopasowania Uruchamia gościa bez nowego powiązania firmy
external_id: "52", bez innych danych Jest dopasowanie Uruchamia powiązanego gościa, z domyślnym imieniem Visitatore
external_id: "52", bez innych danych Brak dopasowania Prosi o dane odwiedzającego
company_name bez external_id, plus name Dowolna Uruchamia odwiedzającego. Nie szuka i nie tworzy firmy

Wyszukiwanie nie używa nazwy firmy, e-maila, telefonu, NIP ani companies.code. "052" i "52" to różne wartości. Jeśli najpierw importujesz kartotekę, wypełnij web_code tą samą wartością, którą strona wyśle w external_id.

Powiązanie dotyczy kontaktu. Dla już powiązanego kontaktu nowy ważny kod może zmienić firmę. Pominięcie kodu albo wysłanie nierozwiązanego nie kasuje poprzedniego powiązania. Przy zmianie osoby albo konta wykonaj reset(): nie ograniczaj się do zmiany profilu.

Limity

web_code nie ma ograniczenia unikalności. Przy kilku dopasowaniach wybierane jest pierwsze, według rosnącej daty utworzenia. Unikaj duplikatów: równoczesne żądania, które jeszcze nie znajdują firmy, mogą utworzyć więcej niż jedną. Przebieg nie gwarantuje atomowego utworzenia i nie zastępuje importu ani synchronizacji z ERP.

Tworzenie niejawne jest ograniczone do 20 prób na okno 60 sekund i adres IP, w procesie backendu obsługującym żądanie. Po przekroczeniu inicjalizacja kończy się TOO_MANY_REQUESTS. Ponów z oczekiwaniem. Unikaj ciągłych pętli init().

Dane przychodzą z przeglądarki i odwiedzający może je zmienić. Powiązanie nie dowodzi przynależności ani tożsamości i nie daje historii firmy. Wysyłaj company_name, gdy chcesz dopuścić to publiczne tworzenie. Istniejące firmy aktualizuj przez Quoting24 albo autoryzowaną integrację ERP.


5. Logowanie, wylogowanie i zmiana konta

Trzymaj jedną instancję w głównej strukturze SPA. Zmiana strony nie wymaga nowego skryptu ani resetu.

Przy zmianie osoby albo konta i przy wylogowaniu wywołaj reset() przed nowym profilem. Przy przeładowaniu z nadal aktywnym logowaniem użyj init() bez resetu, jak przy starcie: tak zostaje zapisana sesja gościa. Strona musi odróżnić przeładowanie od rzeczywistej zmiany.

Wywołanie przy zmianach uwierzytelnienia. profile to profil nowego użytkownika albo null przy wylogowaniu.

import { loadWidget } from "/assets/quoting24-loader.js";

let identityRevision = 0;

export async function applyWidgetIdentity(profile) {
  const revision = ++identityRevision;
  const snapshot = profile ? { ...profile } : null;

  window.Quoting24Widget?.reset();
  if (!snapshot) return;

  const widget = await loadWidget();
  if (revision !== identityRevision) return;
  widget.reset();
  widget.init(snapshot);
}

Obsłuż błąd ładowania w interfejsie strony, z ponowieniem. Jeśli profil pobierasz z backendu przed tą funkcją, unieważnij też te żądania przy zmianie użytkownika. W trakcie oczekiwania od razu wyczyść poprzedni profil przez applyWidgetIdentity(null).

Reset zapomina sesję gościa tej instancji, zapisane poświadczenie, profil i licznik nieprzeczytanych. To nie unieważnienie po stronie serwera i nie kończy sesji na innych urządzeniach. Zmiana uwierzytelnienia musi być zastosowana też w pozostałych otwartych kartach.

Z przywróceniem historii

Gdy profil zawiera restore_id, pobierz je z uwierzytelnionego endpointu strony po reset() i odrzuć odpowiedź, jeśli użytkownik się w międzyczasie zmienił. Przeładowanie z tym samym użytkownikiem używa już zapisanego poświadczenia i nie może wołać reset().

import { loadWidget } from "/assets/quoting24-loader.js";

let authRevision = 0;
let pendingRecovery;

// Tylko przy logowaniu, wylogowaniu i zmianie użytkownika.
// Nie wołaj przy przeładowaniu z tym samym użytkownikiem: reset() porzuciłby trwającą sesję.
export async function applyWidgetAuth(user) {
  const revision = ++authRevision;
  const expectedUserId = user ? String(user.id) : null;
  pendingRecovery?.abort();
  const controller = new AbortController();
  pendingRecovery = controller;

  window.Quoting24Widget?.reset();

  try {
    const widget = await loadWidget();
    if (revision !== authRevision) return;
    widget.reset();
    if (expectedUserId === null) return;

    const response = await fetch("/api/widget/session", {
      method: "POST",
      credentials: "same-origin",
      cache: "no-store",
      signal: controller.signal,
      headers: { "Content-Type": "application/json" },
      body: "{}",
    });
    if (!response.ok) throw new Error("Przywrócenie widgetu jest niedostępne");
    const profile = await response.json();
    if (revision !== authRevision) return;
    if (profile.external_user_id !== expectedUserId || !profile.restore_id) {
      throw new Error("Nieoczekiwana odpowiedź przywrócenia");
    }

    const payload = {
      external_user_id: profile.external_user_id,
      restore_id: profile.restore_id,
    };
    for (const key of ["external_id", "company_name", "name", "email", "phone"]) {
      if (profile[key]) payload[key] = profile[key];
    }
    widget.init(payload);
  } catch (error) {
    if (controller.signal.aborted || revision !== authRevision) return;
    console.warn("Czat wsparcia: przywrócenie nie powiodło się.", error);
  }
}

Pętla kopiuje tylko pola obecne i niepuste. E-mail "" albo liczbowe ID nie mogą być wysłane.

API JavaScript

Metoda Użycie
init(profile) Podaje albo zastępuje bieżący profil. Loader nie scala pól z poprzednim profilem
open() / close() / toggle() Otwiera, zamyka albo przełącza pływający widget. Zamknięcie nie jest wylogowaniem. W trybie osadzonym nie działają
isReady() Inicjalizacja sesji odwiedzającego się udała. Na formularzu kontaktu zostaje fałszem
hasSessionError() Błąd sesji zgłoszony przez iframe
reset() Czyści profil, sesję lokalną, zapisane poświadczenie gościa i licznik nieprzeczytanych
dispose() Demontuje interfejs i nasłuchiwanie. Nie czyści zapisanego poświadczenia gościa ani globalnego obiektu API

Możesz podłączyć przycisk strony do window.Quoting24Widget?.open(), gdy pływający przycisk się pojawi. Nie czekaj na isReady(), żeby nowy gość otworzył formularz. Nie ma publicznego zdarzenia gotowości. Te wskaźniki nie opisują całego stanu sieci.

Rozwiązane loadWidget() nie znaczy, że branding i sesja są gotowe.

Pełny demontaż

Gdy musisz usunąć widget, wykonaj dispose(). Gdy demontaż zbiega się z wylogowaniem, najpierw wykonaj reset(). Zanim załadujesz skrypt ponownie, usuń global i wyzeruj Promise loadera.

export function removeWidget() {
  window.Quoting24Widget?.reset();
  window.Quoting24Widget?.dispose();
  delete window.Quoting24Widget;
  document.getElementById("quoting24-widget-script")?.remove();
  widgetPromise = undefined;
}

widgetPromise to ta zdefiniowana w module loadera. Demontaż wykonaj po zakończeniu ładowania. Nie rób tego równolegle z nowym loadWidget(). Przy zmianach uwierzytelnienia użyj applyWidgetIdentity() albo applyWidgetAuth(), które czekają na loader.

Lokalny reset nie unieważnia restore_id na serwerze.


6. Uwierzytelnione przywrócenie

Sesja gościa czyta swoją najnowszą rozmowę także po zamknięciu. Rozmowy widoczne tej samej osoby w innej przeglądarce wymagają razem external_user_id i restore_id.

widget.init({
  external_id: "52",
  company_name: "Ricambi Stella S.r.l.",
  name: "Mario Rossi",
  phone: "+393331234567",
  email: "mario.rossi@ricambi-stella.example",
  external_user_id: "1042",
  restore_id: session.restore_id,
});

Przywrócenie nie scala starych sesji gościa i nie udostępnia historii współpracownikom tej samej firmy.

Endpoint w backendzie strony

Przykład do wdrożenia w portalu, nie URL Quoting24.

POST /api/widget/session
Content-Type: application/json

{}
{
  "external_user_id": "1042",
  "external_id": "52",
  "company_name": "Ricambi Stella S.r.l.",
  "name": "Mario Rossi",
  "phone": "+393331234567",
  "email": "mario.rossi@ricambi-stella.example",
  "restore_id": "<poswiadczenie-zwrocone-przez-quoting24>"
}

Ustaw Cache-Control: no-store. Tożsamość i firma tylko z sesji serwera. Zastosuj auth i CSRF aplikacji. Nie loguj treści zawierających poświadczenie.

Jeśli portal już udostępnia uwierzytelniony endpoint, który zwraca restore_id bieżącego użytkownika dla tego widgetu, użyj tej odpowiedzi i połącz ją z ID osoby oraz odniesieniem firmy.

Provisioning serwer–serwer

Typowa ścieżka: /fastify/trpc/widget.provisionRestoreId
(albo /trpc/widget.provisionRestoreId, gdy backend jest wystawiony bezpośrednio).

POST /fastify/trpc/widget.provisionRestoreId
Authorization: Bearer <SEKRET_INTEGRACJI>
Content-Type: application/json

{
  "widget_api_key": "<publiczny-uuid-widgetu>",
  "external_user_id": "1042",
  "external_id": "52",
  "name": "Mario Rossi",
  "phone": "+393331234567",
  "email": "mario.rossi@ricambi-stella.example",
  "attempt_id": "1042"
}
{
  "result": {
    "data": {
      "restore_id": "<nieprzejrzyste-poswiadczenie>"
    }
  }
}

Wygenerowane wartości zaczynają się od q24-restore-v1:. Nie wymyślaj ich i nie wyprowadzaj z publicznych ID.

Wejście Wymaganie
widget_api_key Obowiązkowy UUID, dopuszczony dla sekretu
external_user_id Stabilne ID osoby, równe temu w init()
attempt_id Ściśle rosnąca dodatnia liczba całkowita, jako łańcuch dziesiętny, najwyżej 9223372036854775807
saved_restore_id Już zapisane poświadczenie. Pomiń je przy pierwszym provisioningu
external_id, name, email, phone Opcjonalne, z tymi samymi limitami co init()

Wzorzec ensure, pobierz albo utwórz:

  1. Uwierzytelnij użytkownika i weź blokadę na wiersz przywrócenia, w transakcji
  2. Przydziel nowe attempt_id z sekwencji po blokadzie (CACHE 1, niecykliczna; sekwencja przeżywa rollback)
  3. Wywołaj Quoting24 z attempt_id i, jeśli jest, saved_restore_id
  4. Zapisz result.data.restore_id i zrób commit
  5. Dopiero po commicie zwróć poświadczenie do przeglądarki

Ważne saved_restore_id trzeba użyć ponownie. Pominięcie go, gdy ważny klucz już istnieje, podmienia klucz i unieważnia przywrócone sesje. Nie generuj nowego klucza przy każdym wyświetleniu strony.

Gdy zdalny provisioning się uda, a lokalny zapis nie, zwróć błąd. Nie zwracaj niezapisanego klucza. Ogranicz timeouty.

company_name wysyła się w init(). Powyższe ciało provisioningu go nie zawiera: powiązanie i utworzenie firmy, opisane w sekcji 4, zachodzą, gdy przeglądarka inicjalizuje sesję. E-mail publicznej inicjalizacji jest zapisywany tylko przy nowej firmie.

Wynik po stronie widgetu

Quoting24 sprawdza poświadczenie i ładuje widoczną historię widgetu tego użytkownika. Zamknięte czaty mogą pojawić się jako poprzednia rozmowa: ich odczyt nie otwiera starej rozmowy na nowo; wysyłka zostaje w bieżącej.

Poświadczenie nieważne, należące do innego użytkownika albo unieważnione jest odrzucane, bez automatycznej zamiany na sesję gościa. Przycisk «Riprova» widgetu sam nie prosi o nowe restore_id. Bieżący interfejs jest po włosku.


7. Strona, sieć i wygląd

  • Jedna instancja na stronę
  • CSP. Zezwól na pochodzenie widgetu w script-src i frame-src oraz na pochodzenie API wskazane dla instalacji w connect-src (ładowanie ustawień graficznych). Zezwól na skonfigurowane obrazy w img-src. Loader wstawia style inline i nie udostępnia publicznej opcji nonce: sprawdź CSP strony. CSS strony nie zmienia wnętrza iframe z innego pochodzenia
  • Połączenia iframe. Musi dosięgnąć API, Realtime/WebSocket i magazynu własnej instalacji. Do filtrów sieci użyj rzeczywistych hostów środowiska
  • Wygląd i język. Logo, kolor, pozycję i komunikaty konfiguruje się w Quoting24. company_name to nazwa firmy klienta. Nie ma publicznego parametru language ani locale; bieżący interfejs jest po włosku
  • Telefon. Pływający czat może zająć viewport. W trybie osadzonym strona steruje wysokością i szerokością
  • Załączniki i godziny. PDF, DOC/DOCX, JPG/JPEG i PNG. Do 10 MiB na plik i 100 MiB załączników wskazanych w rozmowie. Poza godzinami widget pokazuje baner i zostawia pisanie dostępne

8. Sprawdzenie integracji

Użyj konta testowego i kanału testowego. Sprawdź kartotekę w Quoting24, nie tylko czat.

Próba Oczekiwany wynik
Nowa przeglądarka, tylko skrypt Formularz kontaktu, potem rozmowa
Pełny payload z external_id już obecnym jako web_code Kontakt powiązany z istniejącą firmą; nazwa i e-mail firmy bez zmian
Pełny payload z nowym kodem Firma utworzona z kodem web, nazwą i e-mailem; kontakt powiązany
Nowy kod i imię, bez company_name Czat uruchomiony, żadna firma nie utworzona
Tylko nowy kod Formularz kontaktu
Tylko nazwa firmy i imię Żadna firma nie szukana ani nie utworzona po nazwie
Pusty e-mail, liczbowe ID albo za długa nazwa firmy Payload odrzucony
Drugie init() w tej samej sesji Sesja gościa trwa. E-mail i telefon nie odzyskują czatu innej osoby
Przeładowanie, nawigacja SPA, telefon Jedna instancja; ciągłość gościa, gdy jest pamięć; użyteczny układ
Wylogowanie albo zmiana konta w trakcie ładowania Poprzednia sesja wyczyszczona; spóźnione wyniki nie przywracają starego profilu
Ten sam uwierzytelniony użytkownik, inna przeglądarka, ważne restore_id Historia tej osoby
Dwóch użytkowników tej samej firmy Osobne poświadczenia i historie
Gość, potem przywrócenie reset() pośrodku; sesje się nie scalają
Nieważne albo unieważnione poświadczenie Błąd, bez automatycznego powrotu do gościa
Poprzednie rozmowy Tylko odczyt; wysyłka zostaje w bieżącej
Niedozwolona domena Dostęp odrzucony
Sieć przerwana Ponowne połączenie; szkic zostaje, gdy wysyłka się nie uda

Rozwiązywanie problemów

Objaw Sprawdzenie
Przycisk się nie pojawia URL, klucz, kanał włączony, domeny, CSP, żądanie widget.getBranding. Sam global JavaScript nie dowodzi, że przycisk istnieje. open() nie działa, dopóki branding się nie załaduje, i nie działa w trybie osadzonym
Quoting24Widget jest niezdefiniowane Poczekaj na załadowanie. Sprawdź parametr key
Osadzony czat jest niewidoczny Kontener przed loaderem, właściwe ID, wysokość większa od zera
Domain not allowed Nazwa hosta strony, bez schematu, portu i ścieżki. Sprawdź też testy. Strona musi być serwowana przez HTTP
Formularz telefonu mimo profilu Kolejność asynchroniczna, snake_case, puste wartości, poprzednie reset(), profil niewystarczający dla nowego gościa
Firmy nie znaleziono Porównaj external_id z web_code w tej samej organizacji, łącznie ze spacjami i zerami wiodącymi
Brakująca firma nie jest tworzona Potrzebne są niepuste external_id i company_name. Sprawdź walidację i limit tworzenia
Nazwa albo e-mail firmy się nie zmienia Zgodnie z kontraktem: init() tworzy brakujące firmy i nie aktualizuje istniejących
E-mail nie pojawia się przy kontakcie Zgodnie z kontraktem publicznej inicjalizacji: e-mail jest używany tylko przy tworzeniu firmy
TOO_MANY_REQUESTS Poczekaj i ponów. Unikaj powtarzanych wywołań albo masowego importu przez widget
Brak historii na innym urządzeniu Payload firmy jej nie przywraca. Potrzebne są external_user_id i restore_id
Przywrócone sesje unieważnione Wywołania ensure muszą odesłać saved_restore_id. Nie wystawiaj nowego klucza przy każdym wyświetleniu strony
«Riprova» nie wystarcza Nowe restore_id z Twojego endpointu, potem reset() i init()
Po zmianie konta zostaje stary czat reset() natychmiast przy zmianie tożsamości. Odrzuć poprzednie odpowiedzi asynchroniczne
Po dispose() skrypt nie odtwarza widgetu Usuń global i skrypt, wyzeruj Promise loadera, potem załaduj ponownie
Interfejs zostaje po włosku Brak publicznej opcji language albo locale w loaderze

Odniesienia implementacji

  • Loader i API JavaScript: @frontend/src/public/widget/script.ts
  • Inicjalizacja, powiązanie firmy i tworzenie niejawne: @backend/fastify/routes/widget/initialize.ts
  • Testy inicjalizacji i firm: @backend/fastify/__tests__/widget/widgetInitialize.test.ts
  • Domena i kanał: @backend/fastify/routes/widget/procedures/publicWidgetProcedure.ts
  • Provisioning restore_id: @backend/fastify/routes/widget/provisionRestoreId.ts