Aller au contenu

Guide d'installation

Ajouter le widget à votre site tient en une ligne de code. L'identité des visiteurs, l'intégration au site, les webhooks et l'API sont facultatifs ; suivez la section concernée quand vous en avez besoin.

<script src="{{KOK}}/cd/w.js?k=VOTRE_CLE_DE_SITE" async></script>

Votre clé de site se trouve dans la console, sous Paramètres → Installation. Sur WordPress, installez-le avec l'extension, sans écrire de code.

Démarrage rapide

  1. Dans la console, copiez votre clé de site (elle commence par cd_) depuis Paramètres → Installation.
  2. Ajoutez le code en haut de cette page sur toutes les pages de votre site, juste avant la balise </body>. Remplacez VOTRE_CLE_DE_SITE par votre clé.
  3. Rechargez la page : le bouton de chat apparaît dans le coin inférieur. Tant que vous êtes en ligne dans la console, les visiteurs peuvent vous écrire.

Le code ne ralentit pas votre page : le chargeur est léger et mis en cache, le widget se charge séparément et est versionné. Si notre serveur est injoignable, un simple bouton s'affiche vers l'adresse saisie dans Paramètres → Général → Lien de contact de secours.

WordPress

Pour installer sans code, téléchargez l'extension. Elle ajoute le widget et, si vous le souhaitez, identifie vos membres connectés avec une identité signée.

Télécharger l'extension WordPress

  1. Dans l'administration WordPress, téléversez le fichier zip via Extensions → Ajouter → Téléverser une extension, puis activez-la.
  2. Collez votre clé de site sur la page Réglages → Layvchat.
  3. Facultatif : cochez Identité des membres et saisissez le secret de signature (dans la console : Paramètres → ID visiteur → Afficher le secret).

Compatible avec les extensions de cache : l'identité du membre n'est pas écrite dans le HTML de la page ; elle est récupérée pour chaque visiteur par une requête distincte non mise en cache. Le secret de signature reste sur le serveur de votre site.

Piloter le widget par le code

Le code d'installation ajoute l'objet window.layvchat à la page. Les commandes sont utilisables dès le chargement du code : si le widget n'est pas encore prêt, les appels sont mis en attente puis exécutés. Chaque commande a aussi un nom turc (par ex. open = ac) ; les deux fonctionnent.

Commandes

