Documentation J+SERVICES Guides Référence API

Contrat API — Activation d’une licence par le client

Destinataire : développeur de l’application mobile Base : https://live.jmoai.net · Authentification : Authorization: Bearer <jwt client> Dernière mise à jour : 2026-09-13

Ce contrat décrit ce que le serveur garantit au moment où un client active une licence, et ce qu’il ne garantit pas. Il existe parce qu’un client a vécu l’inverse : l’application annonçait « activé avec succès » et rien n’arrivait sur son compte.


1. POST /v1/licenses/activate

Active une licence par sa CLÉ (voucher JPS-…, clé de licence JSVC-…, clé Chario).

// Corps
{ "code": "JSVC-MIKHMOAIPRO-02386-06BF0" }
// 200 — première activation, ou activation d'une licence encore réclamable
{
  "success": true,
  "already_active": false,
  "message": "Licence activée avec succès !",
  "next_step": "Provisionnez votre routeur depuis l’application pour ouvrir l’accès.",
  "data": { "provider": "local", "plan_code": "MIKHMOAI_PRO", "expires_at": "2027-09-12T…" },
}
// 200 — la licence était DÉJÀ portée par ce compte (voir §3)
{
  "success": true,
  "already_active": true,
  "message": "Cette licence est déjà active sur votre compte — services vérifiés.",
  "next_step": "Provisionnez votre routeur depuis l’application pour ouvrir l’accès.",
  "data": { "…": "…" },
}

Ce que le serveur fait, à chaque appel — y compris sur une licence déjà active

  1. Droits : la carte du plan est redérivée du catalogue et écrite sur la licence (license_entitlements). Aucun droit n’est recopié à la main.
  2. Boutique : créée si le plan l’accorde (web_store_access ou voucher_sales), et rattachée à cette licence-là.
  3. RADIUS : accès créé si le plan l’accorde (radius_hotspot_management). Un accès déjà vivant n’est pas reprovisionné — la création prolonge l’échéance.
  4. VPN : emplacement attribué si le plan accorde vpn_remote_management / vpn_slots.
  5. État du compte : plan et échéance du compte suivent la meilleure licence active.

L’appel est idempotent : le rejouer ne duplique rien et ne prolonge rien.

Ce que le serveur NE garantit PAS


2. POST /v1/licenses/claim et /claim-guest

Mêmes garanties que §1. Différences : la licence passe de PENDING_DISTRIBUTION à ACTIVE, le titulaire devient le compte réclamant, et claim-guest rend une session (Set-Cookie) pour auto-connexion.

// 200
{
  "success": true,
  "message": "Licence activée et rattachée à votre compte.",
  "already_claimed": false,
  "data": { "id": "LOT-822DD9_001", "plan_code": "MIKHMOAI_PRO", "expires_at": "…" },
}

Nouveau champ (2026-09-13) sur les deux routes, renseigné quand already_claimed / already_active vaut true :

"services": {
  "entitlements": "A_JOUR",     // ou "NON_FAIT"
  "boutique": "LIVREE",          // "DEJA_ACTIVE" | "NON_CONCERNEE" | "ECHEC" | null
  "radius": "DEJA_ACTIF",        // "LIVRE" | "NON_CONCERNEE" | "ECHEC" | null
  "vpn": "VERIFIE",              // "NON_CONCERNEE" | "ECHEC" | null
  "etat_commercial": "A_JOUR",
  "echecs": []                   // noms des services non délivrés, à signaler au support
}

services est informative : l’application peut l’afficher en cas d’échec (« certains services n’ont pas pu être vérifiés, notre équipe est prévenue »). Elle ne doit pas en déduire un état local : les droits se lisent toujours côté serveur.


3. Ce qui n’est PAS une erreur

Situation Réponse Pourquoi ce n’est pas une erreur
La licence est déjà active sur ce compte 200, already_active: true Rien à réactiver. Le serveur revérifie et re-livre ce qui manque, puis le dit.
La licence active appartient à un autre compte 400, code ALREADY_CLAIMED_BY_OTHER Refus définitif, avec le motif. La clé ne doit pas circuler.
success: true mais aucun service nouveau visible — Le compte était déjà couvert. Aucun écran d’erreur ne doit s’afficher.
Le plan n’accorde pas la boutique ou RADIUS — Ce n’est pas un échec : le service n’était pas vendu.

4. Déviation assumée — révocation d’un droit

Le serveur résout les droits d’un client par UNION des licences ACTIVES non expirées : un droit accordé par une licence ne peut pas être annulé par le enabled: false d’une AUTRE licence du même compte. Avant le 2026-09-13, l’inverse était vrai et 84 clients perdaient des droits payés (458 couples client×droit), dans un ordre qui dépendait de la lecture en base.

Pour révoquer un droit à un client, on révoque ou on expire la licence qui l’accorde (licenses.status), on n’écrit pas un drapeau sur une licence voisine. Cette section existe pour que personne ne « rétablisse » l’ancien comportement en croyant corriger un oubli.