Intégrer Kauri

La balise, l’identifiant visiteur, les paiements KkiaPay et les codes d’erreur, avec des extraits prêts à copier.

Tu débutes ? Suis le Démarrage rapide, pas à pas.

Sommaire

Les valeurs en majuscules sont des exemples. Connecte-toi pour une page pré-remplie avec ta clé de site et tes URL.

La balise

Colle-la dans le <head> de chaque page de monsaas.com.

À coller dans le <head>
<script defer src="https://kauridata.app/k.js" data-site="ks_TA_CLE_DE_SITE"></script>
Next.js (App Router) : app/layout.tsx
import Script from "next/script";

// Dans <body>, après {children} :
<Script src="https://kauridata.app/k.js" data-site="ks_TA_CLE_DE_SITE" {...(process.env.NODE_ENV === "development" && { "data-debug": "" })} strategy="afterInteractive" />
  • data-site : la clé de site. Elle est publique, puisqu’elle figure dans le code de tes pages.
  • data-debug, facultatif : suit aussi localhost. Les événements reçus en debug sont gardés à part, hors statistiques et quota, et les rejets s’écrivent dans la console.
  • L’URL https://kauridata.app/k.js ne change pas et chaque version reste rétrocompatible.

Kauri n’accepte les événements que depuis monsaas.com, ses sous-domaines et les domaines supplémentaires déclarés dans les Réglages (plus localhost avec data-debug) : ce filtrage empêche un autre site d’utiliser ta clé. Il ne bloque pas un appel fabriqué hors d’un navigateur. Une requête refusée répond par un code : voir Rejets de la balise.

Identifiant visiteur

window.kauri.visitorId() renvoie l’identifiant du visiteur (kv_ suivi de 22 caractères), stocké dans le cookie _kauri_vid de ton domaine. Un visiteur est reconnu jusqu’à 12 mois selon le navigateur. Sans identifiant (robot, localhost sans data-debug, cookies bloqués), la fonction renvoie null.

Appelle-la toujours ainsi : une balise absente, bloquée ou pas encore chargée ne casse jamais ton bouton de paiement.

Appel sûr
window.kauri?.visitorId?.() ?? ""

Pour vérifier l’installation, ouvre https://monsaas.com et exécute window.kauri.visitorId() dans la console du navigateur.

partnerId (méthode A)

Si le partnerId de ton widget KkiaPay est libre, passe-lui l’identifiant visiteur. Kauri relie alors chaque paiement reçu par webhook à son visiteur, sans code serveur.

À l’ouverture du widget
openKkiapayWidget({
  amount: 15000,
  key: "…",
  partnerId: window.kauri?.visitorId?.() ?? "",
});
  • L’identifiant fait 25 caractères. En sandbox, KkiaPay renvoie le partnerId à l’identique jusqu’à 255 caractères au moins, en mobile money (réussi ou échoué) comme par carte.
  • Wave n’a pas pu être vérifié en sandbox : aucune garantie tant qu’un paiement live ne l’a pas confirmé.
  • Passe l’identifiant tel quel, sans préfixe ni troncature : un partnerId qui n’est pas exactement au format kv_ + 22 caractères est ignoré, et le paiement attend un appel à l’API d’attribution.
  • Ton partnerId porte déjà autre chose, un identifiant de commande par exemple ? Garde-le et passe par la méthode B.

API d’attribution (méthode B)

Ton serveur relie le transactionId KkiaPay à l’identifiant visiteur, dès qu’il le connaît. Le webhook et l’appel peuvent arriver dans n’importe quel ordre.

Requête
POST https://kauridata.app/api/v1/attribution
Authorization: Bearer <secret serveur du site>
Content-Type: application/json

{"visitorId": "kv_…", "transactionId": "…"}
  • 200 {"status": "matched"} : le paiement est déjà reçu et relié.
  • 202 {"status": "pending_webhook"} : lien enregistré, relié dès l’arrivée du webhook.
  • La même paire envoyée de nouveau répond 200, sans effet, avec l’état du moment : matched, ou pending_webhook tant que le webhook n’est pas arrivé.
  • Erreurs : voir Erreurs de l’API d’attribution. Chaque réponse porte un request_id à nous transmettre en cas de problème.

Bonnes pratiques

  • Appelle l’API après ta propre logique de paiement, avec un timeout de 3 s : elle ne doit jamais bloquer ton client.
  • Appelle-la aussi après un paiement échoué : Kauri relie l’échec au visiteur et chiffre l’argent perdu sans le surestimer.
  • Sur une réponse 5xx, relance 3 fois au plus en respectant Retry-After : l’appel est idempotent.
  • transactionId : 128 caractères au plus.

Extraits

