Passer au contenu principal
Ce guide vous montre comment utiliser l’API Licence de Chariow pour implémenter un paywall dans votre application SaaS. Que vous construisiez avec Lovable, Cursor, Bolt ou tout autre assistant de codage IA, ce guide fournit tout ce dont vous avez besoin, y compris des prompts IA prêts à l’emploi.
Ce guide suppose que vous avez une boutique Chariow avec un produit de type Licence créé. Si vous n’en avez pas encore, créez votre produit licence d’abord.

Qu’est-ce qu’un Paywall par Licence ?

Un paywall par licence restreint l’accès à votre application SaaS (ou à des fonctionnalités spécifiques) jusqu’à ce que l’utilisateur fournisse une clé de licence valide. C’est idéal pour :
  • Applications de bureau : Applications Electron, logiciels natifs, outils CLI
  • Applications web : Plateformes SaaS, tableaux de bord admin, outils premium
  • Applications mobiles : Applications iOS/Android avec fonctionnalités premium
  • Accès API : Contrôle d’accès API basé sur la validité de la licence

Comment ça fonctionne

1

Le client achète la licence

Le client achète votre produit licence sur Chariow. Une clé de licence unique est automatiquement générée (ex: ABC-123-XYZ-789).
2

Le client entre sa clé de licence

Dans votre application, le client saisit sa clé de licence dans un écran de paramètres ou d’activation.
3

Votre application valide la clé

Votre application appelle l’API Chariow pour valider la clé de licence et vérifier son statut.
4

Accès accordé ou refusé

Selon la réponse de l’API (is_active, is_expired), votre application accorde ou refuse l’accès.
5

Optionnel : Activation sur l'appareil

Pour les licences limitées par appareil, activez la licence pour suivre et limiter l’utilisation.

Points d’accès API nécessaires

Point d’accèsMéthodeObjectif
/v1/licenses/{key}GETValider une clé de licence et vérifier le statut
/v1/licenses/{key}/activatePOSTActiver la licence sur un appareil
Votre clé API (sk_live_...) doit être conservée côté serveur uniquement. Ne l’exposez jamais dans le code côté client.

Modèles d’architecture

Modèle 1 : Validation côté serveur (Recommandé)

Votre backend valide les licences et contrôle l’accès. Idéal pour les applications web.
Flux de validation de licence côté serveur

Modèle 2 : Validation Serverless/Edge

Validez les licences à la périphérie avec des fonctions serverless. Bon pour les sites statiques.
Flux de validation de licence serverless

Modèle 3 : Application de bureau avec validation périodique

Les applications de bureau valident au démarrage et périodiquement. Inclut une période de grâce hors ligne.
Flux de validation de licence application bureau avec cache local

Guide d’implémentation

Étape 1 : Créer une route API pour la validation de licence

Votre backend doit exposer un point d’accès que votre frontend appelle :
// /api/validate-license.js (route API Next.js)
export default async function handler(req, res) {
  const { licenseKey } = req.body;

  if (!licenseKey) {
    return res.status(400).json({ valid: false, error: 'Clé de licence requise' });
  }

  try {
    const response = await fetch(
      `https://api.chariow.com/v1/licenses/${encodeURIComponent(licenseKey)}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.CHARIOW_API_KEY}`
        }
      }
    );

    if (!response.ok) {
      return res.status(200).json({ valid: false, error: 'Clé de licence invalide' });
    }

    const { data } = await response.json();

    // Vérifier la validité de la licence
    if (!data.is_active) {
      return res.status(200).json({
        valid: false,
        error: 'La licence n\'est pas active'
      });
    }

    if (data.is_expired) {
      return res.status(200).json({
        valid: false,
        error: 'La licence a expiré'
      });
    }

    // La licence est valide
    return res.status(200).json({
      valid: true,
      license: {
        status: data.status,
        expiresAt: data.expires_at,
        activationsRemaining: data.activations?.remaining
      }
    });

  } catch (error) {
    console.error('Erreur de validation de licence:', error);
    return res.status(500).json({ valid: false, error: 'Échec de la validation' });
  }
}

