@digitea/licensing
v1.2.2
Published
Official lightweight client for the Digitea Runtime Licensing API
Downloads
1,162
Maintainers
Readme
@digitea/licensing
Le SDK officiel pour activer et vérifier les licences Digitea dans une application Node.js ou Electron.
Il permet à une application Node.js ou Electron de :
- activer une licence sur une installation ;
- vérifier qu’une licence est toujours active ;
- désactiver une installation lorsque le produit l’autorise ;
- distinguer clairement une erreur métier d’une panne réseau ;
- générer et persister un identifiant d’instance stable.
Chaque validation consulte Digitea. Une panne réseau reste une erreur technique et n’accorde jamais automatiquement l’accès.
Important : trois interfaces différentes
| Interface | Utilisation | Identifiants |
|---|---|---|
| @digitea/licensing | Logiciel distribué au client | productId, licenseKey, instanceId |
| Developer API | Serveur de votre application | clé dgt_sk_live_... |
| Webhooks | Digitea vers votre serveur | secret whsec_... |
Une clé Developer API ou un secret webhook ne doit jamais être intégré dans Electron, un exécutable, une application mobile, un navigateur ou un dépôt public.
Installation
npm install @digitea/licensingPrérequis :
- Node.js 18 ou supérieur ;
- projet ESM ou outillage capable d’importer un package ESM ;
- accès HTTPS à
https://api.getdigitea.com.
Le package ne possède aucune dépendance runtime.
Démarrage rapide
import {
DigiteaLicensing,
getOrCreateInstanceId,
type InstanceStorage,
} from "@digitea/licensing";
const instanceStorage: InstanceStorage = {
async get() {
return settings.get("digitea.instanceId") ?? null;
},
async set(value) {
await settings.set("digitea.instanceId", value);
},
};
const licensing = new DigiteaLicensing({
productId: "prd_votreproduit",
});
const instanceId = await getOrCreateInstanceId(instanceStorage);
const activation = await licensing.activate({
licenseKey,
instanceId,
instanceName: "PC principal",
});
console.log(activation.status); // ACTIVE
const validation = await licensing.validate({
licenseKey,
instanceId,
});
if (validation.valid) {
startApplication();
}settings représente ici le stockage persistant choisi par votre application. Pour une session de renouvellement, le SDK genere automatiquement une Idempotency-Key et la reutilise sur chaque retry. Pour reprendre la meme operation apres une interruption, fournissez la meme valeur idempotencyKey. Une nouvelle cle represente une nouvelle session.
Le SDK n’impose ni fichier, ni base locale, ni trousseau système.
Configuration
const licensing = new DigiteaLicensing({
productId: "prd_votreproduit",
baseUrl: "https://api.getdigitea.com",
timeoutMs: 10_000,
retry: {
maxRetries: 2,
baseDelayMs: 250,
maxDelayMs: 5_000,
},
});| Option | Requise | Valeur par défaut | Description |
|---|---:|---:|---|
| productId | oui | — | Identifiant public de votre logiciel sur Digitea |
| baseUrl | non | https://api.getdigitea.com | URL de l’API Runtime |
| timeoutMs | non | 10000 | Timeout de chaque tentative HTTP |
| retry.maxRetries | non | 2 | Nombre maximal de nouvelles tentatives |
| retry.baseDelayMs | non | 250 | Délai initial du backoff |
| retry.maxDelayMs | non | 5000 | Plafond du backoff |
En dehors de localhost, une baseUrl non HTTPS est refusée.
Cycle d’utilisation recommandé
Premier lancement
→ charger ou créer instanceId
→ demander la clé au client
→ activate()
Lancements suivants
→ charger le même instanceId
→ validate()
→ démarrer uniquement si la validation réussit
Retrait de l’appareil
→ deactivate()Ne générez pas un nouvel instanceId à chaque démarrage : chaque nouvelle valeur représente une nouvelle installation et peut consommer une place d’activation.
Activer une licence
const result = await licensing.activate({
licenseKey: userLicenseKey,
instanceId,
instanceName: "MacBook de Marie",
});Résultat :
interface ActivationResult {
valid: true;
status: "ACTIVE";
expiresAt: string | null;
activation: {
publicId: string;
activatedAt: string;
};
activeActivations: number;
activationLimit: number;
idempotent: boolean;
}L’activation est idempotente : rappeler activate() pour une instance déjà active ne consomme pas une place supplémentaire.
Erreurs métier possibles :
LICENSE_INVALIDLICENSE_EXPIREDLICENSE_SUSPENDEDLICENSE_REVOKEDINVALID_PRODUCTACTIVATION_LIMIT_REACHED
Valider une licence
const result = await licensing.validate({
licenseKey: userLicenseKey,
instanceId,
});
if (result.valid && result.status === "ACTIVE") {
enableLicensedFeatures();
}Résultat :
interface ValidationResult {
valid: true;
status: "ACTIVE";
expiresAt: string | null;
}La validation vérifie simultanément :
- la clé ;
- le produit concerné ;
- le statut de la licence ;
- sa date d’expiration ;
- l’existence d’une activation active pour cette instance.
Une instance qui n’a jamais été activée reçoit INSTANCE_NOT_ACTIVATED.
Désactiver une instance
const result = await licensing.deactivate({
licenseKey: userLicenseKey,
instanceId,
});Résultat :
interface DeactivationResult {
valid: true;
status: "DEACTIVATED";
activeActivations: number;
activationLimit: number;
idempotent: boolean;
}La désactivation doit être autorisée dans la configuration du produit. Sinon l’API renvoie DEACTIVATION_NOT_ALLOWED.
Une désactivation déjà effectuée reste idempotente.
Générer et conserver instanceId
Pour générer uniquement une valeur :
import { generateInstanceId } from "@digitea/licensing";
const instanceId = generateInstanceId();Pour utiliser un stockage personnalisé :
import {
getOrCreateInstanceId,
type InstanceStorage,
} from "@digitea/licensing";
const storage: InstanceStorage = {
async get() {
return persistentStore.read("instanceId");
},
async set(value) {
await persistentStore.write("instanceId", value);
},
};
const instanceId = await getOrCreateInstanceId(storage);Propriétés importantes :
- Digitea ne demande aucun fingerprint matériel invasif ;
- seul le digest de
instanceIdest conservé côté serveur ; - l’identifiant doit rester stable entre les redémarrages ;
- le développeur reste responsable de sa persistance ;
- restaurer le stockage permet de conserver la même identité logique.
Gestion structurée des erreurs
Toutes les erreurs du SDK utilisent DigiteaLicensingError.
import {
DigiteaLicensingError,
type LicensingErrorCode,
} from "@digitea/licensing";
try {
await licensing.validate({ licenseKey, instanceId });
} catch (error) {
if (!(error instanceof DigiteaLicensingError)) {
throw error;
}
console.error({
code: error.code,
httpStatus: error.httpStatus,
retryable: error.retryable,
});
switch (error.code) {
case "LICENSE_REVOKED":
showLicenseRevoked();
break;
case "LICENSE_EXPIRED":
showRenewalScreen();
break;
case "NETWORK_ERROR":
case "TIMEOUT":
showTemporaryConnectionProblem();
break;
default:
showLicensingError(error.code);
}
}La classe expose :
| Champ | Type | Description |
|---|---|---|
| code | chaîne typée | Code métier, HTTP ou client |
| message | string | Message lisible sans clé de licence |
| httpStatus | number \| null | Statut HTTP lorsque disponible |
| retryable | boolean | Indique une indisponibilité potentiellement temporaire |
Codes d’erreur serveur
| Code | Signification | Retry automatique |
|---|---|---:|
| LICENSE_INVALID | Clé inconnue ou invalide | non |
| LICENSE_EXPIRED | Licence arrivée à expiration | non |
| LICENSE_SUSPENDED | Licence suspendue par le vendeur | non |
| LICENSE_REVOKED | Licence révoquée définitivement | non |
| INSTANCE_NOT_ACTIVATED | Instance non activée ou désactivée | non |
| ACTIVATION_LIMIT_REACHED | Nombre maximal d’installations atteint | non |
| DEACTIVATION_NOT_ALLOWED | Désactivation interdite par le produit | non |
| INVALID_PRODUCT | Licence utilisée avec un autre produit | non |
| INVALID_REQUEST | Requête malformée | non |
| UNAUTHORIZED | Authentification refusée | non |
| FORBIDDEN | Opération interdite | non |
| RATE_LIMITED | Limite de requêtes atteinte | oui |
| TEMPORARILY_UNAVAILABLE | Service temporairement indisponible | oui |
Erreurs propres au client SDK :
NETWORK_ERRORTIMEOUTABORTEDINVALID_RESPONSE
Timeout, AbortSignal et retries
Chaque méthode accepte un AbortSignal optionnel :
const controller = new AbortController();
const validationPromise = licensing.validate(
{ licenseKey, instanceId },
{ signal: controller.signal },
);
cancelButton.addEventListener("click", () => controller.abort());
await validationPromise;Le SDK peut réessayer uniquement :
- une erreur réseau temporaire ;
- un HTTP
429; - certains HTTP
5xx; TEMPORARILY_UNAVAILABLE.
Il respecte Retry-After lorsqu’il est fourni.
Le SDK ne réessaie jamais automatiquement les refus définitifs comme une licence invalide, expirée, suspendue, révoquée ou une limite d’activation atteinte.
Validation en ligne et comportement offline
Digitea ne définit actuellement aucune politique offline permissive.
Une erreur réseau :
- ne transforme jamais une licence invalide en licence valide ;
- ne transforme jamais une licence valide en licence invalide ;
- reste une erreur technique distincte avec
retryable: true.
Votre application doit présenter un état temporairement indisponible adapté à son usage. N’inventez pas une autorisation locale permanente à partir d’un échec réseau.
Intégration Electron
Utilisez le SDK dans le process principal, jamais dans le renderer.
// main/licensing.ts
import { ipcMain } from "electron";
import { DigiteaLicensing } from "@digitea/licensing";
const licensing = new DigiteaLicensing({
productId: "prd_votreproduit",
});
ipcMain.handle("licensing:validate", async (_event, input) => {
const result = await licensing.validate(input);
return {
valid: result.valid,
status: result.status,
expiresAt: result.expiresAt,
};
});Exposez au renderer une API IPC minimale via contextBridge. Ne transmettez pas de clé Developer API et ne désactivez pas l’isolation de contexte pour faciliter l’intégration.
Le SDK ne stocke jamais la licenseKey. Si votre application choisit de la conserver localement, utilisez un mécanisme adapté à sa plateforme et ne l’écrivez pas dans les logs, crash reports ou analytics.
Utilisation Node.js
import { DigiteaLicensing } from "@digitea/licensing";
const licensing = new DigiteaLicensing({
productId: process.env.DIGITEA_PRODUCT_ID!,
});
const result = await licensing.validate({
licenseKey: process.env.CUSTOMER_LICENSE_KEY!,
instanceId: process.env.APP_INSTANCE_ID!,
});Node.js 18+ fournit nativement fetch, AbortController et Web Crypto.
Runtime API sans SDK
Le SDK appelle exclusivement :
POST /v1/licenses/activatePOST /v1/licenses/validatePOST /v1/licenses/deactivatePOST /v1/licenses/renewal-sessionPOST /v1/licenses/renewal-session/status
Exemple curl :
curl https://api.getdigitea.com/v1/licenses/validate \
--request POST \
--header 'Content-Type: application/json' \
--data '{
"productId": "prd_votreproduit",
"licenseKey": "<clé-de-licence-client>",
"instanceId": "identifiant-installation-stable"
}'La Runtime API ne nécessite jamais :
- une clé
dgt_sk_live_...; - un secret
whsec_...; - une session vendeur ;
- un compte vendeur.
Developer API et webhooks
La Developer API permet à votre serveur de lister, suspendre, réactiver ou révoquer les licences de vos logiciels. Elle utilise une clé serveur dgt_sk_live_....
Les webhooks Licensing notifient notamment :
license.issuedlicense.activatedlicense.deactivatedlicense.suspendedlicense.reactivatedlicense.revokedlicense.expiredlicense.renewed
Les webhooks sont signés avec un secret séparé whsec_..., un timestamp et HMAC-SHA256. Ils ne sont pas traités par ce SDK Runtime.
Consultez le portail développeur Digitea pour la référence API, la vérification HMAC et les exemples prêts à adapter.
Exports publics
DigiteaLicensing
DigiteaLicensingConfig
ActivateInput
ActivationResult
ValidateInput
ValidationResult
DeactivateInput
DeactivationResult
LicenseStatus
LicensingErrorCode
LicensingClientErrorCode
DigiteaLicensingError
InstanceStorage
RequestOptions
RetryConfig
generateIdempotencyKey
CreateRenewalSessionInput
RenewalStatusInput
RenewalSessionResult
RenewalStatusResult
generateInstanceIdLes statuts et codes d’erreur exportés correspondent au contrat public de l’API Digitea.
Sécurité
- N’intégrez jamais une Developer API key dans une application distribuée.
- Ne journalisez jamais
licenseKey. - Ne placez jamais de secret webhook dans le client.
- Utilisez uniquement HTTPS en production.
- Persistez
instanceId, mais ne l’utilisez pas comme secret. - Ne construisez pas un fingerprint matériel invasif.
- Gardez les erreurs réseau distinctes des refus de licence.
- Ne mettez pas en cache les réponses Runtime dans un CDN.
Le SDK évite volontairement d’inclure la clé de licence dans ses messages d’erreur.
TypeScript
Le package fournit directement ses déclarations :
import type {
ActivateInput,
ActivationResult,
DigiteaLicensingConfig,
LicenseStatus,
LicensingErrorCode,
} from "@digitea/licensing";Aucun paquet @types/* supplémentaire n’est nécessaire.
Support navigateurs
Le client repose sur Fetch et peut être compilé par certains bundlers navigateur. Cependant, les appels restent soumis à la politique CORS de Digitea.
Les cibles officiellement recommandées pour cette première version sont :
- Node.js ;
- process principal Electron.
Ne placez jamais une Developer API key dans du JavaScript navigateur.
FAQ
La licence est-elle liée au matériel ?
Non. Digitea utilise un identifiant stable fourni par l’application et n’impose aucun fingerprint matériel.
Le SDK conserve-t-il la clé de licence ?
Non. Il utilise la clé uniquement pour la requête en cours.
Puis-je réutiliser instanceId après redémarrage ?
Oui, c’est précisément son objectif. Il doit être persisté.
Pourquoi validate renvoie INSTANCE_NOT_ACTIVATED ?
L’instance doit d’abord appeler activate(), ou elle a déjà été désactivée.
Une licence perpétuelle doit-elle être validée ?
Oui. Perpétuelle signifie sans date d’expiration, pas sans contrôle de suspension ou révocation.
Une validation déclenche-t-elle un webhook ?
Non. Les validations fréquentes ne génèrent pas de webhook.
Puis-je utiliser le SDK hors ligne ?
Aucune politique offline permissive n’est fournie actuellement. Une indisponibilité réseau reste une erreur technique.
Ressources
Licence
MIT © Digitea
Renouveler une licence limitée
validate() expose renewable. Quand cette valeur est vraie, votre application peut demander une session de paiement Digitea :
const renewal = await licensing.createRenewalSession({
licenseKey,
instanceId,
});
await openInBrowser(renewal.checkoutUrl);Le SDK n’effectue aucun paiement local et ne modifie aucune date. Après confirmation, appelez de nouveau validate() pour obtenir le nouvel expiresAt. Les licences perpétuelles, suspendues ou révoquées ne peuvent pas ouvrir de session.
Responsabilités et identifiants
Le vendeur conserve le productId public de son logiciel dans sa configuration. Le client fournit la licenseKey complète après son achat. Le SDK génère et persiste un instanceId stable, puis l’envoie avec la clé pour activate(), validate(), deactivate() ou createRenewalSession().
Le licenseId public permet au vendeur de retrouver une licence dans la Developer API, mais ne permet pas de valider une installation. La Developer API ne liste pas les produits et ne révèle jamais les clés complètes ; les releases et artifacts se gèrent dans le dashboard vendeur.