CommandeNom turcEffet
open()ac()Ouvre la fenêtre de chat.
close()kapat()Ferme la fenêtre.
toggle()degistir()Ferme la fenêtre si elle est ouverte, l'ouvre si elle est fermée.
hide() / show()gizle() / goster()Retire complètement le bouton et la fenêtre de la page / les rétablit (par ex. pendant le paiement).
prefill(texte)doldur()Ouvre la fenêtre et écrit le texte dans la zone de saisie ; le visiteur appuie sur Envoyer.
setVisitor({ ad, eposta, telefon })ziyaretci()Présente le visiteur : les champs du formulaire sont préremplis, le nom et l'e-mail apparaissent dans la conversation. Clés : ad = nom, eposta = e-mail, telefon = téléphone.
setAttributes({ cle: valeur })ozellik()Informations personnalisées visibles par vos agents (montant du panier, niveau d'adhésion…). Jusqu'à 20 champs ; la valeur null supprime un champ. Modifiable pendant une conversation.
pageView()sayfa()Signale un changement de page. Dans les applications monopage (React, Vue…), les changements d'adresse sont détectés automatiquement ; à utiliser pour un routage personnalisé.
getState()durum()Renvoie { acik, sohbet, okunmamis, ajan } : fenêtre ouverte, conversation en cours, nombre de non-lus, nom de l'agent.
on(evenement, fn) / off(evenement, fn)identiqueÉcoute un événement / arrête l'écoute.
// Bouton « Poser une question sur ce produit »
document.querySelector('#question-produit').addEventListener('click', function () {
  window.layvchat.prefill('Bonjour, j\'aimerais en savoir plus sur le « Sac à dos en cuir ».')
})

// Montrer le membre connecté et son panier à l'agent
window.layvchat.setVisitor({ ad: 'Camille Martin', eposta: '[email protected]' })
window.layvchat.setAttributes({ 'Panier': '129,90 €', 'Adhésion': 'Or' })

Les données de setVisitor et setAttributes viennent du navigateur et ne sont pas signées ; la console les affiche comme non vérifiées. Pour identifier un compte membre en toute sécurité, utilisez l'identité du visiteur.

Événements

ÉvénementNom turcQuandDonnées
readyhazirLe widget est installé (un écouteur ajouté plus tard est appelé immédiatement).getState()
open / closeacildi / kapandiLa fenêtre a été ouverte / fermée.—
chatStartedsohbetBasladiLe visiteur a démarré une nouvelle conversation.—
chatEndedsohbetBittiLa conversation est terminée.—
messagemesajL'agent ou le visiteur a envoyé un message.{ kim: 'ajan' | 'ziyaretci', metin, ajan } — expéditeur, texte, nom de l'agent
unreadokunmamisLe nombre de messages non lus a changé.{ n }
window.layvchat.on('chatStarted', function () {
  gtag('event', 'chat_en_direct_demarre')        // analytique
})
window.layvchat.on('message', function (m) {
  if (m.kim === 'ajan') console.log(m.ajan + ': ' + m.metin)
})

// Les mêmes événements sont aussi émis sur window (noms anglais et turcs)
window.addEventListener('layvchat:unread', function (e) { badge(e.detail.n) })

Si votre code s'exécute avant le code d'installation, placez les appels dans la file d'attente :

(window.layvchatKuyruk = window.layvchatKuyruk || []).push(['open'], ['setAttributes', { 'Page': 'Paiement' }])

Variables CSS

La position et l'ordre d'empilement du bouton peuvent être remplacés depuis le CSS de votre site ; la fenêtre se positionne par rapport au bouton. Utile si le widget chevauche un bandeau cookies ou une barre de navigation mobile.

VariablePar défautEffet
--layvchat-altMarge inférieure des réglages du widgetDistance du bouton par rapport au bas de la page.
--layvchat-yanMarge latérale des réglages du widgetDistance du bouton par rapport au bord droit (ou gauche).
--layvchat-z2147483600Ordre d'empilement (z-index).
@media (max-width: 768px) {
  :root { --layvchat-alt: 84px; }   /* rester au-dessus de la barre mobile */
}

Si vous utilisiez Tawk.to ou Comm100, vos appels existants Tawk_API.maximize() et Comm100API.do('livechat.button.click') ouvrent aussi la fenêtre Layvchat ; inutile de modifier vos boutons.

Identité du visiteur

Si vous identifiez les visiteurs connectés à votre site, vos agents voient comme vérifié (coche bleue) le membre avec lequel ils discutent. Pour un visiteur vérifié, le formulaire avant conversation peut être ignoré et, si l'intégration au site est active, la fiche client s'affiche.

L'identité est prouvée par une signature créée sur le serveur de votre site ; personne ne peut donc usurper un autre membre depuis le navigateur. Obtenez le secret de signature dans la console via Paramètres → ID visiteur → Afficher le secret et conservez-le uniquement sur votre serveur.

Signature

imza = HMAC-SHA256(secret, id + "|" + username + "|" + zaman)   → hexadécimal minuscule (64 caractères)
ChampRègle
idIdentifiant du membre sur votre site. 1 à 64 caractères : lettres, chiffres, _ et -.
usernameNom d'utilisateur affiché aux agents. Utilisez exactement la valeur signée (jusqu'à 80 caractères affichés).
zamanHorodatage : temps Unix en secondes (pas en millisecondes). Une signature est valable 2 heures ; l'horloge de votre serveur peut avancer de 5 minutes au plus.
imzaSignature : HMAC-SHA256 en hexadécimal minuscule. Le secret sert de clé tel quel, comme texte.

Trois façons de transmettre l'identité au widget

1. L'écrire dans la page. Si vos pages sont générées côté serveur, ajoutez ceci avant le code d'installation. À éviter avec un cache de pages : la signature d'un membre pourrait être servie à d'autres visiteurs.