Étape 2 : Créer le composant de saisie de licence

// components/LicenseGate.jsx
import { useState, useEffect } from 'react';

export function LicenseGate({ children }) {
  const [isValidated, setIsValidated] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [licenseKey, setLicenseKey] = useState('');
  const [error, setError] = useState('');

  // Vérifier la licence stockée au montage
  useEffect(() => {
    const storedKey = localStorage.getItem('license_key');
    if (storedKey) {
      validateLicense(storedKey);
    } else {
      setIsLoading(false);
    }
  }, []);

  const validateLicense = async (key) => {
    setIsLoading(true);
    setError('');

    try {
      const response = await fetch('/api/validate-license', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ licenseKey: key })
      });

      const data = await response.json();

      if (data.valid) {
        localStorage.setItem('license_key', key);
        setIsValidated(true);
      } else {
        setError(data.error || 'Licence invalide');
        localStorage.removeItem('license_key');
      }
    } catch (err) {
      setError('Échec de la validation de licence');
    } finally {
      setIsLoading(false);
    }
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    validateLicense(licenseKey);
  };

  if (isLoading) {
    return <div className="license-loading">Validation de la licence...</div>;
  }

  if (isValidated) {
    return children;
  }

  return (
    <div className="license-gate">
      <h2>Entrez votre clé de licence</h2>
      <p>Veuillez saisir votre clé de licence pour accéder à l'application.</p>

      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={licenseKey}
          onChange={(e) => setLicenseKey(e.target.value)}
          placeholder="ABC-123-XYZ-789"
        />
        <button type="submit">Activer</button>
      </form>

      {error && <p className="error">{error}</p>}

      <p className="help-text">
        Vous n'avez pas de licence ? <a href="https://votreboutique.chariow.com">Achetez-en une ici</a>
      </p>
    </div>
  );
}

Étape 3 : Envelopper votre application avec le contrôle de licence

// App.jsx
import { LicenseGate } from './components/LicenseGate';
import { Dashboard } from './components/Dashboard';

function App() {
  return (
    <LicenseGate>
      <Dashboard />
    </LicenseGate>
  );
}

Activation sur appareil (Optionnel)

Si votre licence a des activations limitées, activez sur l’appareil :
// /api/activate-license.js
export default async function handler(req, res) {
  const { licenseKey, deviceId } = req.body;

  const response = await fetch(
    `https://api.chariow.com/v1/licenses/${encodeURIComponent(licenseKey)}/activate`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.CHARIOW_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        device_identifier: deviceId
      })
    }
  );

  const data = await response.json();

  if (!response.ok) {
    return res.status(400).json({
      success: false,
      error: data.message
    });
  }

  return res.status(200).json({
    success: true,
    activationsRemaining: data.data.activations?.remaining
  });
}

Prompts IA pour l’implémentation

Utilisez ces prompts avec Lovable, Cursor, Bolt ou tout assistant de codage IA pour implémenter rapidement le paywall par licence.

Prompt 1 : Paywall de licence basique (React + Next.js)

Créer un système de paywall par licence pour mon application Next.js avec ces exigences :

1. Créer une route API à /api/validate-license qui :
   - Accepte les requêtes POST avec { licenseKey: string }
   - Appelle l'API Chariow : GET https://api.chariow.com/v1/licenses/{licenseKey}
   - Utilise l'authentification Bearer token avec la variable d'environnement CHARIOW_API_KEY
   - Retourne { valid: true/false, error?: string, license?: object }
   - Vérifie les champs is_active et is_expired de la réponse

2. Créer un composant LicenseGate qui :
   - Affiche un formulaire de saisie de clé de licence quand non validé
   - Stocke la clé de licence valide dans localStorage
   - Vérifie localStorage au montage pour auto-valider les utilisateurs récurrents
   - Affiche les enfants uniquement quand la licence est valide
   - Affiche un état de chargement pendant la validation
   - Affiche les messages d'erreur pour les licences invalides

3. Styliser le formulaire de licence avec Tailwind CSS :
   - Centrer le formulaire sur la page
   - Utiliser un style propre et professionnel
   - Inclure un lien pour acheter une licence