Ton serveur tient l’identifiant visiteur du cookie _kauri_vid ou du front, qui l’envoie avec la requête de paiement (window.kauri?.visitorId?.() ?? "").

La balise pose le cookie _kauri_vid sur ton domaine parent : ton serveur le lit s’il reçoit la requête de paiement sur ce domaine ou l’un de ses sous-domaines.

Essai depuis ton terminal
curl -X POST https://kauridata.app/api/v1/attribution \
  --max-time 3 \
  -H "Authorization: Bearer $KAURI_SITE_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"visitorId": "kv_…", "transactionId": "…"}'

Webhooks KkiaPay

Dans le tableau de bord KkiaPay, ajoute un webhook par environnement avec les événements « Successful transactions » et « Failed transactions ». Colle le secret serveur du site dans « Secret hash », en live comme en sandbox. L’URL qui reçoit un paiement fixe son environnement : les paiements sandbox ne comptent dans aucun revenu.

URL du webhook live
https://kauridata.app/api/webhooks/kkiapay/ID_DU_SITE/live
URL du webhook sandbox
https://kauridata.app/api/webhooks/kkiapay/ID_DU_SITE/sandbox

Mode relais

KkiaPay n’accepte qu’une URL de webhook par environnement. Si ton serveur l’utilise déjà, garde-la : ta route transmet le corps brut à Kauri après sa réponse, sans la ralentir.

  • Ne relaie qu’un webhook dont tu as vérifié le secret KkiaPay, et transmets le corps sans le modifier.
  • En-têtes : x-kkiapay-secret = secret serveur du site, x-kauri-relay: 1 (affiché « Relayé » dans le journal).
  • 3 s par essai et 2 relances ; l’échec final s’écrit dans tes logs, jamais dans ta réponse à KkiaPay.
  • KKIAPAY_SANDBOX=true quand ton widget utilise la clé sandbox : le relais vise alors l’URL sandbox.
Relais à ajouter à ta route webhook
// Ta route webhook KkiaPay existante, par exemple app/api/webhooks/kkiapay/route.ts.
import { after } from "next/server";

// KKIAPAY_SANDBOX=true quand ton widget utilise la clé sandbox.
const KAURI_URL =
  process.env.KKIAPAY_SANDBOX === "true"
    ? "https://kauridata.app/api/webhooks/kkiapay/ID_DU_SITE/sandbox"
    : "https://kauridata.app/api/webhooks/kkiapay/ID_DU_SITE/live";

// Relais vers Kauri : 3 s par essai, 2 relances (après 1 s puis 2 s), sans jamais bloquer ta réponse.
async function relayToKauri(body: string) {
  for (let attempt = 0; attempt < 3; attempt++) {
    if (attempt > 0) await new Promise((resolve) => setTimeout(resolve, attempt * 1000));
    try {
      const response = await fetch(KAURI_URL, {
        method: "POST",
        headers: {
          "content-type": "application/json",
          "x-kkiapay-secret": process.env.KAURI_SITE_SECRET ?? "",
          "x-kauri-relay": "1",
        },
        body,
        signal: AbortSignal.timeout(3000),
      });
      if (response.ok) return;
      if (attempt === 2) console.error(`Relais Kauri : échec final, statut ${response.status}`);
    } catch (error) {
      if (attempt === 2) console.error("Relais Kauri : échec final", error);
    }
  }
}

export async function POST(request: Request) {
  // Corps brut, lu une seule fois : c’est lui qui est relayé.
  const body = await request.text();
  // … ta vérification existante de x-kkiapay-secret : ne relaie qu’un webhook authentifié …
  after(() => relayToKauri(body));
  // … ton traitement existant, à partir de JSON.parse(body) …
  return new Response(null, { status: 200 });
}

Secret serveur du site

Le secret whsec_… authentifie les webhooks, le relais et l’appel à l’API d’attribution. Garde-le côté serveur, dans la variable KAURI_SITE_SECRET, jamais dans le code envoyé au navigateur. Tu le trouves, masqué, dans les Réglages. Après une régénération, l’ancien reste accepté 24 h : remplace-le partout avant.

Codes d’erreur et journal

Chaque code a son ancre : copie le lien d’une ligne pour la partager.

Formats d’erreur

Une réponse d’erreur de Kauri a l’un de ces trois formats.

FormatRouteAppelantConduite à tenir
Code simple : {"code": "…"}Ingestion /api/e et webhooks /api/webhooks/kkiapay/…La balise Kauri dans le navigateur, ou KkiaPay (et ton relais).Cherche le code dans les tableaux ci-dessous (Rejets de la balise, Codes du journal) et applique la correction.
application/problem+json avec request_id/api/v1/attributionTon serveur, avec le secret serveur du site.Lis type et status, corrige la requête si c’est un 4xx, relance au plus 3 fois sur un 5xx en respectant Retry-After, et transmets le request_id si le problème persiste.
Erreur de configuration : {"error": "server_config_error", "variables": […]}Les routes de Kauri, en 500, quand une variable d’environnement du serveur est invalide.Quiconque appelle : balise, KkiaPay ou ton serveur.Rien à corriger de ton côté : la faute est dans la configuration de Kauri. Les variables listées sont à signaler à l’opérateur ; relance plus tard.

