Centre d'aide
Apps métier · Guide

Webhooks : relier vos adhésions et votre boutique à un serveur de jeu ou un service

Capibara prévient votre service (serveur de jeu, bot Discord, n8n, Make) quand une adhésion commence, se renouvelle ou s’arrête, et quand une commande est payée. Mise en place, contenu des envois, vérification de la signature.

Un webhook est une adresse de VOTRE service que Capibara appelle dès qu’il se passe quelque chose : un joueur achète le rang VIP sur votre site, Capibara envoie l’information à votre serveur, qui donne le rang en jeu. Quand l’adhésion s’arrête, Capibara le dit aussi, et votre serveur retire le rang.

Les webhooks se règlent dans Paramètres › Intégrations, par le propriétaire et les administrateurs (droit « Webhooks » des rôles). Le forfait Gratuit en permet 2, les forfaits Pro et Business 10.

Mettre en place

  • Dans Paramètres › Intégrations, choisissez « Ajouter un webhook ». Donnez-lui un nom, l’adresse de votre service (en https://, avec un nom de domaine), et les familles d’événements à recevoir (Adhésions, Boutique).
  • Copiez le secret de signature affiché : il ne s’affichera plus. Votre service s’en sert pour vérifier que chaque envoi vient bien de Capibara.
  • Choisissez « Envoyer un test » : Capibara envoie un événement « ping » et vous dit tout de suite si votre service l’a reçu.
  • Pour demander le pseudo du joueur, ouvrez le palier dans Mon site › Adhésions et réglez « Information demandée à l’achat » (pseudo Minecraft, SteamID64 ou texte court). Le champ apparaît sur le bloc « Adhésion » de votre site et part avec chaque envoi. Une faute de frappe se corrige depuis le tableau des adhérents (menu de la ligne).

Les événements

  • membership.created : le premier paiement est passé, l’accès commence.
  • membership.renewed : une nouvelle période est payée.
  • membership.canceled : l’adhérent (ou vous) a demandé la résiliation ; l’accès reste jusqu’à la fin de la période payée.
  • membership.resumed : la résiliation demandée est levée.
  • membership.expired : l’abonnement est clos, l’accès s’arrête maintenant.
  • membership.updated : vous avez corrigé l’information saisie à l’achat (l’ancienne valeur est dans data.previous).
  • membership.refunded : un paiement de l’adhésion est remboursé, en tout ou en partie, depuis Capibara ou depuis Stripe (contestation perdue comprise). data.refund porte le montant de ce remboursement (amountCents), le total déjà rendu sur ce paiement (cumulativeCents), la devise et la facture Stripe du paiement (invoiceId). L’accès ne change pas : si le rang doit s’arrêter, résiliez l’adhésion, et membership.expired suivra. Un remboursement en attente chez Stripe (prélèvement) n’est envoyé qu’une fois confirmé. Un paiement d’avant le 24 octobre 2026 ne déclenche pas cet événement.
  • order.paid : une commande de la boutique est payée (articles, quantités, montants).
  • booking.created : une réservation du planning est créée, par votre équipe ou en ligne. Payée en ligne, elle part avant le paiement : son paymentStatus vaut alors « pending ».
  • booking.confirmed : la réservation est confirmée, par votre équipe, à l’acceptation d’une demande ou au paiement en ligne.
  • booking.cancelled : la réservation ne tient plus son créneau : annulée (par le client, votre équipe ou faute de paiement, voir cancelledBy) ou demande refusée (status « rejected »).

Les réservations

Un événement booking.* porte data.booking : id, status (scheduled, confirmed, pending_approval, cancelled, rejected, no_show), resourceId et resourceName, startsAt et endsAt (instants UTC, ISO 8601), quantity, source (online ou team), seriesId (la série récurrente, ou null), clientId (la fiche du client dans Capibara, ou null), priceCents (TTC, en centimes, ou null), paymentStatus (none, on_site, pending, authorized, paid, expired, voided) et, annulée, cancelledBy (client, team ou system).

Ni le titre, ni les notes, ni le nom ou le téléphone du client ne sont transmis ; l’e-mail du client seulement avec cette même option. Une même réservation peut être annulée, réactivée, puis annulée de nouveau : chaque fois est un événement distinct. Appliquez l’état reçu plutôt que de compter les événements.

Ce que reçoit votre service

Une requête POST en JSON, avec trois en-têtes : webhook-id (l’identifiant de l’envoi, le même à chaque nouvel essai), webhook-timestamp (l’heure de l’envoi, en secondes) et webhook-signature. Exemple pour une adhésion qui commence :

{
  "type": "membership.created",
  "timestamp": "2026-10-12T14:02:11.000Z",
  "organizationId": "cm…",
  "data": {
    "membership": {
      "id": "cm…",
      "sequence": 1,
      "status": "active",
      "access": true,
      "tier": { "id": "cm…", "name": "VIP", "rank": 2 },
      "currentPeriodEnd": "2026-11-12T14:02:00.000Z",
      "cancelAtPeriodEnd": false,
      "canceledAt": null,
      "createdAt": "2026-10-12T14:01:40.000Z",
      "memberId": "cm…",
      "purchaseField": { "kind": "minecraft", "label": "Pseudo Minecraft", "value": "Steve_42" }
    }
  }
}

La charge porte l’ÉTAT COMPLET de l’adhésion au moment de l’événement. Appliquez cet état (« access » vrai : donner le rang du palier ; faux : le retirer) plutôt que de compter les événements. Un même envoi peut arriver deux fois, et deux envois peuvent arriver dans le désordre : ignorez un webhook-id déjà traité, et un « sequence » plus petit que le dernier appliqué pour cette adhésion.

L’e-mail de l’acheteur n’est pas transmis, sauf si vous cochez « Inclure l’e-mail de l’acheteur ou du client ». Le nom ne l’est jamais. Vous restez responsable de ce que votre service fait de ces données.

Vérifier la signature

Capibara suit le format ouvert Standard Webhooks : la signature est « v1, » suivi du HMAC-SHA256, en base64, de « webhook-id.webhook-timestamp.corps », calculé avec votre secret. Les bibliothèques officielles du standard (JavaScript, PHP, Python, Java, Go, Ruby, C#, Rust…, voir github.com/standard-webhooks/standard-webhooks) vérifient sans code. Sans bibliothèque, en JavaScript :

const crypto = require('node:crypto');

// secret : la valeur « whsec_… » affichée à la création du webhook.
// corps : le corps BRUT de la requête, tel que reçu (pas un JSON relu puis réécrit).
function verifierCapibara(secret, headers, corps) {
  const id = headers['webhook-id'];
  const horodatage = Number(headers['webhook-timestamp']);
  if (!id || !Number.isInteger(horodatage)) return false;
  // Plus de 5 minutes d'écart : refusé (un envoi rejoué plus tard ne passe pas).
  if (Math.abs(Date.now() / 1000 - horodatage) > 300) return false;
  const cle = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
  const attendu = crypto.createHmac('sha256', cle).update(id + '.' + horodatage + '.' + corps).digest();
  return String(headers['webhook-signature'] || '').split(' ').some((partie) => {
    const [version, signature] = partie.split(',');
    if (version !== 'v1' || !signature) return false;
    const recu = Buffer.from(signature, 'base64');
    return recu.length === attendu.length && crypto.timingSafeEqual(recu, attendu);
  });
}

Après une rotation du secret (« Plus d’actions », « Faire tourner le secret »), l’ancien secret signe encore pendant 24 heures : l’en-tête porte alors deux signatures séparées d’une espace, et l’exemple ci-dessus accepte l’une ou l’autre.

Répondre, et ce qui se passe en cas d’échec

Répondez par un code 2xx en moins de 10 secondes, puis faites le travail (donner le rang) ensuite. Une redirection n’est pas suivie. Une adresse dont le nom désigne un réseau interne est refusée : rien n’y part.

Un envoi qui échoue est repris 1, 5, 15 et 60 minutes plus tard, puis à 3, 6, 12 et 24 heures. Si tous les essais échouent, le webhook se désactive et les administrateurs sont prévenus ; ses envois en attente partent dès que vous le réactivez. Le journal du webhook garde 30 jours d’envois, avec leur résultat, et « Renvoyer » relance un envoi avec le même webhook-id.

Un serveur de jeu

Capibara appelle une adresse HTTPS publique. Un serveur de jeu n’en a souvent pas : placez un petit service entre les deux (un bot, n8n, Make, ou un plugin qui écoute derrière un certificat HTTPS), qui vérifie la signature puis passe la commande au serveur. La valeur saisie à l’achat est déjà contrôlée par Capibara (un pseudo Minecraft ne contient que des lettres, des chiffres et _), mais gardez côté serveur une liste fixe des commandes autorisées. Un texte court peut contenir des espaces : ne l’insérez jamais tel quel dans une commande, il y ajouterait des arguments.

Pour un achat unique, sans abonnement, la boutique sait déjà vendre une clé d’activation : votre plugin génère les codes, vous les importez sur le produit, le joueur saisit le sien en jeu.

Vérifiez les règles de votre jeu avant de vendre un avantage : celles de Minecraft encadrent ce qu’un serveur peut vendre, et FiveM ou RedM n’autorisent qu’un seul prestataire de paiement. Stripe n’accepte la vente d’objets de jeu que si vous exploitez vous-même le serveur.

Cet article vous a-t-il été utile ?

Cet article ne répond pas à votre besoin ?

Contacter le support →

Toute documentation qui ne serait pas encore écrite ici figure déjà, quelque part, dans la Bibliothèque de Babel — bon courage pour la page exacte. Sinon, un ticket ira plus vite.