<script>
  window.layvchatKimlik = { id: "123", username: "cmartin", zaman: 1760000000, imza: "…" }
</script>

2. Signaler la connexion et la déconnexion. Adapté aux applications monopage (React, Vue…). Une signature étant valable 2 heures, rappelez-la avec une nouvelle signature sur les pages qui restent longtemps ouvertes.

window.layvchat.identify({ id: "123", username: "cmartin", zaman: 1760000000, imza: "…" })
window.layvchat.identify(null)   // après la déconnexion

3. Endpoint d'identité. Dans la console, sous Paramètres → ID visiteur → Endpoint d'identité, saisissez une adresse relative de votre site qui renvoie l'identité signée (par ex. /layvchat/identity). Le widget la lit depuis la même origine avec les cookies et l'actualise au chargement, toutes les 15 secondes, au retour sur l'onglet et lors de la navigation interne. Pour un visiteur non connecté, renvoyez {"id": null} ; une réponse non JSON ou en échec vaut « déconnecté ».

<?php // /layvchat/identity
session_start();
header('Content-Type: application/json');
header('Cache-Control: no-store');
$secret = getenv('LAYVCHAT_IDENTITY_SECRET');
if (empty($_SESSION['member_id'])) { echo json_encode(['id' => null]); exit; }
$id = (string) $_SESSION['member_id'];
$name = (string) $_SESSION['member_username'];
$time = time();
echo json_encode([
  'id' => $id, 'username' => $name, 'zaman' => $time,
  'imza' => hash_hmac('sha256', "$id|$name|$time", $secret),
]);
// Express — /layvchat/identity
import crypto from 'node:crypto'
app.get('/layvchat/identity', (req, res) => {
  res.set('Cache-Control', 'no-store')
  const member = req.session?.member
  if (!member) return res.json({ id: null })
  const id = String(member.id), name = String(member.username), time = Math.floor(Date.now() / 1000)
  const imza = crypto.createHmac('sha256', process.env.LAYVCHAT_IDENTITY_SECRET).update(`${id}|${name}|${time}`).digest('hex')
  res.json({ id, username: name, zaman: time, imza })
})
# Flask — /layvchat/identity
import hmac, hashlib, os, time
from flask import jsonify, session

@app.get("/layvchat/identity")
def layvchat_identity():
    if "member_id" not in session:
        response = jsonify(id=None)
    else:
        uid, name, ts = str(session["member_id"]), str(session["member_username"]), int(time.time())
        imza = hmac.new(os.environ["LAYVCHAT_IDENTITY_SECRET"].encode(), f"{uid}|{name}|{ts}".encode(), hashlib.sha256).hexdigest()
        response = jsonify(id=uid, username=name, zaman=ts, imza=imza)
    response.headers["Cache-Control"] = "no-store"
    return response

Lorsque Paramètres → ID visiteur → Afficher uniquement les identités signées est activé, les noms d'utilisateur dont la signature est invalide ne sont jamais montrés aux agents. Désactivé, ils servent seulement de nom d'affichage et ne sont pas considérés comme vérifiés.

Content Security Policy (CSP)

Si votre site utilise une CSP, autorisez l'adresse Layvchat comme suit :

