Skip to content

Cas d'usage

Émettre automatiquement des clés depuis une boutique

Connectez une boutique ou un Merchant-of-Record à Keyright pour qu'une commande payée génère une clé de licence et l'envoie automatiquement par e-mail à l'acheteur — avec des événements vérifiés par HMAC, des réessais idempotents et une révocation déclenchée par le remboursement.

Objectif. Un client paie sur votre boutique (ou chez un Merchant-of-Record qui vend pour votre compte), et une licence Keyright est générée et lui est envoyée par e-mail sans aucune étape manuelle — et un remboursement ou une annulation la révoque tout aussi automatiquement.

Quand l’utiliser

  • Utilisez ceci si vous vendez via un parcours de paiement — votre propre boutique, un prestataire de paiement ou un Merchant-of-Record (MoR) qui gère la fiscalité et la facturation — et que vous voulez une exécution des commandes sans intervention.
  • Utilisez ceci si vous voulez que les remboursements et annulations coupent l’accès sans que quelqu’un ait à penser à révoquer.
  • Pas ceci si vous émettez des clés à la main pour une poignée de comptes entreprise (utilisez simplement Licenses → Issue license ou POST /admin/licenses), ou si votre système de facturation appelle déjà lui-même l’API d’administration — le webhook est une commodité, pas le seul chemin.

C’est avant tout une commodité Cloud — le service hébergé est déjà joignable depuis l’Internet public, donc une boutique peut lui envoyer un POST directement. Cela fonctionne aussi en auto-hébergé, tant que votre instance est joignable par la boutique (une URL publique ou un tunnel que le prestataire peut atteindre) ; tout ce qui suit est identique, à l’URL de base près.

Boutique / MoRl'acheteur paieproduit + varianteHMACPOST /webhooks/fulfill/{slug}1. vérifier X-Keyright-Signature2. correspondance → produit + édition3. générer la licence (idempotentpar orderRef)4. envoyer la clé par e-mailL'acheteur reçoit la cléla colle dans votre applicationremboursement / annulation→ révoque la même licenceLe tenant est pris dans la route et imposé à l'événement : une signature valide pour un tenant ne peut jamais émettre dans un autre.Un « paid » en double pour le même orderRef renvoie la licence existante au lieu d'en émettre une seconde.Keyright ne traite jamais le paiement — il réagit à l'événement normalisé que votre boutique (ou un adaptateur léger) envoie.
Une commande payée devient une clé de licence signée et envoyée par e-mail ; un remboursement la révoque — le tout depuis un seul webhook vérifié par HMAC.

Ce que vous faites (côté éditeur)

1. Récupérer votre URL de webhook et votre secret

Chaque tenant dispose d’un webhook d’exécution des commandes par tenant à POST /webhooks/fulfill/{slug} (où {slug} est votre tenant) et d’un secret utilisé pour signer les événements en HMAC. Le tenant est pris dans la route et imposé à l’événement, de sorte qu’une signature valide pour un tenant ne peut jamais générer une licence dans un autre.

Trouvez les deux dans les paramètres d’intégration/développeur du tableau de bord, ou via l’API : GET /admin/account renvoie un bloc integration avec l’URL de base, la clé publique de signature et l’URL du webhook. Faites tourner le secret à tout moment (rôle owner) :

curl -X POST $BASE/admin/webhook-secret/rotate -H "X-Admin-Token: $TOKEN"
# -> { "webhookSecret": "…", "webhookUrl": "https://keyright.delta1labs.com/webhooks/fulfill/acme" }

2. Y faire pointer votre boutique

Dans votre boutique / prestataire de paiement / MoR, ajoutez un webhook qui se déclenche sur le cycle de vie de la commande et définissez :

  • URL — votre /webhooks/fulfill/{slug}.
  • Secret de signature — le webhookSecret ci-dessus. Le prestataire signe en HMAC le corps brut de la requête et l’envoie dans l’en-tête X-Keyright-Signature ; Keyright le vérifie avec votre secret et rejette tout ce qui ne correspond pas (401).

La plupart des boutiques émettent leur propre forme d’événement. Keyright attend un événement normalisé ; si la charge utile de votre prestataire diffère, placez un adaptateur léger en amont (une petite fonction qui traduit le webhook du prestataire en corps normalisé, le signe et le transmet). Le corps normalisé :

{
  "type": "paid",
  "licensee": "Acme Inc.",
  "product": "acme-app",
  "tier": "pro",
  "seats": 3,
  "expiryUtc": "2027-10-12T00:00:00Z",
  "orderRef": "order_8421",
  "email": "buyer@acme.com"
}
  • type pilote l’action : un événement paid (paid / order.paid / issue) génère une licence ; un événement refund / cancel / revoke la révoque.
  • product et tier sont la façon dont vous faites correspondre le produit/la variante de la boutique à un produit + une édition Keyright. Votre boutique vend un SKU ; vous décidez à quel slug product Keyright et à quel tier (édition) ce SKU correspond, et vous définissez ces champs dans l’événement (directement, ou dans la table de correspondance de votre adaptateur). Les sièges de l’édition, ses droits et toute politique de couverture sont appliqués automatiquement à l’émission, exactement comme pour une licence émise à la main — une clé Pro issue d’une boutique est donc indiscernable d’une clé émise dans le tableau de bord.
  • seats / expiryUtc surchargent les valeurs par défaut de l’édition quand le SKU l’exige (p. ex. un pack de 5 sièges, ou une durée annuelle).
  • orderRef est l’ancre d’idempotence (section suivante) — envoyez toujours l’identifiant de commande de votre boutique.
  • email est l’adresse à laquelle la clé est envoyée.