Variable d'environnement nécessaire : CHARIOW_API_KEY (clé API boutique)

Prompt 2 : Paywall de licence avec activation d’appareil

Étendre le système de paywall par licence avec l'activation d'appareil :

1. Générer un identifiant d'appareil unique :
   - Pour les apps web : combiner user agent + résolution d'écran + fuseau horaire
   - Hasher le résultat pour créer un ID d'appareil cohérent
   - Stocker l'ID d'appareil dans localStorage

2. Créer le point d'accès /api/activate-license :
   - Accepte POST avec { licenseKey, deviceId }
   - Appelle l'API Chariow : POST https://api.chariow.com/v1/licenses/{key}/activate
   - Corps : { device_identifier: deviceId }
   - Gérer l'erreur "Limite d'activation atteinte" gracieusement

3. Mettre à jour le composant LicenseGate :
   - Après validation de la licence, appeler le point d'activation
   - Afficher le nombre d'activations restantes à l'utilisateur
   - Gérer les erreurs d'activation avec des messages conviviaux

4. Ajouter une section "Gérer les appareils" affichant :
   - Nombre d'activations actuel
   - Nombre maximum d'activations autorisées
   - Option pour désactiver l'appareil actuel

Prompt 3 : Licence basée sur les fonctionnalités

Implémenter une licence à fonctionnalités contrôlées où différents niveaux de licence débloquent différentes fonctionnalités :

1. Créer un hook useLicense qui :
   - Valide la licence au chargement de l'app
   - Retourne { isValid, tier, features, expiresAt }
   - Le niveau est déterminé par le nom du produit ou les métadonnées de Chariow

2. Créer un composant FeatureGate :
   - Accepte la prop requiredTier (ex : "pro", "enterprise")
   - Affiche une invite de mise à niveau si le niveau de l'utilisateur est insuffisant
   - Affiche les enfants si l'exigence de niveau est satisfaite

3. Exemple d'utilisation :
   <FeatureGate requiredTier="pro">
     <AdvancedAnalytics />
   </FeatureGate>

4. Créer une modale de mise à niveau qui :
   - Affiche le niveau actuel vs requis
   - Liens vers le checkout Chariow pour le produit de mise à niveau
   - Inclut une comparaison des fonctionnalités

Prompt 4 : Validation de licence compatible hors ligne

Ajouter le support hors ligne au système de licence :

1. Mettre en cache le résultat de validation de licence localement :
   - Stocker l'horodatage de validation
   - Stocker la date d'expiration de la licence
   - Stocker l'objet licence complet

2. Implémenter la logique de validation :
   - Si en ligne : valider avec l'API Chariow
   - Si hors ligne : vérifier la validation en cache (valide pendant 7 jours)
   - Si le cache a expiré : afficher un avertissement hors ligne mais permettre un accès limité

3. Ajouter une re-validation périodique :
   - Re-valider toutes les 24 heures quand en ligne
   - Mettre à jour le cache lors d'une validation réussie
   - Gérer les erreurs réseau gracieusement

4. Afficher un indicateur de statut de licence :
   - Vert : validé en ligne aujourd'hui
   - Jaune : utilisation de la validation en cache
   - Rouge : cache expiré, connexion nécessaire

Prompt 5 : Implémentation complète pour application Electron

Créer un système de licence pour une application de bureau Electron :

1. Gestionnaire de licence dans le processus principal :
   - Stocker la licence dans electron-store (chiffré)
   - Valider au lancement de l'app
   - Re-valider toutes les 24 heures
   - Envoyer le statut de validation au renderer via IPC

2. Script preload :
   - Exposer des APIs de licence sécurisées au renderer
   - licenseAPI.validate(key)
   - licenseAPI.getStatus()
   - licenseAPI.activate()

3. UI de licence dans le renderer :
   - Écran de saisie de licence au premier lancement
   - Statut de licence dans les paramètres
   - Option "Gérer les activations"

4. Identifiant d'appareil :
   - Utiliser le package machine-id pour un ID d'appareil cohérent
   - Passer au point d'activation Chariow