Rejets de la balise

Réponse 4xx de l’ingestion, corps {code}. Avec data-debug, la balise l’écrit dans la console.

CodeStatutSignification et correction
unknown_site_key404La clé data-site ne correspond à aucun site. Recopie la balise depuis Kauri, sans la modifier.
invalid_payload400Requête illisible : la balise a été modifiée ou un autre script appelle l’ingestion. Utilise la balise telle quelle.
payload_too_large413Requête de plus de 8 Ko, souvent une URL ou un référent démesuré. Raccourcis les paramètres de l’URL de la page.
origin_mismatch403La page n’est ni sur le domaine du site, ni sur l’un de ses sous-domaines, ni sur un domaine supplémentaire. Corrige le domaine ou ajoute ce domaine dans les Réglages.

Erreurs de l’API d’attribution

Corps application/problem+json (RFC 9457) : type, title, status et request_id.

CodeStatutSignification et correction
invalid_body400Le corps doit être un objet JSON {visitorId, transactionId}.
invalid_visitor_id400visitorId attendu au format kv_ suivi de 22 caractères base62.
missing_transaction_id400transactionId manquant.
invalid_transaction_id400transactionId attendu en texte de 128 caractères au plus.
invalid_secret401Secret serveur du site invalide.
site_not_found404Aucun site ne correspond à ce secret.
transaction_already_linked409Cette transaction est déjà liée à un autre visiteur.
server_config_error500Erreur de configuration de Kauri : relance sûre, idempotente (3 relances au plus).
temporarily_unavailable503Erreur temporaire de Kauri : relance sûre, idempotente (3 relances au plus).

Codes du journal des webhooks

Chaque webhook reçu apparaît dans Réglages › Derniers webhooks avec l’un de ces codes.

CodeLibelléSignification et correction
processedTraitéPaiement enregistré. Rien à faire.
duplicateDoublonDéjà reçu : KkiaPay a relancé ou le relais a renvoyé le même paiement. Rien à faire.
ignored_eventIgnoréÉvénement sans paiement à enregistrer. Rien à faire.
secret_mismatchRejeté : secret incorrectLe secret reçu n’est pas le secret serveur du site. Recopie-le dans le champ « Secret hash » du webhook KkiaPay, ou dans l’en-tête x-kkiapay-secret de ton relais.
payload_invalidRejeté : payload invalideLe corps reçu n’est pas un webhook KkiaPay lisible. Si tu relaies, transmets le corps brut sans le modifier.
unknown_siteRejeté : site inconnuAucun site ne correspond à cette URL. Recopie l’URL de webhook depuis Kauri.
env_mismatchRejeté : environnementLe paiement ne vient pas de l’environnement de cette URL. Colle l’URL live dans KkiaPay live et l’URL sandbox dans KkiaPay sandbox.
server_config_errorErreur de configuration côté KauriErreur de configuration chez Kauri, pas chez toi. Nous sommes prévenus ; KkiaPay relancera.
db_unavailableKauri momentanément indisponibleBase de données de Kauri momentanément indisponible, pas chez toi. KkiaPay relancera ; rien à faire.

Journal des changements

La balise et l’API /api/v1/ restent rétrocompatibles : une nouveauté ne casse jamais une intégration existante.

  1. 31/10/2026WebhooksRégénération du secret serveur du site : l’ancien reste accepté 24 h et le journal signale « Ancien secret ».
  2. 30/10/2026APIPOST /api/v1/attribution : attribution par appel serveur (méthode B), erreurs RFC 9457 avec request_id.
  3. 30/10/2026WebhooksUne URL live et une URL sandbox par site ; relais accepté avec l’en-tête x-kauri-relay: 1.
  4. 30/10/2026BalisePremière version de k.js : pages vues, window.kauri.visitorId(), apps monopages, attribut data-debug.

Politique de confidentialité

Paragraphe prêt à coller sur ton site.

Nous utilisons Kauri pour mesurer l’audience de ce site et comprendre quelles pages mènent à un achat. Kauri dépose deux cookies sur notre domaine : un identifiant aléatoire, conservé jusqu’à 12 mois selon le navigateur, et un identifiant de visite, qui expire après 30 minutes d’inactivité. Kauri ne collecte ni nom, ni numéro de téléphone, ni adresse e-mail. Votre pays est déduit de votre adresse IP, qui n’est pas conservée.