Aller au contenu
Bondry

API de licence pour les auteurs de modules

Comment un module payant se licencie, la clé du manifeste, l'appel d'activation, le heartbeat et la politique de dégradation.

Un module payant est licencié par le même serveur avec lequel parle le core, en utilisant le même transport signé. Vous le déclarez dans le manifeste ; le kernel fait le reste.

La clé du manifeste#

JSON
"licensing": { "product_slug": "your-module" }

Sa présence signifie que le module est licencié : le panneau affiche un écran Licence pour lui, et le module peut demander si les updates sont autorisés. Son absence signifie que le module est gratuit et que rien n'est vérifié. Les modules fournis en standard avec le core (paiements, gamification) ne portent aucune clé licensing.

Le product_slug est ce sous quoi le serveur de licences connaît le produit, et c'est ce qui se retrouve dans la claim prd du token.

Activation#

Le membre saisit le numéro de série dans Modules > License. Le kernel appelle le serveur :

Code
POST /v1/activate
{ "serial": "...", "domain": "example.com", "host": "...", "app_version": "1.2.0" }

Un succès renvoie le token signé et le droit associé :

JSON
{
  "ok": true,
  "token": "<JWT RS256>",
  "license": { "serial": "...", "product": "your-module", "updates_until": 1789000000, "domain": "example.com" },
  "features": []
}

Un refus revient avec ok: false et une raison : invalid_serial, domain_in_use, revoked ou rate_limited. Il arrive en HTTP 200 ; un 4xx signifie que le serveur est indisponible, ce qui est une situation totalement différente.

Une licence de module s'active sur le même domaine de production que la licence du core.

Le transport signé#

Chaque appel à /v1/* porte quatre en-têtes :

Code
X-Bondry-Key:        l'identifiant de la clé partagée
X-Bondry-Timestamp:  heure unix, valide 300 secondes
X-Bondry-Nonce:      uuid v4, à usage unique
X-Bondry-Signature:  hex(hmac_sha256(secret, METHOD\nPATH\nTIMESTAMP\nNONCE\nsha256(body)))

Vous ne construisez jamais cela vous-même : le client de licence du kernel signe l'appel. C'est documenté ici pour que vous puissiez le reconnaître dans un log.

Le token#

RS256, vérifié localement avec la clé publique livrée avec Bondry. Claims : iss, sub (le numéro de série), prd (votre product slug), dom (le domaine activé), upd (la fin de la fenêtre de mises à jour), feat, iat, exp (30 jours) et jti.

Le token est stocké chiffré et ne quitte jamais l'installation.

Le heartbeat#

Quotidiennement, depuis le scheduler :

Code
POST /v1/heartbeat
{ "token": "...", "domain": "example.com", "app_version": "1.2.0" }

La réponse rafraîchit le token quand il est proche d'expirer et renvoie le updates_until actuel, plus des indicateurs pour révoqué et expiré, avec un message optionnel destiné à l'humain, affiché dans le panneau.

Si le serveur est injoignable, rien ne se dégrade pendant quatorze jours. Votre module ne doit pas traiter une erreur réseau comme un refus.

La politique de dégradation#

C'est une règle du produit, pas une suggestion.

État Ce que votre module peut faire
Actif Tout
Fenêtre de mises à jour expirée Tout ce que l'acheteur a déjà. Updates indisponibles.
Révoqué Tout ce que l'acheteur a déjà. Un avis permanent dans le panneau. Updates indisponibles.
Hors ligne au-delà de la période de grâce Comme pour l'expiration, réversible à la reconnexion

Ne bloquez jamais le contenu ni les fonctionnalités de base. Une licence contrôle les updates et les extras marqués comme tels, pas le droit de continuer à utiliser ce qui a été acheté. Un module qui enferme un acheteur hors de ses propres données ne sera pas accepté, et c'est le moyen le plus rapide de provoquer une rétrofacturation.

Interroger la licence#

Interrogez le kernel, n'appelez jamais le serveur vous-même :

PHP
if (\App\Kernel\License\ModuleLicenses::allowsUpdates('your-module')) {
    // Proposer l'update.
}

Tester sans serveur#

Pendant le développement, le client de licence peut tourner sans serveur configuré. Il enregistre l'intention et reste déverrouillé, afin que vous ne soyez jamais bloqué par l'accès réseau pendant l'écriture d'un module.