5. Gestion hors ligne :
   - Mettre en cache la validation pendant 7 jours
   - Afficher l'indicateur "Mode hors ligne"
   - Bloquer l'app après expiration du cache

Prompt 6 : Intégration Supabase

Intégrer la validation de licence avec l'authentification Supabase :

1. Créer une fonction edge Supabase à /validate-license :
   - Vérifier que l'utilisateur est authentifié
   - Stocker les licences validées dans une table 'user_licenses'
   - Lier la licence à l'ID utilisateur

2. Schéma de base de données :
   CREATE TABLE user_licenses (
     id UUID PRIMARY KEY,
     user_id UUID REFERENCES auth.users,
     license_key TEXT NOT NULL,
     validated_at TIMESTAMP,
     expires_at TIMESTAMP,
     status TEXT
   );

3. Hook React useUserLicense :
   - Vérifier si l'utilisateur actuel a une licence valide
   - Synchroniser le statut de licence à la connexion
   - Gérer les scénarios multi-appareils

4. Row Level Security :
   - Les utilisateurs ne peuvent voir que leurs propres licences
   - Activer les abonnements realtime pour les mises à jour de statut

Bonnes pratiques de sécurité

N’exposez jamais votre clé API Chariow dans le code côté client. Validez toujours les licences via votre backend.

À faire

  • Stocker les clés API côté serveur dans les variables d’environnement
  • Valider côté backend avant d’accorder l’accès
  • Mettre en cache les résultats de validation pour réduire les appels API
  • Implémenter une limitation de débit sur votre point de validation
  • Utiliser HTTPS pour toutes les communications API
  • Journaliser les tentatives de validation pour l’audit de sécurité

À ne pas faire

  • Ne pas faire confiance à la validation côté client seule - elle peut être contournée
  • Ne pas stocker l’objet licence complet dans un localStorage accessible
  • Ne pas ignorer la validation pour les utilisateurs “de confiance”
  • Ne pas coder en dur les clés de licence dans votre application
  • Ne pas exposer des messages d’erreur détaillés qui pourraient aider les attaquants

Tester votre intégration

Scénarios de test

ScénarioComportement attendu
Licence valide et activeAccès accordé
Clé de licence invalideAfficher erreur “Licence invalide”
Licence expiréeAfficher message “Licence expirée” avec lien de renouvellement
Licence révoquéeAfficher “Licence révoquée” - contacter le support
Erreur réseauAfficher le résultat en cache ou avertissement hors ligne
Limite d’activation atteinteAfficher “Trop d’appareils” avec options de gestion

Clés de licence de test

Pendant le développement, créez des produits de test dans votre boutique Chariow avec :
  • Des licences de test gratuites pour le développement
  • Des périodes d’expiration courtes pour tester la gestion d’expiration
  • Des activations limitées pour tester les limites d’appareils

Exemple complet : Next.js App Router

Voici une implémentation complète utilisant Next.js App Router :
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  const { licenseKey } = await request.json();

  if (!licenseKey) {
    return NextResponse.json(
      { valid: false, error: 'Clé de licence requise' },
      { status: 400 }
    );
  }

  try {
    const response = await fetch(
      `https://api.chariow.com/v1/licenses/${encodeURIComponent(licenseKey)}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.CHARIOW_API_KEY}`
        }
      }
    );

    if (!response.ok) {
      return NextResponse.json({ valid: false, error: 'Clé de licence invalide' });
    }

    const { data } = await response.json();

    if (!data.is_active) {
      return NextResponse.json({ valid: false, error: 'La licence n\'est pas active' });
    }

    if (data.is_expired) {
      return NextResponse.json({ valid: false, error: 'La licence a expiré' });
    }

    return NextResponse.json({
      valid: true,
      license: {
        status: data.status,
        expiresAt: data.expires_at,
        activationsRemaining: data.activations?.remaining
      }
    });
  } catch (error) {
    return NextResponse.json(
      { valid: false, error: 'Échec de la validation' },
      { status: 500 }
    );
  }
}

Prochaines étapes