Widget — Technische Integration
Entwicklerhandbuch: Skript, init()-Payload, Unternehmenszuordnung und -anlage, Lebenszyklus, authentifizierte Wiederherstellung (restore_id), CSP und Prüfung.
Wegweiser: Übersicht · B2C · B2B.
Referenzausgabe: 26. Oktober 2026. Die Wiederherstellung über Geräte hinweg kommt zum Unternehmens-Payload hinzu. Vor dem Aktivieren prüfen, ob provisionRestoreId in der Installation verfügbar ist.
Die Oberfläche läuft in einem iframe. Die einbindende Website braucht weder Angular noch einen Supabase-Client noch Zugriff auf die Quoting24-Datenbank.
Integrationstyp wählen
| Fall | Implementierung | Kontinuität |
|---|---|---|
| Öffentliche Website, unbekannter Besucher | Nur Skript. Kontaktformular | Gastsitzung im selben Browser |
| Website mit Kundendaten | Skript und init() mit Profil und Unternehmen |
Vorausgefüllte Gastsitzung und Unternehmenszuordnung |
| Portal mit persönlichem Verlauf über Geräte hinweg | Zusätzlich ein Backend für external_user_id und restore_id |
Persönlicher Verlauf auf dem freigegebenen Widget-Kanal |
Unternehmen zuordnen und den Verlauf wiederherstellen sind getrennte Vorgänge. external_id, company_name, Name, E-Mail und Telefon geben die Unterhaltungen einer anderen Person nicht frei. Ein auf der Website angemeldeter Benutzer bleibt ein Widget-Gast, bis ein gültiger Wiederherstellungsnachweis übergeben wird.
1. Konfiguration aus Quoting24
Unter Einstellungen → Kanäle den Widget-Kanal öffnen und holen:
- vollständige Skript-URL und öffentlichen Schlüssel
- erlaubte Domains, einschließlich Test und lokaler Entwicklung
- Logo, Farbe, Position, Begrüßung, Willkommenstext und Zeiten
URL und Schlüssel derselben Umgebung verwenden. In den Beispielen WIDGET_HOST und WIDGET_SCHLUESSEL ersetzen. Der Schlüssel in der URL ist öffentlich und wählt den Kanal: er ist kein persönlicher Nachweis und kein Backend-Integrationsgeheimnis.
Domains sind Hostnamen (shop.example.com, localhost), ohne https://, Port, Pfad oder Präfix *.. example.com erlaubt auch seine Subdomains. Eine leere Liste wendet keine Beschränkung an. Für den lokalen Test die Seite über HTTP ausliefern, nicht als Datei.
| Wert | Wo speichern | Zweck |
|---|---|---|
| Öffentlicher Schlüssel | Skript-URL | Wählt den Widget-Kanal |
| Privates Integrationsgeheimnis | Nur Backend-Secrets | Erlaubt Anlegen und Wiederverwenden von restore_id |
restore_id |
Privater Speicher pro Benutzer; auf der Seite nur bei authentifizierter Sitzung | Zeigt, welcher Verlauf wiederherzustellen ist |
| Gastnachweis der Sitzung | localStorage der Website, pro Widget-Schlüssel, vom Loader verwaltet |
Gastkontinuität im selben Browser |
Die Wiederherstellung braucht eine Backend-Integration, einen privaten Endpunkt und ein Geheimnis, getrennt vom öffentlichen Code. Siehe Abschnitt 6.
2. Installation
Schwebende Schaltfläche
Einmal, vor </body>:
Die Schaltfläche erscheint nach dem Laden der grafischen Einstellungen. Ein neuer Besucher öffnet das Formular mit Telefon und optionalem Namen. Der Loader speichert den Gastnachweis in localStorage, getrennt nach Schlüssel. Ist der Speicher blockiert, kann die Kontinuität auf die aktuelle Seite beschränkt sein.
Der Loader stellt ein einziges window.Quoting24Widget bereit. Ein zweites Skript erzeugt kein zweites Widget.
Eingebetteter Chat
Container vor dem Skript, explizite Höhe:
<section aria-labelledby="titel-support">
<h2 id="titel-support">Support</h2>
<div id="chat-support" style="width: 100%; height: 600px;"></div>
</section>
<script
src="https://WIDGET_HOST/assets/widget/script.js?key=WIDGET_SCHLUESSEL&mode=embedded&target=chat-support"
async
></script>
target ist die ID, ohne #. Das iframe füllt den Container. Es gibt keine schwebende Schaltfläche. open(), close() und toggle() haben keine Wirkung.
Auf den Loader warten
Für vorausgefüllte Daten dieses Modul anstelle des statischen Tags verwenden, einmal im Hauptlayout importiert. Die Beispiele sind JavaScript-Module: Bundler oder <script type="module" src="/assets/support.js"></script>.
// /assets/quoting24-loader.js
const widgetScriptUrl =
"https://WIDGET_HOST/assets/widget/script.js?key=WIDGET_SCHLUESSEL";
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("Widget-API ist nicht verfügbar"));
}
};
script.onerror = () => {
script.remove();
reject(new Error("Widget-Skript konnte nicht geladen werden"));
};
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",
});
Für den eingebetteten Modus &mode=embedded&target=chat-support an die URL hängen und den Container zuerst einbinden. Dieses Laden nicht mit einem statischen Script-Tag verdoppeln.
loadWidget() meldet, dass die API verfügbar ist. init() gibt void zurück: es reiht das Profil ein, wenn das iframe noch nicht bereit ist, und wartet nicht auf die Sitzung. Für das Ergebnis isReady() und hasSessionError() verwenden.
3. Felder von init()
Alle Werte sind Zeichenketten. Unbekannte weglassen. Kein null, keine numerischen IDs, keine leere E-Mail senden. Überschreitet ein Feld die Grenzen oder ist die E-Mail ungültig, wird die Anfrage abgelehnt.
| Feld | Grenze und Verhalten |
|---|---|
external_id |
Höchstens 100 Zeichen. Unternehmensreferenz, exakt mit web_code verglichen, in der Organisation des Widgets. Leerzeichen werden nicht entfernt |
company_name |
Höchstens 250 Zeichen nach dem Entfernen führender und nachgestellter Leerzeichen. Legt ein fehlendes Unternehmen nur zusammen mit external_id an. Eine leere oder nur aus Leerzeichen bestehende Zeichenkette schaltet die Anlage nicht frei |
name |
Höchstens 250 Zeichen. Name des Kontakts; kann ihn in der aktuellen Sitzung aktualisieren |
email |
Gültige Adresse. Bei der öffentlichen Initialisierung wird sie nur auf einem neuen Unternehmen gespeichert. Sie wird weder am Kontakt noch an einem vorhandenen Unternehmen geschrieben |
phone |
Höchstens 80 Zeichen. Nummer des Kontakts; internationales Format bevorzugen, zum Beispiel +393331234567. Ein Konflikt mit einer vorhandenen Nummer führt Kontakte oder Chats nicht zusammen |
external_user_id |
Im Basisfall optional. Stabile Personen-ID, nicht leer, höchstens 100 Zeichen nach dem Trim. Allein stellt sie den Verlauf nicht wieder her |
restore_id |
Privater Nachweis, höchstens 200 Zeichen, vom Backend geholt. Verlangt die passende external_user_id. Den zurückgegebenen Wert unverändert verwenden |
Beispiel, wenn die Website das Konto kennt. Feldnamen im ERP können abweichen: Unternehmensreferenz auf external_id, Firmenname auf company_name, Person auf name, Konten-E-Mail auf email abbilden.
widget.init({
external_id: "52",
company_name: "Ricambi Stella S.r.l.",
name: "mario.rossi",
phone: "+393331234567",
email: "amministrazione@ricambi-stella.example",
});
Die Formularbeschriftungen Name und Telefon werden als name und phone gesendet. Die E-Mail heißt email. Nicht Name, Telefon oder Email senden.
Der erste Aufruf öffnet den Chat mit Name und Telefon und sucht kein Unternehmen. Der zweite genügt mit einer gültigen E-Mail. Im vollständigen Profil darüber ist phone die Nummer des Kontakts und email wird am Unternehmen nur geschrieben, wenn dieser Aufruf es anlegt.
name und company_name getrennt halten. Den Firmennamen nicht an den Benutzernamen hängen, um die Zuordnung zu erhalten. Kein Feld web_code senden: der öffentliche Name ist external_id. Nicht die interne UUID von Quoting24 und nicht companies.code anstelle des Werts in web_code verwenden.
init(profile) ersetzt das vom Loader gespeicherte Profil. Es führt nicht zusammen: die Felder übergeben, die bleiben sollen.
Ein neuer Gast kann mit mindestens einem Namen, einem Telefon, einer gültigen E-Mail oder einer external_user_id starten, oder mit einem über external_id gefundenen oder angelegten Unternehmen. init({}) und company_name allein starten keine neue Sitzung. Wird nur eine unbekannte external_id ohne company_name gesendet, fordert das Widget die Daten über das Formular an.
init({ restoreId: "..." }) überträgt den Nachweis nicht. Das Feld heißt restore_id.
4. Unternehmen zuordnen oder anlegen
Der Ablauf startet, wenn Quoting24 die Initialisierung verarbeitet, auch ohne Nachricht:
- Empfängt es eine nicht leere
external_id, sucht es ein Unternehmen derselben Organisation, dessenweb_codediesem Wert entspricht - Findet es eines, verknüpft es den Kontakt. Name und E-Mail des vorhandenen Unternehmens bleiben unverändert
- Findet es keines und empfängt es auch ein nicht leeres
company_name, legt es das Unternehmen mitname = company_name,web_code = external_idund gegebenenfallsemailan und verknüpft dann den Kontakt - Kann es das Unternehmen wegen fehlender Daten nicht finden oder anlegen, fährt es mit dem verfügbaren Profil fort. Reicht das für einen neuen Gast nicht, fordert es das Formular an. Datenbankfehler oder das Anfragelimit lassen die Initialisierung fehlschlagen
| Gesendete Daten | Lage in Quoting24 | Ergebnis |
|---|---|---|
external_id: "52", company_name: "Ricambi Stella S.r.l." |
web_code = "52" existiert |
Verwendet das vorhandene Unternehmen; ändert seinen Namen nicht |
| Dieselben Daten plus eine gültige E-Mail | web_code = "52" existiert nicht |
Legt das Unternehmen mit Web-Code 52, Firmenname und E-Mail an und ordnet den Kontakt zu |
external_id: "52", name: "mario.rossi" |
Keine Übereinstimmung | Startet den Gast ohne neue Unternehmenszuordnung |
external_id: "52", ohne weitere Daten |
Eine Übereinstimmung existiert | Startet den zugeordneten Gast mit dem Standardnamen Visitatore |
external_id: "52", ohne weitere Daten |
Keine Übereinstimmung | Fordert die Daten des Besuchers an |
company_name ohne external_id, plus name |
Beliebig | Startet den Besucher. Sucht und legt kein Unternehmen an |
Die Suche verwendet weder Firmenname, E-Mail, Telefon, Umsatzsteuer-ID noch companies.code. "052" und "52" sind verschiedene Werte. Wird der Bestand vorher importiert, web_code mit demselben Wert füllen, den die Website in external_id senden wird.
Die Zuordnung gilt für den Kontakt. Bei einem bereits verknüpften Kontakt kann ein neuer gültiger Code das Unternehmen wechseln. Den Code wegzulassen oder einen nicht aufgelösten zu senden löscht die vorherige Zuordnung nicht. Bei Personen- oder Kontowechsel reset() ausführen: nicht nur das Profil ändern.
Grenzen
web_code hat keine Eindeutigkeitsbeschränkung. Bei mehreren Treffern wird der erste gewählt, geordnet nach aufsteigendem Erstellungsdatum. Dubletten vermeiden: gleichzeitige Anfragen, die das Unternehmen noch nicht finden, können mehrere anlegen. Der Ablauf garantiert keine atomare Anlage und ersetzt keinen Import und keine Synchronisation aus dem ERP.
Die implizite Anlage ist auf 20 Versuche pro 60-Sekunden-Fenster und IP-Adresse begrenzt, im Backend-Prozess, der die Anfrage bearbeitet. Darüber schlägt die Initialisierung mit TOO_MANY_REQUESTS fehl. Mit Wartezeit wiederholen. Fortlaufende init()-Schleifen vermeiden.
Die Daten kommen aus dem Browser und der Besucher kann sie ändern. Die Zuordnung beweist keine Zugehörigkeit und keine Identität und gibt keinen Unternehmensverlauf frei. company_name senden, wenn diese öffentliche Anlage erlaubt sein soll. Vorhandene Unternehmen über Quoting24 oder eine autorisierte ERP-Integration aktualisieren.
5. Anmeldung, Abmeldung und Kontowechsel
Eine einzige Instanz in der Hauptstruktur einer SPA halten. Ein Seitenwechsel braucht weder ein neues Skript noch ein Reset.
Bei Personen- oder Kontowechsel und beim Abmelden reset() vor dem neuen Profil aufrufen. Beim Neuladen mit noch aktiver Anmeldung init() ohne Reset verwenden, wie beim Start: so bleibt die gespeicherte Gastsitzung erhalten. Die Website muss das Neuladen vom tatsächlichen Wechsel unterscheiden.
Aufruf bei Authentifizierungswechseln. profile ist das Profil des neuen Benutzers oder null beim Abmelden.
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);
}
Den Ladefehler in der Oberfläche der Website behandeln, mit einem erneuten Versuch. Wird das Profil vor dieser Funktion vom Backend geholt, diese Anfragen beim Benutzerwechsel ebenfalls ungültig machen. Während des Wartens das vorherige Profil sofort mit applyWidgetIdentity(null) löschen.
Reset vergisst die Gastsitzung dieser Instanz, den gespeicherten Nachweis, das Profil und den Zähler der Ungelesenen. Es ist kein Server-Widerruf und beendet keine Sitzungen auf anderen Geräten. Der Authentifizierungswechsel muss auch in den anderen offenen Tabs angewendet werden.
Mit Verlaufswiederherstellung
Enthält das Profil restore_id, sie nach dem reset() vom authentifizierten Endpunkt der Website laden und die Antwort verwerfen, wenn der Benutzer sich inzwischen geändert hat. Ein Neuladen mit demselben Benutzer verwendet den schon gespeicherten Nachweis erneut und darf reset() nicht aufrufen.
import { loadWidget } from "/assets/quoting24-loader.js";
let authRevision = 0;
let pendingRecovery;
// Nur bei Anmeldung, Abmeldung und Benutzerwechsel.
// Nicht beim Neuladen mit demselben Benutzer aufrufen: reset() würde die laufende Sitzung verwerfen.
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("Widget-Wiederherstellung nicht verfügbar");
const profile = await response.json();
if (revision !== authRevision) return;
if (profile.external_user_id !== expectedUserId || !profile.restore_id) {
throw new Error("Unerwartete Wiederherstellungsantwort");
}
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("Support-Chat: Wiederherstellung fehlgeschlagen.", error);
}
}
Die Schleife kopiert nur vorhandene und nicht leere Felder. Eine E-Mail "" oder eine numerische ID darf nicht gesendet werden.
JavaScript-API
| Methode | Verwendung |
|---|---|
init(profile) |
Übergibt oder ersetzt das aktuelle Profil. Der Loader führt Felder nicht mit einem vorherigen Profil zusammen |
open() / close() / toggle() |
Öffnet, schließt oder schaltet das schwebende Widget um. Schließen ist kein Abmelden. Im eingebetteten Modus ohne Wirkung |
isReady() |
Initialisierung der Besuchersitzung erfolgreich. Bleibt während des Kontaktformulars falsch |
hasSessionError() |
Vom iframe gemeldeter Sitzungsfehler |
reset() |
Löscht Profil, lokale Sitzung, gespeicherten Gastnachweis und Zähler der Ungelesenen |
dispose() |
Hängt Oberfläche und Listener ab. Löscht weder den gespeicherten Gastnachweis noch das globale API-Objekt |
Eine Schaltfläche der Website kann mit window.Quoting24Widget?.open() verbunden werden, wenn die schwebende Schaltfläche erschienen ist. Nicht auf isReady() warten, damit ein neuer Gast das Formular öffnen kann. Es gibt kein öffentliches Verfügbarkeitsereignis. Diese Anzeigen beschreiben nicht den gesamten Netzstatus.
Ein aufgelöstes loadWidget() bedeutet nicht, dass Branding und Sitzung bereit sind.
Vollständiger Abbau
Muss das Widget entfernt werden, dispose() ausführen. Fällt der Abbau mit dem Abmelden zusammen, zuerst reset() ausführen. Vor dem erneuten Laden des Skripts das Global löschen und die Promise des Loaders zurücksetzen.
export function removeWidget() {
window.Quoting24Widget?.reset();
window.Quoting24Widget?.dispose();
delete window.Quoting24Widget;
document.getElementById("quoting24-widget-script")?.remove();
widgetPromise = undefined;
}
widgetPromise ist die im Loader-Modul definierte. Den Abbau nach Abschluss des Ladens ausführen. Nicht gleichzeitig mit einem neuen loadWidget() ausführen. Für Authentifizierungswechsel applyWidgetIdentity() oder applyWidgetAuth() verwenden, die auf den Loader warten.
Das lokale Reset widerruft restore_id auf dem Server nicht.
6. Authentifizierte Wiederherstellung
Die Gastsitzung liest ihre letzte Unterhaltung auch nach dem Schließen. Die für dieselbe Person auf einem anderen Browser sichtbaren Unterhaltungen brauchen external_user_id und restore_id zusammen.
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,
});
Die Wiederherstellung führt alte Gastsitzungen nicht zusammen und teilt den Verlauf nicht unter Kollegen desselben Unternehmens.
Endpunkt im Backend der Website
Beispiel für das Portal, keine Quoting24-URL.
{
"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": "<von-quoting24-zurueckgegebener-nachweis>"
}
Cache-Control: no-store setzen. Identität und Unternehmen nur aus der Serversitzung. Auth und CSRF der Anwendung anwenden. Körper, die den Nachweis enthalten, nicht protokollieren.
Stellt das Portal schon einen authentifizierten Endpunkt bereit, der die restore_id des aktuellen Benutzers für dieses Widget zurückgibt, diese Antwort verwenden und mit Personen-ID und Unternehmensreferenz verbinden.
Server-zu-Server-Provisioning
Typischer Pfad: /fastify/trpc/widget.provisionRestoreId
(oder /trpc/widget.provisionRestoreId, wenn das Backend direkt exponiert ist).
POST /fastify/trpc/widget.provisionRestoreId
Authorization: Bearer <INTEGRATIONSGEHEIMNIS>
Content-Type: application/json
{
"widget_api_key": "<oeffentliche-widget-uuid>",
"external_user_id": "1042",
"external_id": "52",
"name": "Mario Rossi",
"phone": "+393331234567",
"email": "mario.rossi@ricambi-stella.example",
"attempt_id": "1042"
}
Erzeugte Werte beginnen mit q24-restore-v1:. Sie nicht erfinden und nicht aus öffentlichen IDs ableiten.
| Eingabe | Anforderung |
|---|---|
widget_api_key |
Pflicht-UUID, für das Geheimnis freigegeben |
external_user_id |
Stabile Personen-ID, gleich der in init() |
attempt_id |
Streng steigende positive Ganzzahl, als Dezimalzeichenkette, höchstens 9223372036854775807 |
saved_restore_id |
Schon gespeicherter Nachweis. Beim ersten Provisioning weglassen |
external_id, name, email, phone |
Optional, mit denselben Grenzen wie init() |
Muster ensure, holen oder anlegen:
- Den Benutzer authentifizieren und in einer Transaktion eine Sperre auf die Wiederherstellungszeile setzen
- Eine neue
attempt_idaus einer Sequenz nach der Sperre vergeben (CACHE 1, nicht zyklisch; die Sequenz überlebt das Rollback) - Quoting24 mit
attempt_idund, falls vorhanden,saved_restore_idaufrufen result.data.restore_idspeichern und committen- Den Nachweis erst nach dem Commit an den Browser zurückgeben
Eine gültige saved_restore_id muss wiederverwendet werden. Sie wegzulassen, wenn schon ein gültiger Schlüssel existiert, ersetzt den Schlüssel und macht wiederhergestellte Sitzungen ungültig. Nicht bei jedem Seitenaufruf einen neuen Schlüssel erzeugen.
Gelingt das entfernte Provisioning und schlägt das lokale Speichern fehl, einen Fehler zurückgeben. Den nicht gespeicherten Schlüssel nicht zurückgeben. Timeouts begrenzen.
company_name wird in init() gesendet. Der Provisioning-Körper oben enthält es nicht: Zuordnung und Anlage des Unternehmens, beschrieben in Abschnitt 4, geschehen, wenn der Browser die Sitzung initialisiert. Die E-Mail der öffentlichen Initialisierung wird nur auf einem neuen Unternehmen geschrieben.
Ergebnis im Widget
Quoting24 prüft den Nachweis und lädt den für diesen Benutzer sichtbaren Widget-Verlauf. Geschlossene Chats können als vorherige Unterhaltung erscheinen: sie zu lesen öffnet die alte Unterhaltung nicht wieder; das Senden bleibt in der aktuellen.
Ein ungültiger, einem anderen Benutzer gehörender oder widerrufener Nachweis wird abgelehnt, ohne automatische Umwandlung in eine Gastsitzung. Die Schaltfläche «Riprova» des Widgets fordert nicht von sich aus eine neue restore_id an. Die aktuelle Oberfläche ist Italienisch.
7. Seite, Netz und Erscheinungsbild
- Eine Instanz pro Seite
- CSP. Die Widget-Herkunft in
script-srcundframe-srcerlauben und die für die Installation angegebene API-Herkunft inconnect-src(Laden der grafischen Einstellungen). Konfigurierte Bilder inimg-srcerlauben. Der Loader fügt Inline-Stile ein und bietet keine öffentliche Nonce-Option: die CSP der Website prüfen. Das CSS der Seite ändert das Innere des Cross-Origin-iframe nicht - Verbindungen des iframe. Es muss API, Realtime/WebSocket und Speicher der eigenen Installation erreichen. Für Netzfilter die tatsächlichen Hosts der Umgebung verwenden
- Erscheinungsbild und Sprache. Logo, Farbe, Position und Texte werden in Quoting24 konfiguriert.
company_nameist der Firmenname des Kunden. Es gibt keinen öffentlichen Parameterlanguageoderlocale; die aktuelle Oberfläche ist Italienisch - Mobilgerät. Der schwebende Chat kann den Viewport füllen. Im eingebetteten Modus steuert die Website Höhe und Breite
- Anhänge und Zeiten. PDF, DOC/DOCX, JPG/JPEG und PNG. Bis 10 MiB pro Datei und 100 MiB referenzierte Anhänge pro Unterhaltung. Außerhalb der Zeiten zeigt das Widget das Banner und lässt das Verfassen verfügbar
8. Integration prüfen
Ein Testkonto und einen Testkanal verwenden. Den Bestand in Quoting24 prüfen, nicht nur den Chat.
| Prüfung | Erwartetes Ergebnis |
|---|---|
| Neuer Browser, nur Skript | Kontaktformular, dann Unterhaltung |
Vollständiges Payload mit external_id, die schon als web_code vorhanden ist |
Kontakt dem vorhandenen Unternehmen zugeordnet; Name und E-Mail des Unternehmens unverändert |
| Vollständiges Payload mit neuem Code | Unternehmen mit Web-Code, Firmenname und E-Mail angelegt; Kontakt zugeordnet |
Neuer Code und Name, ohne company_name |
Chat gestartet, kein Unternehmen angelegt |
| Nur neuer Code | Kontaktformular |
| Nur Firmenname und Name | Kein Unternehmen nach Name gesucht oder angelegt |
| Leere E-Mail, numerische ID oder zu langer Firmenname | Payload abgelehnt |
Zweites init() in derselben Sitzung |
Gastsitzung läuft weiter. E-Mail und Telefon holen nicht den Chat einer anderen Person |
| Neuladen, SPA-Navigation, Mobilgerät | Eine Instanz; Gastkontinuität, wenn Speicher da ist; nutzbares Layout |
| Abmeldung oder Kontowechsel während des Ladens | Vorherige Sitzung gelöscht; verspätete Ergebnisse stellen das alte Profil nicht wieder her |
Derselbe authentifizierte Benutzer, anderer Browser, gültige restore_id |
Verlauf dieser Person |
| Zwei Benutzer desselben Unternehmens | Getrennte Nachweise und Verläufe |
| Gast, dann Wiederherstellung | reset() dazwischen; Sitzungen werden nicht zusammengeführt |
| Ungültiger oder widerrufener Nachweis | Fehler, ohne automatischen Rückfall auf den Gast |
| Vorherige Unterhaltungen | Nur Lesen; das Senden bleibt in der aktuellen |
| Nicht erlaubte Domain | Zugriff abgelehnt |
| Netz unterbrochen | Wiederverbindung; Entwurf bleibt erhalten, wenn das Senden fehlschlägt |
Problemlösung
| Symptom | Prüfung |
|---|---|
| Die Schaltfläche erscheint nicht | URL, Schlüssel, Kanal aktiv, Domains, CSP, Anfrage widget.getBranding. Das JavaScript-Global allein beweist nicht, dass die Schaltfläche existiert. open() hat keine Wirkung, bis das Branding geladen ist, und keine Wirkung im eingebetteten Modus |
Quoting24Widget ist nicht definiert |
Auf das Laden warten. Den Parameter key prüfen |
| Der eingebettete Chat ist unsichtbar | Container vor dem Loader, richtige ID, Höhe größer als null |
Domain not allowed |
Hostname der Website, ohne Schema, Port und Pfad. Auch den Test prüfen. Die Seite muss über HTTP ausgeliefert werden |
| Telefonformular trotz Profil | Asynchrone Reihenfolge, snake_case, leere Werte, vorheriges reset(), Profil reicht für einen neuen Gast nicht |
| Das Unternehmen wird nicht gefunden | external_id mit web_code in derselben Organisation vergleichen, einschließlich Leerzeichen und führender Nullen |
| Das fehlende Unternehmen wird nicht angelegt | external_id und company_name müssen beide nicht leer sein. Validierung und Anlagelimit prüfen |
| Name oder E-Mail des Unternehmens ändern sich nicht | Erwartet: init() legt fehlende Unternehmen an und aktualisiert vorhandene nicht |
| Die E-Mail erscheint nicht am Kontakt | Erwartet bei der öffentlichen Initialisierung: die E-Mail wird nur beim Anlegen des Unternehmens verwendet |
TOO_MANY_REQUESTS |
Warten und wiederholen. Wiederholte Aufrufe oder Massenimporte über das Widget vermeiden |
| Der Verlauf fehlt auf einem anderen Gerät | Das Unternehmens-Payload stellt ihn nicht wieder her. external_user_id und restore_id sind erforderlich |
| Wiederhergestellte Sitzungen ungültig | Ensure-Aufrufe müssen saved_restore_id erneut senden. Nicht bei jedem Seitenaufruf einen neuen Schlüssel ausgeben |
| «Riprova» reicht nicht | Neue restore_id vom eigenen Endpunkt, dann reset() und init() |
| Nach einem Kontowechsel bleibt der alte Chat | reset() sofort beim Identitätswechsel. Vorherige asynchrone Antworten verwerfen |
Nach dispose() erzeugt das Skript das Widget nicht neu |
Global und Skript löschen, die Promise des Loaders zurücksetzen, dann neu laden |
| Die Oberfläche bleibt Italienisch | Keine öffentliche Option language oder locale am Loader |
Implementierungsreferenzen
- Loader und JavaScript-API:
@frontend/src/public/widget/script.ts - Initialisierung, Unternehmenszuordnung und implizite Anlage:
@backend/fastify/routes/widget/initialize.ts - Tests zu Initialisierung und Unternehmen:
@backend/fastify/__tests__/widget/widgetInitialize.test.ts - Domain und Kanal:
@backend/fastify/routes/widget/procedures/publicWidgetProcedure.ts - Provisioning von
restore_id:@backend/fastify/routes/widget/provisionRestoreId.ts