script-src   {{KOK}}
connect-src  {{KOK}} {{WS}}     (et 'self' si vous utilisez un endpoint d'identité)
frame-src    {{KOK}}
style-src    'unsafe-inline'
img-src      https:            (seulement avec une image de bouton personnalisée)
media-src    {{KOK}}           (seulement avec un son de notification personnalisé)

Le widget ne dépose aucun cookie sur votre site. La clé visiteur et le lien de secours sont stockés dans localStorage avec le préfixe layv_cd_ ; le contenu des conversations et le jeton d'accès restent dans l'origine propre de la fenêtre Layvchat.

Intégration au site Pro

Pendant une conversation, vos agents voient les informations du compte du visiteur vérifié (dernières commandes, statut…) et peuvent effectuer les actions que vous autorisez. Layvchat récupère ces informations auprès de quelques endpoints de votre serveur par des requêtes signées. L'intégration ne fonctionne que pour les visiteurs vérifiés.

  1. Implémentez les endpoints ci-dessous sur votre serveur (par ex. sous https://votresite.fr/layvchat-api).
  2. Saisissez cette adresse dans la console, sous Paramètres → Intégration au site. Le secret de signature (commence par entg_) n'est affiché qu'une fois ; conservez-le sur votre serveur.
  3. Donnez aux agents les autorisations nécessaires sous Paramètres → Agents (voir les informations, notes, blocage).

Requêtes

RequêteCorpsQuand
GET /uye/:id—Quand un agent ouvre la conversation (fiche client)
POST /uye/:id/not{ not, yapan }Note client
POST /uye/:id/durum{ engelli: true | false, yapan }Bloquer / débloquer le compte
POST /uye/:id/mesaj{ baslik, govde, yapan }Réponse dans la messagerie de votre site (titre, texte)

:id est l'id de l'identité du visiteur. yapan désigne l'agent qui a effectué l'action (canli-destek:nomutilisateur).

Vérifier la signature

Chaque requête arrive avec les en-têtes X-Imza-Zaman (secondes Unix) et X-Imza :

X-Imza = HMAC-SHA256(secret, temps + "." + METHODE + "." + cheminComplet + "." + corpsBrut)   → hexadécimal minuscule

cheminComplet est le chemin de la requête, chemin de votre adresse compris : si l'adresse est https://votresite.fr/layvchat-api, c'est /layvchat-api/uye/42. Pour les requêtes GET, le corps est une chaîne vide. Rejetez les horodatages de plus de 5 minutes.

<?php
function layvchat_verify(string $secret): bool {
  $time = $_SERVER['HTTP_X_IMZA_ZAMAN'] ?? '';
  $sig  = $_SERVER['HTTP_X_IMZA'] ?? '';
  if (!ctype_digit($time) || abs(time() - (int) $time) > 300) return false;
  $body = file_get_contents('php://input');
  $expected = hash_hmac('sha256', $time . '.' . $_SERVER['REQUEST_METHOD'] . '.' . $_SERVER['REQUEST_URI'] . '.' . $body, $secret);
  return hash_equals($expected, $sig);
}
// Express : nécessite le corps brut → app.use(express.json({ verify: (req, _r, buf) => { req.rawBody = buf.toString() } }))
import crypto from 'node:crypto'
function layvchatVerify(req, secret) {
  const time = req.get('x-imza-zaman') || '', sig = req.get('x-imza') || ''
  if (!/^\d+$/.test(time) || Math.abs(Date.now() / 1000 - Number(time)) > 300) return false
  const expected = crypto.createHmac('sha256', secret)
    .update(`${time}.${req.method}.${req.originalUrl}.${req.rawBody || ''}`).digest('hex')
  return expected.length === sig.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))
}

Réponse de la fiche client

GET /uye/:id doit renvoyer un objet JSON. Seul uye.id est obligatoire ; les champs inconnus sont ignorés. Si le membre n'existe pas, renvoyez 404.