Un champ tenant dans le corps est ignoré sur la route par tenant — le slug de l’URL l’emporte toujours.

3. Ce que reçoit le client

Sur un événement paid, Keyright génère la licence et envoie la clé par e-mail à email (l’instance doit avoir un SMTP configuré pour que le courrier parte). L’acheteur colle cette clé dans votre application et l’active comme n’importe quelle autre licence — voir Premiers pas. Rien ne diffère dans l’activation, les sièges ou les droits parce que la clé provient d’une boutique.

Idempotence et réessais

Les boutiques réessaient les webhooks — sur un délai d’attente dépassé, un 5xx ou un incident chez le prestataire, vous recevrez le même événement plus d’une fois. Keyright rend paid idempotent par orderRef : la première livraison génère la licence, et chaque livraison ultérieure avec le même orderRef renvoie la licence existante au lieu d’émettre une seconde clé. C’est pourquoi orderRef doit être stable (l’identifiant de commande de votre boutique, pas un identifiant par tentative). L’endpoint répond 200 pour chaque issue traitée — émise, existe déjà, révoquée ou ignorée — de sorte qu’une boutique bien élevée voit un succès et cesse de réessayer.

Les événements de remboursement/annulation révoquent la licence que cette commande a générée ; l’application du client retombe en Free à son prochain rafraîchissement de lease (voir Révoquer, renouveler et gérer l’expiration). Un remboursement pour une commande jamais exécutée est sans effet (ignorée, toujours 200).

Dans votre code

Il n’y a aucun travail SDK côté application pour l’exécution des commandes — c’est un flux de serveur à serveur entre votre boutique et Keyright, et l’application du client ne voit jamais qu’une clé de licence ordinaire. Le seul code que vous pourriez écrire est l’adaptateur facultatif qui normalise le webhook de votre prestataire. Esquisse :

// Adaptateur minimal : traduire un événement du prestataire en corps normalisé et signé HMAC de Keyright.
var evt = new {
    type     = providerEvent.IsPaid ? "paid" : "refund",
    licensee = providerEvent.CustomerName,
    product  = MapToKeyrightProduct(providerEvent.Sku),   // votre SKU → slug de produit Keyright
    tier     = MapToKeyrightTier(providerEvent.Sku),      // votre SKU → édition
    seats    = providerEvent.Quantity,
    orderRef = providerEvent.OrderId,                     // l'ancre d'idempotence
    email    = providerEvent.CustomerEmail,
};
var body = JsonSerializer.SerializeToUtf8Bytes(evt);
var sig  = Convert.ToBase64String(new HMACSHA256(secretBytes).ComputeHash(body));   // sur les octets BRUTS
using var http = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Post, "https://keyright.delta1labs.com/webhooks/fulfill/acme")
    { Content = new ByteArrayContent(body) };
req.Content.Headers.ContentType = new("application/json");
req.Headers.Add("X-Keyright-Signature", sig);
await http.SendAsync(req);

Si votre prestataire peut envoyer directement un corps JSON personnalisé et un schéma de signature personnalisé (certains le peuvent), vous n’aurez peut-être pas besoin d’adaptateur du tout — faites-le pointer directement sur le webhook avec la forme normalisée ci-dessus.

Cloud ou auto-hébergé

  • La joignabilité est la seule vraie différence : le Cloud est public, donc une boutique atteint https://keyright.delta1labs.com/webhooks/fulfill/{slug} sans rien faire ; une instance auto-hébergée doit être joignable par la boutique à une URL vers laquelle elle peut envoyer un POST.
  • La route historique mono-tenant. Une instance auto-hébergée mono-tenant peut à la place utiliser la route globale du déploiement POST /webhooks/fulfill avec un KEYRIGHT_WEBHOOK_SECRET. Préférez la route par tenant /webhooks/fulfill/{slug} dès que l’instance a plus d’un tenant.
  • $BASE / $TOKEN / SMTP suivent la répartition habituelle ; l’étape d’envoi de la clé par e-mail exige que le courrier soit configuré sur l’instance qui génère la licence, quelle qu’elle soit.

Vérifier

  1. Faites tourner le secret du webhook et notez-le ; faites correspondre un SKU de boutique à product=acme-app, tier=pro.
  2. Envoyez un événement paid de test avec un email réel et un orderRef inédit, signé avec le secret. Attendez-vous à 200, une nouvelle licence dans Licenses, et la clé dans la boîte de réception.
  3. Idempotence : renvoyez exactement le même événement. Attendez-vous à 200 et à aucune seconde licence — la même clé revient.
  4. Mauvaise signature : envoyez le même corps avec une signature erronée. Attendez-vous à 401 et aucune licence.
  5. Remboursement : envoyez un événement refund pour cet orderRef. La licence passe à révoquée ; l’application du client retombe en Free au prochain rafraîchissement.

Voir aussi