{
  "uye": {
    "id": "42", "kullaniciAdi": "cmartin", "ad": "Camille", "soyad": "Martin",
    "eposta": "[email protected]", "telefon": "5551112233", "ulke": "FR", "paraBirimi": "EUR",
    "toplamHarcama": 5000, "toplamIade": 320,
    "durum": "aktif", "kayitTarihi": "2025-03-14T10:00:00Z", "sonGiris": "2026-10-04T21:10:00Z",
    "riskEtiketleri": ["nouveau compte"]
  },
  "sonIslemler": [
    { "id": "S1001", "tur": "siparis", "tutar": 500, "durum": "expédiée", "aciklama": "2 articles", "zaman": "2026-10-04T20:00:00Z" }
  ]
}
Tous les champs client
TypeChamps
Texte (200 caractères max.)kullaniciAdi (nom d'utilisateur), eposta (e-mail), ad (prénom), ikinciAd (deuxième prénom), soyad (nom), telefon, telefonKodu (téléphone, indicatif), sehir (ville), dil (langue), sonGirisIp, kayitIp (IP de dernière connexion / d'inscription), yoneticiNotu (note administrateur)
Code pays (2 lettres)ulke, kayitUlke (pays, pays d'inscription)
Devise (3 lettres)paraBirimi
Oui / noncevrimici (en ligne), epostaDogrulandi, telefonDogrulandi (e-mail / téléphone vérifié)
Horodatage (ISO 8601)sonCevrimici, sonGiris, kayitTarihi (dernière présence, dernière connexion, inscription)
Date (AAAA-MM-JJ)dogumTarihi (date de naissance)
Montant (nombre)toplamHarcama, toplamIade (total dépensé, total remboursé)
Statutdurum : aktif (actif), engelli (bloqué), beklemede (en attente), kapali (fermé)
ListeriskEtiketleri (étiquettes de risque, 20 max.)
Dernières opérationssonIslemler[] : id (obligatoire), tur (siparis commande, odeme paiement, iade remboursement, ou votre propre texte), tutar, durum, aciklama, zaman (montant, statut, description, date) — les 10 premières sont affichées

Pour les requêtes POST, renvoyez en cas de succès 2xx et un objet JSON (par ex. {"ok": true}). Pour refuser, renvoyez {"hata": "code_court"}. Un 404 est présenté à l'agent comme « introuvable », les autres codes d'erreur comme « le site a renvoyé une erreur ».

Les requêtes passent uniquement par https sur le port 443, vers des adresses publiques ; les redirections ne sont pas suivies. Délai maximal 10 secondes, réponses limitées à 512 Ko.

Webhooks Pro

Envoyez les événements de conversation vers vos propres systèmes (CRM, notifications, reporting) en temps réel. Choisissez l'adresse et les événements dans la console, sous Paramètres → Webhooks et API ; le secret de signature (commence par whsec_) n'est affiché qu'une fois.

ÉvénementQuandveri (données)
sohbet.basladiUne nouvelle conversation a commencé{ konusma }
sohbet.bittiUne conversation a été fermée{ konusma }
sohbet.kacirildiLe visiteur est parti sans réponse{ konusma }
sohbet.cevrimdisiUn message a été laissé hors ligne{ konusma, mesaj }
sohbet.puanlandiLe visiteur a donné une note{ konusmaId, puan, yorum, anket }
sohbet.riskLe niveau de risque a augmenté{ konusmaId, risk: { seviye, turler, kelimeler, zaman } }
{
  "id": "6f1c…",                 // identifiant de livraison — identique lors des nouvelles tentatives
  "olay": "sohbet.bitti",          // événement
  "zaman": "2026-10-05T09:12:00.000Z",
  "deneme": 1,                     // tentative
  "veri": {
    "konusma": {                   // conversation
      "id": "…", "durum": "kapandi", "kaynak": "ziyaretci",
      "baslatildi": "…", "kapandi": "…", "kapanisSebep": "kapatildi",
      "ziyaretci": { "no": 128, "ad": "Camille", "kullaniciAdi": "cmartin", "uyeId": "42", "ulke": "FR" },
      "ajan": "Léa", "etiketler": ["paiement"], "puan": 5
    }
  }
}

kullaniciAdi (nom d'utilisateur) et uyeId (identifiant membre) ne sont remplis que pour les visiteurs vérifiés.

En-têtes et signature

X-Imza-OlayNom de l'événement
X-Imza-ZamanSecondes Unix
X-Imza-Imzasha256= + HMAC-SHA256(secret, temps + "." + corpsBrut), hexadécimal minuscule
X-Imza-TeslimIdentifiant de livraison (identique à id dans le corps)
<?php
$body = file_get_contents('php://input');
$time = $_SERVER['HTTP_X_IMZA_ZAMAN'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $time . '.' . $body, getenv('LAYVCHAT_WEBHOOK_SECRET'));
if (!ctype_digit($time) || abs(time() - (int) $time) > 300 || !hash_equals($expected, $_SERVER['HTTP_X_IMZA_IMZA'] ?? '')) {
  http_response_code(401); exit;
}
$event = json_decode($body, true);
// ignorer si $event['id'] a déjà été traité ; puis renvoyer 2xx
http_response_code(204);
import crypto from 'node:crypto'
app.post('/layvchat/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const time = req.get('x-imza-zaman') || '', raw = req.body.toString()
  const expected = 'sha256=' + crypto.createHmac('sha256', process.env.LAYVCHAT_WEBHOOK_SECRET).update(`${time}.${raw}`).digest('hex')
  const sig = req.get('x-imza-imza') || ''
  const valid = /^\d+$/.test(time) && Math.abs(Date.now() / 1000 - Number(time)) <= 300 &&
    sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
  if (!valid) return res.sendStatus(401)
  const event = JSON.parse(raw)
  // ignorer si event.id a déjà été traité
  res.sendStatus(204)
})

Répondez 2xx en moins de 8 secondes ; placez les traitements lourds en file d'attente. Une livraison échouée est retentée après 5 s, 30 s et 2 min (4 tentatives au plus). Après 20 échecs consécutifs, le webhook est désactivé ; dans la console, vous voyez les 50 dernières livraisons et pouvez envoyer un exemple avec « Tester ». L'adresse doit être en https, sur le port 443 et accessible publiquement.

API REST Pro

Récupérez rapports et conversations dans vos propres systèmes. Créez une clé dans la console, sous Paramètres → Webhooks et API → Clés API (propriétaire de l'espace de travail uniquement) ; la clé secrète n'est affichée qu'une fois. Jusqu'à 5 clés actives ; chacune peut avoir une liste d'IP autorisées. Les clés sont en lecture seule.

curl -u "layv_…:layvs_…" "{{KOK}}/api/v1/canli/rapor?gun=7"
EndpointParamètresRéponse
GET /api/v1/canli/raporgun (jours) : 1, 7, 30 ou 90 (7 par défaut)Synthèse, répartition par jour et par heure, performance des agents, notes, étiquettes, conversations manquées
GET /api/v1/canli/sohbetlerbas, bit (du / au, AAAA-MM-JJ, inclus) · durum : acik | kapandi (ouverte | fermée) · adet 1–200 (50) · sayfa (page){ toplam, sayfa, adet, liste: [konusma] }
GET /api/v1/canli/sohbet/:id—{ sohbet, mesajlar: [{ kim, ajan, metin, dosya, zaman }] } — notes internes et chuchotements exclus
GET /api/v1/ben—Nom et portée de la clé

L'objet konusma est le même que dans les webhooks. Codes d'erreur : 401 clé absente ou invalide (la raison n'est pas précisée par sécurité) · 402 votre offre n'inclut pas l'API · 404 conversation introuvable.

Dépannage

Le bouton de chat n'apparaît pas
Vérifiez la clé de site (elle commence par cd_). Dans la console, Paramètres → Général → Chat en direct activé est peut-être désactivé, ou Paramètres → Apparence du widget → Masquer sur mobile activé. Si votre site a une CSP, ajoutez les autorisations. Dans la console du navigateur, regardez la réponse de la requête /cd/v/ayar : 404 signifie que la clé n'est pas reconnue.
Le visiteur n'apparaît pas comme vérifié
Causes les plus fréquentes : zaman envoyé en millisecondes (il faut des secondes), signature en majuscules, horloge serveur décalée, nom d'utilisateur signé différent de celui envoyé, ou id contenant des caractères non autorisés. Une signature est valable 2 heures ; renouvelez-la sur les pages ouvertes longtemps.
Les webhooks n'arrivent pas
Consultez l'historique des livraisons dans la console, sous Paramètres → Webhooks et API. L'adresse doit être en https, répondre 2xx en moins de 8 secondes et ne pas rediriger. Après 20 erreurs consécutives, le webhook est désactivé ; réactivez-le une fois corrigé.
La fiche client ne s'ouvre pas
La fiche ne s'ouvre que pour les visiteurs vérifiés et si l'agent a l'autorisation de voir les informations. Pour vérifier la signature, votre serveur doit utiliser le chemin complet (chemin de votre adresse compris).