# Intégrer techniquement les abonnements à votre application

Connecter Bookelio côté serveur, gérer abonnements et droits par API, configurer le prorata et attribuer des mois gratuits.

Langue : fr
Mis à jour : 2026-09-09
Source : https://app.staging.bookelio.app/docs/fr/integration-technique-abonnements

## Comprendre les responsabilités

Votre application authentifie ses utilisateurs et identifie leur compte ou organisation. Bookelio conserve le catalogue commercial, les clients à facturer, les abonnements et leurs cycles de paiement. Votre serveur interroge Bookelio pour autoriser les fonctionnalités vendues. Masquer un bouton dans le navigateur ne remplace pas ce contrôle serveur.

L'organisation vendeuse possède un ou plusieurs logiciels. Chaque logiciel possède ses fonctionnalités, ses plans et ses abonnements. Le module peut donc servir à plusieurs applications et à plusieurs vendeurs, chacun dans son périmètre.

Les exemples ci-dessous sont fictifs. Remplacez les domaines et identifiants par ceux de votre installation. Pour le parcours dans les écrans, consultez [Vendre des abonnements logiciels](/docs/fr/abonnements-logiciels).

## Préparer la connexion et les permissions

Activez le module **Abonnements logiciels** pour l'organisation vendeuse. Préparez un client de facturation dans cette organisation et une politique de TVA pour les offres payantes. Configurez ses moyens de paiement et son service e-mail pour les règlements et notifications.

Créez une clé API de l'organisation vendeuse avec les [permissions adaptées](/docs/fr/equipe-et-droits). Conservez-la sur le serveur de votre application. Elle identifie le vendeur ; elle ne remplace pas l'authentification de vos utilisateurs et peut accéder aux abonnements de plusieurs clients du même vendeur.

| Usage | Permissions de la clé |
| --- | --- |
| Lire logiciels, plans, abonnements et fonctionnalités autorisées | `softwareBilling.read` |
| Gérer le catalogue, résilier, accorder une offre ou programmer les sièges | `softwareBilling.manage` |
| Provisionner les fonctionnalités et les identités externes | `softwareBilling.manage` et `customers.create` |
| Créer un abonnement | `softwareBilling.manage` et `paymentRequests.create` |
| Modifier un abonnement en fournissant `planId`, notamment pour une réactivation | `softwareBilling.manage` et `paymentRequests.create` |
| Appliquer tout changement `IMMEDIATE`, même sans supplément ou pour les sièges seuls | `softwareBilling.manage` et `paymentRequests.create` |
| Lire ou modifier le déclenchement des factures dans les paramètres du vendeur | `settings.read` pour lire ; `settings.update` pour modifier |

Créer ou lire les clients et les politiques de TVA par leurs propres API demande également les permissions de ces ressources. Les exemples supposent ces éléments déjà créés.

Les routes REST ont pour préfixe `/api/v1/admin/software-billing`. Le transport oRPC utilise `/rpc/v1` et les opérations `admin.softwareBilling.*`. La spécification consultable sur l'instance API est `/spec/v1.json`. Dans le monorepo Bookelio, le type `BookelioApiV1Client` décrit ce contrat ; une application externe peut utiliser directement REST, sans dépendre d'un package interne au dépôt.

## Préparer les appels HTTP côté serveur

Pour les exemples, les variables ci-dessous sont des noms choisis par l'application tierce. Elles ne configurent pas automatiquement Bookelio lui-même.

```dotenv
SOFTWARE_BILLING_API_URL=https://api.billing.example.com
SOFTWARE_BILLING_API_KEY=REPLACE_WITH_SERVER_SIDE_KEY
SOFTWARE_BILLING_APPLICATION_ID=REPLACE_WITH_APPLICATION_ID
```

Voici un helper JavaScript à utiliser uniquement côté serveur, dans un environnement disposant de `fetch`. Il appelle les routes REST avec un corps JSON simple. Les dates reçues sont des chaînes ISO 8601 ; le client oRPC gère pour sa part la sérialisation des `Date`.

```js
const billingBase = new URL(
  '/api/v1/admin/software-billing/',
  process.env.SOFTWARE_BILLING_API_URL,
);

async function billingRequest(path, { method = 'GET', body } = {}) {
  const response = await fetch(new URL(path, billingBase), {
    method,
    headers: {
      'x-api-key': process.env.SOFTWARE_BILLING_API_KEY,
      'content-type': 'application/json',
    },
    body: body === undefined ? undefined : JSON.stringify(body),
    redirect: 'error',
    signal: AbortSignal.timeout(10_000),
  });
  if (!response.ok) {
    throw new Error(`Bookelio billing request failed: HTTP ${response.status}`);
  }
  return response.json();
}
```

Ne placez pas la clé dans une URL, un composant navigateur ou une variable publique. L'URL de connexion est celle de l'API ; les pages de paiement sont hébergées par le dashboard du vendeur, qui peut avoir un autre domaine.

## Créer le logiciel et les plans

La configuration du catalogue est une opération d'administration à effectuer une fois, pas à chaque connexion d'utilisateur. Conservez les `id` renvoyés par Bookelio. Les `code` sont des références lisibles et ne remplacent pas ces identifiants dans les appels.

Pour réutiliser un catalogue déjà créé dans le dashboard, `GET /applications` renvoie `{ applications: [...] }` et `GET /applications/{applicationId}/plans` renvoie `{ plans: [...] }`, sous le préfixe REST indiqué plus haut. Cette dernière liste inclut les plans inactifs : filtrez `isActive` avant de proposer une offre à l'achat. `GET /applications/{applicationId}/subscriptions` permet au vendeur de lister les abonnements avec une réponse `{ subscriptions: [...] }`.

```js
const application = await billingRequest('applications', {
  method: 'POST',
  body: {
    name: 'Example Workspace',
    code: 'example-workspace',
    features: [
      { key: 'projects', name: 'Projets' },
      { key: 'exports', name: 'Exports' },
    ],
  },
});

const enterprise = await billingRequest('plans', {
  method: 'POST',
  body: {
    applicationId: application.id,
    name: 'Enterprise',
    code: 'enterprise-monthly',
    interval: 'MONTHLY',
    basePriceCents: 400000,
    seatPriceCents: 0,
    includedSeats: 0,
    trialDays: 14,
    gracePeriodDays: 7,
    vatPolicyId: 'REPLACE_WITH_SELLER_VAT_POLICY_ID',
    featureKeys: ['projects', 'exports'],
  },
});
```

Ce plan coûte **4 000 € HTVA par mois**, indépendamment du nombre de sièges, après un essai de 14 jours. Il accorde ensuite sept jours de grâce au début de chaque cycle payant pour laisser le temps de régler. `seatPriceCents: 0` désactive le supplément par siège ; aucun champ `pricingMode` n'est requis par l'API.

| Modèle fictif | `basePriceCents` | `seatPriceCents` | `includedSeats` | Résultat HTVA |
| --- | --- | --- | --- | --- |
| Gratuit | `0` | `0` | `0` | Aucun paiement |
| Enterprise fixe | `400000` | `0` | `0` | 4 000 € par période, quel que soit l'effectif |
| Base et sièges supplémentaires | `1000` | `300` | `2` | 19 € par période pour 5 sièges |
| Tous les sièges payants | `0` | `500` | `0` | 25 € par période pour 5 sièges |

Les prix sont des entiers en centimes EUR HTVA. `interval` vaut `MONTHLY` ou `YEARLY` : pour une offre annuelle, fournissez le prix de l'année entière, pas un prix mensuel à multiplier. `trialDays` va de 0 à 365 ; zéro désactive l'essai. Un plan entièrement gratuit ne crée pas d'essai payant ni de demande de paiement, même si une durée a été saisie.

`gracePeriodDays` est un entier de 0 à 365, indépendant de l'essai. Il est facultatif à la création : l'omettre revient à `0` et conserve le fonctionnement sans grâce. Avec `PATCH /plans/{planId}`, l'omettre conserve la valeur actuelle ; envoyer `0` désactive la grâce pour les futurs cycles. Les plans existants restent sans grâce tant que cette valeur n'est pas modifiée.

Les clés de `featureKeys` doivent déjà exister dans le logiciel. Une politique de TVA appartenant au vendeur est obligatoire dès qu'un prix fixe ou par siège est positif. Le nombre de sièges d'un abonnement est un entier de 1 à 100 000. Cette borne technique ne transforme pas le tarif fixe en prix par siège et ne constitue pas une limite commerciale d'utilisateurs incluse dans le plan.

## Identifier le client et créer son abonnement

`customerId` désigne le client à facturer dans l'organisation vendeuse. `externalId` désigne le compte de ce client dans votre application, par exemple un identifiant d'organisation stable. Stockez cette correspondance côté serveur. Évitez un nom ou une adresse e-mail susceptible de changer.

L'identité est unique par couple `(applicationId, externalId)`. Dans le code suivant, `accountId` doit provenir de votre session vérifiée et de vos contrôles d'appartenance, et non d'un identifiant libre envoyé par le navigateur. Le plan et le client doivent également être sélectionnés et validés côté serveur.

```js
const subscription = await billingRequest('subscriptions', {
  method: 'POST',
  body: {
    applicationId: process.env.SOFTWARE_BILLING_APPLICATION_ID,
    planId: selectedPlanId,
    customerId: billingCustomerId,
    externalId: accountId,
    seats: memberCount,
  },
});
// Conserver subscription.id pour les modifications et résiliations.
```

Une nouvelle tentative avec le même logiciel, la même référence externe, le même client et le même plan retrouve l'abonnement existant. Elle ne redémarre pas l'essai, ne refacture pas et ne modifie pas les sièges. Un client ou plan différent pour cette référence produit un conflit. Pour changer de plan, utilisez l'opération de modification ; si le client diffère, vérifiez votre correspondance d'identités, car cette opération ne remplace pas le client de facturation. Les créations de logiciels et de plans, elles, ne sont pas des opérations de création-ou-mise-à-jour ; ne les rejouez pas comme l'inscription d'un client.

## Vérifier les fonctionnalités avant une action

Utilisez `subscriptions.entitlements`, et non le seul statut enregistré ou le nom du plan. Cette lecture vérifie la période en cours, son règlement net et son éventuel délai de grâce, et retourne les clés de fonctionnalités figées pour ce cycle.

```js
async function canUseFeature(verifiedAccountId, featureKey) {
  const appId = encodeURIComponent(
    process.env.SOFTWARE_BILLING_APPLICATION_ID,
  );
  const externalId = encodeURIComponent(verifiedAccountId);
  try {
    const rights = await billingRequest(
      `applications/${appId}/entitlements/${externalId}`,
    );
    return rights.access === true
      && typeof rights.validUntil === 'string'
      && Date.parse(rights.validUntil) > Date.now()
      && Array.isArray(rights.featureKeys)
      && rights.featureKeys.includes(featureKey);
  } catch {
    // Renvoyer un refus temporaire ; ne pas ouvrir les fonctions payantes.
    return false;
  }
}
```

Exemple fictif de réponse REST :

```json
{
  "subscriptionId": "example-subscription",
  "status": "PAST_DUE",
  "access": true,
  "featureKeys": ["projects", "exports"],
  "validUntil": "2026-09-29T10:00:00.000Z",
  "seats": 5,
  "isInGracePeriod": true,
  "graceEndsAt": "2026-09-29T10:00:00.000Z"
}
```

Cet exemple représente le premier cycle payant d'un essai terminé le 22 septembre à 10 h UTC : le règlement est encore attendu, mais les droits restent ouverts jusqu'au 29 septembre à 10 h UTC grâce aux sept jours configurés. `PAST_DUE` ne suffit donc pas à refuser une action ; utilisez `access`, la clé demandée et `validUntil` comme dans le helper.

Sans abonnement, la réponse contient `subscriptionId: null`, `status: null`, `access: false`, `featureKeys: []`, `validUntil: null`, `seats: 0`, `isInGracePeriod: false` et `graceEndsAt: null`. Si vous ajoutez un cache, isolez-le par vendeur, logiciel et référence externe, limitez sa durée et ne servez jamais un accord au-delà de `validUntil`. Le module ne fournit pas de webhook sortant de changement d'abonnement à l'application : prévoyez ces lectures aux points de contrôle de votre serveur.

Les lectures `entitlements` et `getByExternalId`, ainsi que `syncSeats`, conservent leurs contrôles de clé, de permissions et de vendeur, mais restent accessibles sans vérifier l'accès du vendeur à l'interface du module. Cela permet de consulter et régulariser les abonnements existants ; les autres opérations restent protégées par l'activation du module.

## Appliquer le délai de grâce de paiement

Le cycle conserve un instantané de `gracePeriodDays` à sa création ; `graceEndsAt` est calculé à partir de cette valeur et des dates immuables de la période. Pour un cycle payant, la grâce court de `periodStart` jusqu'à la première des deux dates : `periodStart + gracePeriodDays × 24 heures` ou `periodEnd`. L'instant de fin est exclu : à cette date, un cycle toujours impayé ne donne plus accès. La grâce concerne aussi le premier cycle payant et celui qui suit l'essai, sans modifier les dates de facturation ni créer un nouvel essai.

Pendant la grâce, le statut calculé reste `PAST_DUE`, `access` vaut `true`, `isInGracePeriod` vaut `true` et `validUntil` vaut `graceEndsAt`. Le paiement intégral vérifié fait passer les droits à une période réglée : `isInGracePeriod: false` et `validUntil: periodEnd`. Une grâce expirée avec paiement toujours insuffisant donne `access: false`, `featureKeys: []` et `validUntil: null`, sans attendre le worker.

`graceEndsAt` est une métadonnée, pas une preuve d'accès : la date reste visible après règlement ou expiration. Elle vaut `null` pour un cycle sans grâce, gratuit ou d'essai. Seul `isInGracePeriod` indique que l'accès actuel utilise cette tolérance. N'ajoutez aucun délai local en cas d'échec de l'API et ne prolongez pas un cache jusqu'à cette date sans vérifier `access` et `validUntil`.

Ces champs sont ajoutés en révision compatible V1-r28. Les réponses d'une ancienne instance V1 peuvent omettre `gracePeriodDays`, `graceEndsAt` et `isInGracePeriod` : ne déduisez aucune grâce de cette absence et continuez à utiliser `access` et `validUntil` pour décider. L'instance vendeuse doit être mise à jour pour configurer et appliquer cette fonctionnalité.

La durée et la date sont figées pour chaque cycle : une modification du plan, une nouvelle tentative du worker ou un paiement tardif ne les déplace pas. Le worker doit d'abord créer le nouveau cycle ; avant ce passage, un ancien cycle expiré n'accorde pas de grâce anticipée. Un retard de traitement peut donc créer une brève interruption, mais pas reporter le début du délai à l'heure d'exécution du worker.

Un paiement partiel ne prolonge pas la grâce. Un remboursement réalisé ou en cours empêche la grâce ; un remboursement échoué (`FAILED`) ne la bloque pas à lui seul. Une résiliation effective refuse tout accès, et une résiliation programmée ne prolonge pas les droits au-delà de leur limite. La grâce ne marque aucun paiement comme réalisé, ne déclenche pas de facture à elle seule et ne permet pas l'émission de nouvelles échéances tant que le cycle reste impayé.

## Présenter le règlement et suivre les statuts

```js
const appId = encodeURIComponent(process.env.SOFTWARE_BILLING_APPLICATION_ID);
const externalId = encodeURIComponent(accountId);
const { subscription } = await billingRequest(
  `applications/${appId}/subscriptions/by-external-id/${externalId}`,
);
const currentCycle = subscription?.cycles.find(
  (cycle) => cycle.periodStart === subscription.currentPeriodStart,
);
const paymentUrl = currentCycle?.paymentUrl ?? null;
```

Cette lecture retourne au plus les 20 cycles les plus récents. Affichez le `paymentUrl` du cycle à régler, tel que renvoyé par le vendeur. Ne reconstruisez pas ce lien avec le domaine de votre application. S'il est nul pour un cycle payant, vérifiez la configuration `BOOKELIO_WEB_URL` du vendeur. Un plan gratuit ou un essai n'a pas de demande de paiement.

| Statut | Conséquence pour l'intégration |
| --- | --- |
| `TRIALING` | Essai en cours ; vérifier `access`, les clés et `validUntil` |
| `ACTIVE` | Période gratuite ou réglée ; continuer à vérifier les droits calculés |
| `PAST_DUE` | Règlement attendu ; accès temporaire possible pendant la grâce, à vérifier via `access`, `isInGracePeriod` et `validUntil` |
| `CANCELED` | Abonnement résilié ; aucune fonctionnalité accordée par cet abonnement |

Le worker traite les renouvellements chaque minute. À la fin d'un essai ou d'une période réglée, il crée la prochaine échéance payante. Une échéance non réglée bloque la création des suivantes. Des droits expirés peuvent être refusés avant le prochain passage du worker : n'accordez pas de délai supplémentaire sur la base d'un ancien statut.

Le client règle via les moyens de paiement du vendeur, ou le vendeur enregistre un règlement externe. Les confirmations vérifiées rapprochent le paiement ; un règlement complet déclenche par défaut la facture, ou réutilise celle déjà émise à la création de la demande selon le réglage vendeur. Une redirection réussie du navigateur ne prouve pas le paiement : relisez les droits côté serveur. Les renouvellements utilisent des demandes de paiement et non un prélèvement récurrent automatique. Les [paiements partiels et remboursements](/docs/fr/paiements) peuvent retirer le droit correspondant à un cycle intégralement réglé.

## Configurer le déclenchement des factures

Ce réglage appartient à l'organisation vendeuse, pas au plan ni à l'application cliente. Il est disponible dans **Paramètres → Facturation → Factures des abonnements logiciels** et via `admin.invoicing.settings.get` / `.update`. Les routes REST sont `GET` et `PUT /api/v1/admin/invoicing/settings/automation` : elles n'utilisent pas le préfixe `software-billing` du helper précédent.

| `softwareSubscriptionInvoiceTiming` | Comportement |
| --- | --- |
| `PAYMENT_COMPLETED` | Valeur par défaut : génération après règlement intégral vérifié |
| `PAYMENT_REQUEST_CREATED` | Génération dès la création de la demande de paiement, même impayée |

Le champ est facultatif en V1-r28. Omettre le champ dans une mise à jour conserve le choix existant ; l'absence de configuration vendeur conserve `PAYMENT_COMPLETED`. L'opération `PUT` demande aussi les booléens `bookings`, `trainings` et `trainingPaymentReminderInvoices` : relisez les paramètres avec `GET` et renvoyez leurs valeurs actuelles avec le nouveau `softwareSubscriptionInvoiceTiming`, afin de ne pas modifier ces automatisations indépendantes. L'activation du mode à la création vérifie les services e-mail et stockage du vendeur ; un prérequis manquant empêche l'enregistrement.

Bookelio fige le choix dans chaque nouveau cycle payant. Il s'applique donc aux nouveaux abonnements, aux sorties d'essai, aux renouvellements et aux reprises créant une période, mais pas aux demandes existantes. Un cycle gratuit ou d'essai sans demande de paiement ne crée aucune facture. La génération à la création intervient après validation de la transaction d'abonnement ; les erreurs sont réessayées par le worker et ne doivent pas conduire votre application à recréer la souscription.

Pour le mode à la création, la date d'émission et l'échéance correspondent toutes deux à la date de création de la demande de paiement : la facture est exigible à l'émission, et non à `graceEndsAt`. Ces dates ne sont pas repoussées lors des nouvelles tentatives. La génération à la création et celle après règlement réutilisent la même facture : le paiement ultérieur ne crée pas de doublon et ne remplace pas le PDF/XML existant. Consultez la demande liée pour l'état actualisé du règlement.

L'émission d'une facture ne crée aucun paiement et ne modifie pas `access`. La grâce du plan et le règlement net vérifié restent indépendants du moment de facturation. Cette règle vaut aussi lorsque Bookelio est lui-même client de l'instance vendeuse via URL.

## Synchroniser les sièges et changer de plan

Pour une application tierce, définissez ce qu'est un siège et calculez la quantité sur votre serveur. Bookelio n'observe pas automatiquement les utilisateurs de votre application et ne crée pas de quota de connexion à partir de ce nombre.

```js
await billingRequest(
  `applications/${appId}/subscriptions/by-external-id/${externalId}/seats`,
  { method: 'PUT', body: { seats: 12 } },
);

await billingRequest(`subscriptions/${encodeURIComponent(subscriptionId)}`, {
  method: 'PATCH',
  body: { planId: nextPlanId, seats: 12 },
});
```

Sans `change`, ces modifications alimentent `pendingSeats` et `pendingPlanId` pour le prochain renouvellement. Elles ne recalculent ni le prix ni les droits du cycle actuel et n'effectuent pas de prorata. La quantité connue par Bookelio au début de la nouvelle période est figée. Modifiez aussi les sièges d'un forfait fixe si vous souhaitez suivre l'effectif : avec un prix par siège à zéro, son montant reste identique.

Pour modifier les tarifs, fonctionnalités ou délais de grâce d'un plan pour ses futurs cycles, utilisez `PATCH /plans/{planId}` avec les champs à modifier, par exemple `{"basePriceCents": 450000, "gracePeriodDays": 7}`. Les cycles existants conservent leurs conditions. Mettre un logiciel ou un plan à `isActive: false` empêche les nouveaux abonnements concernés ; cela ne résilie pas les contrats existants.

## Appliquer un changement immédiat avec prorata

V1-r29 ajoute `change` facultatif aux deux opérations précédentes. `effectiveAt: 'NEXT_PERIOD'` conserve la programmation et n'accepte que `proration: 'NONE'`. Pour agir maintenant, envoyez `effectiveAt: 'IMMEDIATE'` avec une clé d'idempotence stable de 1 à 100 caractères. `proration` vaut `NONE` par défaut : choisissez explicitement le traitement financier voulu.

```js
await billingRequest(`subscriptions/${encodeURIComponent(subscriptionId)}`, {
  method: 'PATCH',
  body: {
    planId: upgradedPlanId,
    seats: 12,
    change: {
      effectiveAt: 'IMMEDIATE',
      proration: 'CHARGE_AND_CREDIT',
      idempotencyKey: 'example-account-change-2026-09-08-001',
    },
  },
});
```

Pour les sièges seuls, ajoutez le même objet `change` au corps `{ seats: 12 }` du `PUT .../seats`. Dérivez toujours le compte, la quantité et le plan autorisé côté serveur. Conservez la clé pour les retries de la même commande ; une clé nouvelle désigne une nouvelle action. Réutiliser une clé avec d'autres paramètres est un conflit. Ne générez pas une nouvelle clé à chaque tentative réseau.

| `proration` | Effet sur la période restante |
| --- | --- |
| `NONE` | Applique le plan/les sièges sans ajustement financier |
| `CHARGE_ONLY` | Facture seulement les hausses ; aucune remise pour les baisses |
| `CHARGE_AND_CREDIT` | Facture les hausses et reporte les baisses financées éligibles comme remise commerciale |

Le calcul couvre le montant fixe et les sièges, selon la fraction exacte de temps UTC restant dans la période calendaire, arrondie aux centimes. Il ne suppose pas des mois de 30 jours. À mi-période, une hausse fictive de 100 € à 160 € HTVA produit 30 € HTVA avant TVA ; les changements de sièges n'affectent pas un plan à `seatPriceCents: 0`.

L'abonnement doit être non résilié, sa période encore valide et toutes ses obligations financières réglées. Un impayé, même en grâce, bloque l'ajustement. La nouvelle périodicité doit être identique ; programmez les passages mensuel/annuel au renouvellement. Durant un essai ou une offre, le changement conserve la date de fin gratuite et ne génère ni montant dû ni crédit. Pour réactiver un abonnement résilié, omettez `change` ou utilisez `NEXT_PERIOD` sans prorata.

Le serveur ajoute un cycle immuable dont `previousCycleId` désigne le précédent. `currentPeriodStart` devient la date du changement, mais la fin initiale et l'ancrage de renouvellement restent inchangés. Les anciennes factures et demandes restent intactes. Un supplément à régler crée une demande avec les règles de facture et de grâce figées sur ce nouveau cycle ; les droits suivent le règlement ou cette grâce. Relisez `entitlements` après l'appel : le succès du changement n'est pas une preuve de paiement et ne débite aucune carte automatiquement.

`creditGrantedCents` retrace la remise accordée, `creditAppliedCents` la remise consommée et `creditBalanceCents` le solde restant, en centimes HTVA pour ce seul abonnement. Ce solde réduit de prochaines charges avant TVA : ce n'est ni un avoir comptable sur une facture antérieure, ni un remboursement bancaire, ni un paiement. Aucune demande négative ni paiement fictif n'est créé. La remise est plafonnée à la valeur financée ; un essai, une offre ou une hausse gratuite ne crée pas de valeur créditable. Les remboursements génériques d'un abonnement ayant des ajustements sont refusés tant qu'un parcours de réconciliation dédié n'est pas disponible.

## Accorder des mois gratuits par API

À la création, ajoutez par exemple `offer: { label: 'Offre de bienvenue', freeMonths: 1 }` au corps du `POST /subscriptions`. Pour un abonnement existant, utilisez `subscriptions.grantOffer` :

```js
await billingRequest(
  `subscriptions/${encodeURIComponent(subscriptionId)}/offers`,
  {
    method: 'POST',
    body: {
      label: 'Un mois offert — exemple fictif',
      freeMonths: 1,
      idempotencyKey: 'example-commercial-offer-001',
    },
  },
);
```

Le libellé comporte de 1 à 200 caractères, `freeMonths` est un entier de 1 à 36 et la clé comporte de 1 à 100 caractères. Une seule offre peut être en attente. Rejouer la même clé et les mêmes données n'ajoute pas de mois, même après application ; changer les données sous cette clé produit un conflit. Rejouer la création d'un abonnement existant ne lui ajoute pas d'offre : utilisez l'opération dédiée. Un abonnement résilié doit être réactivé avant l'attribution.

L'offre initiale fournie lors d'un retry de création doit correspondre à celle déjà enregistrée ; une offre absente ou différente dans l'abonnement existant produit un conflit. Les réponses exposent au plus les 20 offres les plus récentes, comme les 20 cycles récents : ce n'est pas un export exhaustif de l'historique.

L'offre attend le prochain cycle normalement payant, après l'essai ou la période déjà émise ; sans essai, elle peut commencer à la création. Les impayés restent bloquants et les plans entièrement gratuits la laissent en attente. Aucun document ni montant dû existant n'est modifié. Un mois signifie un mois calendaire UTC même sur un plan annuel, avec adaptation aux fins de mois ; la facturation payante reprend à la fin de l'offre avec ce nouvel ancrage.

Le cycle offert expose `isComplimentary: true` et `offerId`, reste `ACTIVE` et ne crée ni demande de paiement ni facture automatique. Il n'est pas un nouvel essai ; vérifiez toujours `access` et `validUntil`. `offers` expose le libellé, la durée, `appliedAt` et les dates de période lorsqu'elle a été consommée. Les nouveaux champs de sortie de prorata et d'offres sont facultatifs pour accepter des vendeurs plus anciens : leur absence ne prouve ni remise ni offre. Le vendeur doit être en V1-r29 pour appliquer ces options ; vérifiez sa révision avant de proposer ces actions.

En cas de worker retardé, l'offre ne commence jamais avant sa date d'attribution. Si toute sa fenêtre gratuite aurait déjà expiré avant le traitement, elle démarre à la reprise du worker pour ne pas être consommée entièrement dans le passé. Les dates renvoyées par Bookelio font référence.

## Résilier et réactiver

```js
await billingRequest(
  `subscriptions/${encodeURIComponent(subscriptionId)}/cancel`,
  { method: 'POST', body: { cancelAtPeriodEnd: true } },
);
```

`true` programme la fin à l'échéance : les droits déjà valides restent utilisables dans leur limite actuelle, sans prolonger une grâce qui expire avant cette date. `false` demande une résiliation immédiate et coupe aussi une grâce en cours ; il ne signifie pas « annuler une résiliation programmée ». Les demandes de paiement émises et l'historique financier sont conservés.

Une fois le statut `CANCELED`, envoyez un `PATCH /subscriptions/{subscriptionId}` avec un `planId` explicite pour réactiver. La seule synchronisation des sièges ne suffit pas. Une période déjà payée et encore valide reprend sans être refacturée ; après expiration, un nouveau cycle commence. La reprise ne donne pas de nouvel essai et refuse une ancienne échéance non réglée. Relancer `POST /subscriptions` retrouve l'abonnement existant et ne le réactive pas.

Une modification par `subscriptions.update` avant que la résiliation programmée soit effective ne retire pas `cancelAtPeriodEnd`. Depuis V1-r33, l'activation explicite par `subscriptions.activate`, décrite plus bas, applique le plan maintenant et retire cette programmation après vérification des montants déjà dus.

## Connecter Bookelio à une instance de facturation par URL

Cette configuration concerne Bookelio lui-même. Sur l'instance vendeuse, créez un logiciel Bookelio et préparez vos plans. Au démarrage, Bookelio ajoute les clés manquantes de son catalogue, par exemple `invoicing`, `trainings` ou `softwareBilling`, sans écraser les définitions existantes. Sélectionnez ensuite explicitement les fonctionnalités incluses dans chaque plan : l'ajout d'une clé ne la rend pas automatiquement disponible dans les offres. Le module de vente d'abonnements peut ainsi lui-même être inclus dans une offre.

Sur **l'API et les workers de l'instance cliente**, configurez les trois variables reconnues par Bookelio :

```dotenv
BOOKELIO_BILLING_URL=https://api.billing.example.com
BOOKELIO_BILLING_API_KEY=REPLACE_WITH_SELLER_ORGANIZATION_KEY
BOOKELIO_BILLING_APPLICATION_ID=REPLACE_WITH_BOOKELIO_APPLICATION_ID
```

La connexion passe toujours par HTTP `/rpc/v1`, même si l'URL vise la même instance. L'adresse doit être joignable depuis l'API et les workers. Utilisez une URL API sans identifiants intégrés, paramètres de requête ou fragment. Sur l'API et les workers de l'instance vendeuse, `BOOKELIO_WEB_URL` désigne l'origine de son dashboard public pour les liens de règlement. La clé de connexion a besoin de `softwareBilling.read`, `softwareBilling.manage` et `customers.create` pour la consultation, le provisionnement et la synchronisation ; ajoutez `paymentRequests.create` pour les inscriptions aux plans.

### Lier les organisations au démarrage

Après l'ouverture de son serveur HTTP, l'API cliente provisionne le catalogue manquant et les comptes correspondant aux organisations locales non legacy. Elle utilise l'ID d'organisation comme `externalId`, son nom et son nombre de membres, propriétaire inclus, avec un minimum d'un siège. Une liaison distante est unique pour le logiciel et cet identifiant. Les relances réutilisent cette liaison ; un scan sans chevauchement réessaie les échecs et découvre les nouvelles organisations chaque minute. Les organisations legacy sont exclues des comptes et de la synchronisation des sièges, mais pas du provisionnement du catalogue commun. Le démarrage n'attend pas une réponse de sa propre URL avant de commencer à écouter.

Si un abonnement distant existe déjà pour cette identité, son client est réutilisé. Sinon, les coordonnées professionnelles locales ne permettent de créer un client de facturation que si le profil nécessaire est complet et valide. Aucun pays ni identifiant fiscal n'est inventé. Un profil incomplet laisse un compte lié sans client ; les informations d'un client déjà lié ne sont pas écrasées. L'opération `applications.provision` elle-même ne crée aucun plan, abonnement, demande de paiement, facture ou règlement.

Après cette opération, si un abonnement existe déjà, le scan appelle `subscriptions.syncSeats` sans option immédiate : les sièges sont programmés au renouvellement, sans prorata ni nouvelle obligation de paiement. Un abonnement résilié n'est pas réactivé. Cet appel conserve les automatismes existants : il peut réessayer une facture déjà attendue pour une demande de paiement existante en mode `PAYMENT_REQUEST_CREATED`, sans créer une nouvelle demande.

Le vendeur doit disposer de V1-r30 et du schéma additif déployé avant d'activer ce démarrage côté client. Une ancienne instance peut encore répondre aux lectures existantes, mais ne propose pas le nouveau provisionnement. La clé doit avoir les deux permissions du provisionnement, même lorsque le lot ne nécessite finalement aucun nouveau client.

L'opération est aussi disponible pour une autre application : `applications.provision`, soit `POST /applications/{applicationId}/provision` sous le préfixe REST du module. Exemple fictif sans profil de facturation, donc sans création de client :

```js
const provisioned = await billingRequest(
  `applications/${encodeURIComponent(applicationId)}/provision`,
  {
    method: 'POST',
    body: {
      features: [{ key: 'reporting', name: 'Rapports' }],
      accounts: [{ externalId: 'example-organisation-001', name: 'Organisation exemple', seats: 5 }],
    },
  },
);
```

Les lots acceptent au plus 200 fonctionnalités et 100 comptes. La réponse identifie le logiciel, les clés et, pour chaque identité, `accountId`, `customerId` et `subscriptionId` ; les deux derniers peuvent être `null`. Une identité provisionnée n'est donc pas une preuve d'abonnement ou d'accès. Utilisez toujours `entitlements` pour autoriser les fonctionnalités. Un appel identique ne duplique ni clés ni comptes et ne remplace pas les métadonnées existantes des fonctionnalités.

### Attribuer les plans et appliquer les droits distants

Dans le superadmin, la page **Facturation** vérifie la connexion. L'onglet abonnement d'une organisation peut associer un plan et un client de l'instance vendeuse. Le propriétaire peut aussi choisir un plan dans **Paramètres → Mon abonnement Bookelio**, après confirmation. Bookelio utilise l'ID de l'organisation cliente comme `externalId` et synchronise le nombre de membres, propriétaire inclus, avec un minimum d'un siège.

La connexion utilise les mêmes droits calculés, y compris la grâce configurée sur le plan du vendeur. Aucun paramètre de grâce supplémentaire n'est nécessaire sur l'instance cliente, même si elle utilise sa propre URL pour la facturation.

Les offres et ajustements décidés chez le vendeur utilisent aussi cette connexion. La synchronisation automatique des membres Bookelio n'envoie pas d'option immédiate : elle conserve les changements de sièges au renouvellement. La page de votre propre abonnement affiche les offres et le solde de remise en lecture seule ; les modifications commerciales se font chez le vendeur.

Pour les organisations non legacy, le fournisseur Bookelio décide de toutes les fonctionnalités, y compris celles auparavant activées par défaut ou sans tarification. Une clé absente de ses droits est désactivée. Depuis V1-r32, les plans Bookelio passent uniquement par un fournisseur externe : les anciens plans et abonnements locaux, valeurs par défaut et dérogations individuelles n'accordent plus aucun accès. Le catalogue local conserve les définitions, correspondances de clés et l'activation globale, pas les droits commerciaux. Les tables historiques ne sont pas supprimées ; le module vendeur continue à stocker ses propres plans, lus par HTTP même sur la même instance.

Le fournisseur générique `BOOKELIO_FEATURE_PROVIDER_*` n'est pas interrogé en parallèle de Bookelio. Une configuration Bookelio partielle ou une panne ne déclenche aucun repli. Si les trois variables `BOOKELIO_BILLING_*` sont absentes, le fournisseur générique peut seul fournir les droits ; sans aucun fournisseur, une organisation non legacy n'a aucune fonctionnalité. Ce fournisseur générique expose uniquement un contrat de lecture : Bookelio n'invente pas d'API d'écriture pour y créer des clés ou des organisations. Le provisionnement décrit ici concerne exclusivement une instance Bookelio configurée par `BOOKELIO_BILLING_*`.

### Exemption explicite des organisations legacy

V1-r32 ajoute `Organization.bookelioLegacyAccess`, à `false` par défaut. Seul un superadministrateur peut marquer une organisation legacy, par exemple pour Bookelio lui-même ou un contrat de développement sur mesure. L'opération auditée `superadmin.organizations.setLegacyAccess` accepte strictement `{ organizationId, enabled }` et répond `{ id, bookelioLegacyAccess }`. Sa route REST est `PUT /api/v1/superadmin/organizations/{organizationId}/legacy-access`. Un owner ou une clé d'organisation ne peuvent pas l'appeler ; aucune variable générique ni dérogation par fonctionnalité n'accorde cette exemption.

Legacy donne accès à toutes les fonctionnalités globalement actives sans consulter le fournisseur, même absent, mal configuré ou en panne. Il ne contourne ni l'authentification, ni l'isolation des organisations, ni les permissions métier des membres et clés, ni une désactivation globale. Le diagnostic expose l'objet facultatif `providers.legacy: { active: true }` et conserve la source V1 `manual`. Les anciennes routes de dérogations restent disponibles pour compatibilité, mais leurs données n'accordent plus de droits.

Les lectures `admin.organization.subscription.get/options` exposent le booléen facultatif `bookelioLegacyAccess`. Pour legacy, elles renvoient `connected: false`, sans lecture distante, avec `subscription: null` pour `get` et aucun plan pour `options`. Ce résultat signifie une exemption et non une panne du fournisseur. La page Mon abonnement Bookelio l'explique sans sélecteur ; `configure` refuse un plan pour une organisation legacy.

Activer l'exemption ne résilie pas un abonnement déjà présent chez le vendeur, n'arrête pas ses factures futures et n'efface aucun impayé ou document. Gérez séparément et explicitement cet abonnement chez le vendeur. Désactiver legacy rétablit les contrôles externes ; la synchronisation peut reprendre sans créer automatiquement de souscription.

Avant de déployer V1-r32, faites générer, revoir et appliquer manuellement la colonne additive `Organization.bookelioLegacyAccess Boolean @default(false)`. Préparez les droits externes ou marquez explicitement les seules organisations legacy prévues lors de la bascule. Aucun marquage automatique des anciens clients ni suppression des tables historiques ne doit accompagner cette étape : sans fournisseur, une organisation non legacy n'obtient plus les droits locaux précédents.

### Choix du plan par le propriétaire via l'API cliente

V1-r31 ajoute deux opérations sur l'API de l'instance cliente, pas sur l'API du vendeur. Elles exigent une session humaine et une appartenance `owner` encore présente en base dans l'organisation active. Une clé API, un administrateur non propriétaire ou une autre organisation ne peuvent pas les utiliser. Le contrôle ne dépend pas du module de vente, afin de pouvoir régulariser un accès expiré. Les autres rôles autorisés conservent la consultation historique `admin.organization.subscription.get`.

| Opération oRPC | Route REST | Effet |
| --- | --- | --- |
| `admin.organization.subscription.options` | `GET /api/v1/admin/organization/subscription/options` | Lecture seule : `connected`, `plans` actifs, `seats` calculés côté serveur et `billingDetailsRequired` |
| `admin.organization.subscription.configure` | `PUT /api/v1/admin/organization/subscription/plan` | Après confirmation : sélection avec `{ planId, expectedOrganizationId, activation? }`, retour de l'abonnement Bookelio |

`expectedOrganizationId` reprend l'organisation pour laquelle le propriétaire vient de confirmer. C'est uniquement une précondition : si la session a changé d'organisation entre-temps, l'API refuse avec `CONFLICT` avant toute écriture. La cible, l'identité externe et les sièges restent dérivés de la session et de la base ; aucun prix, client, quantité, offre ou paramètre de prorata supplémentaire n'est accepté.

La lecture des options ne crée aucun client ni abonnement. Lors de la configuration, le serveur réutilise le client de l'abonnement ou du compte distant. Le résultat vendeur `subscriptions.getByExternalId` expose désormais un `account` facultatif pour cette liaison avant abonnement ; les anciennes réponses sans ce champ restent acceptées. Sans client lié, un profil professionnel complet est transmis au provisionnement existant ; des informations insuffisantes nécessitent de compléter **Paramètres → Informations de l'entreprise** avant de confirmer. Aucun pays ou identifiant fiscal n'est inventé, et un client déjà lié n'est pas réécrit.

Depuis V1-r33, le dashboard envoie `activation: { idempotencyKey }` pour appliquer le plan immédiatement. La clé reste identique pour les nouvelles tentatives de la même confirmation. Sans ce champ facultatif, les anciens appels gardent leur comportement : création initiale, nouveau plan au renouvellement ou reprise protégée. Un premier choix respecte la gratuité et l'essai du plan ; un choix ultérieur ne recrée aucun essai.

Le serveur appelle alors `admin.softwareBilling.subscriptions.activate` chez le vendeur (`POST /api/v1/admin/software-billing/subscriptions/activate`) avec l'entrée stricte `{ applicationId, externalId, customerId, planId, seats, idempotencyKey }`. Cette opération atomique exige `softwareBilling.manage` et `paymentRequests.create` et conserve un reçu pour chaque activation réussie, même si le plan est déjà appliqué. La clé et l'empreinte empêchent une double charge ou la réapplication d'un ancien choix après une modification ultérieure. Le prix et le traitement financier sont calculés par le vendeur, jamais choisis dans le navigateur.

La politique est `CHARGE_ONLY` : à périodicité identique, seul l'écart positif est facturé au prorata du temps restant, jusqu'à la fin initiale. Un changement de périodicité commence une nouvelle période maintenant et déduit la valeur HTVA restante du service dont le financement est vérifié ; l'excédent éventuel n'est ni reporté ni remboursé. Un essai ou une période offerte conserve sa fin gratuite, même si la périodicité change. Une période expirée et réglée recommence maintenant, sans rattrapage de périodes anciennes. L'activation explicite annule aussi une résiliation déjà programmée.

Un choix identique à l'instantané courant, sans changement en attente, retrouve la demande de paiement éventuelle sans autre charge. Les obligations impayées empêchent une configuration différente. Réappliquer le plan courant permet de rafraîchir des fonctionnalités figées périmées, par exemple celles d'un ancien cycle gratuit. Les cycles, paiements et factures passés restent intacts. Relisez l'abonnement et ses droits : la page propose **Payer maintenant** pour une demande due, mais confirmer un plan ne débite pas automatiquement une carte et ne prouve pas son règlement. Les fonctionnalités payantes suivent le paiement vérifié ou la grâce applicable. Les offres restent des décisions du vendeur.

La clé serveur vers le vendeur conserve les permissions `softwareBilling.read`, `softwareBilling.manage`, `customers.create` et `paymentRequests.create`. Elle n'est jamais envoyée au navigateur. Les appels restent HTTP `/rpc/v1`, même pour la même instance. Faites générer, revoir et appliquer manuellement la table additive `BookelioSoftwareActivation`, sa relation et sa clé unique avant de déployer le vendeur V1-r33, puis le client qui demande l'activation. Un vendeur ancien ne reçoit pas de programmation différée en remplacement. Les schémas additifs existants des cycles et comptes et la colonne legacy restent requis ; aucun reçu historique ne doit être inventé et aucune variable d'environnement supplémentaire n'est nécessaire. Sans connexion ou pour une organisation legacy, aucun plan ne peut être choisi par ce parcours.

Avant une activation propriétaire, le serveur lit le registre public `/api-versions.json` du vendeur, sans transmettre la clé API. Une entrée V1 de révision 33 ou supérieure est requise avant tout provisionnement ou activation. Un vendeur plus ancien peut afficher ses plans, mais le parcours propriétaire refuse l'activation avec une erreur `SERVICE_UNAVAILABLE` demandant sa mise à jour ; cela ne signifie pas que le plan ou le compte a disparu. Si le registre est indisponible ou invalide, l'erreur précise que la version ne peut pas être vérifiée : rétablissez ce point d'accès avant de réessayer. Les anciens appels sans `activation` restent inchangés.

## Diagnostiquer et valider l'intégration

| Symptôme | Vérification |
| --- | --- |
| Clé refusée ou accès interdit | Clé d'organisation valide, permissions de l'opération et module activé pour le vendeur |
| Fonctionnalités absentes sans fournisseur | Configurer les droits externes ou faire vérifier l'exception legacy par un superadministrateur ; aucun plan local ne sert de repli |
| Organisation legacy sans sélecteur de plan | Comportement prévu ; l'exemption ne résilie pas un éventuel abonnement chez le vendeur |
| Choix du plan Bookelio refusé | Session du propriétaire de l'organisation active ; aucune clé API ne remplace ce contrôle. Après un changement d'organisation, recharger avant de confirmer |
| Coordonnées demandées avant souscription | Compléter les détails de l'entreprise ou faire vérifier le client déjà lié sur le fournisseur ; aucune donnée fictive ne doit être utilisée |
| Logiciel, plan ou client introuvable | Identifiants appartenant au même vendeur ; le plan doit appartenir au logiciel |
| Liaison au démarrage absente | Vendeur en V1-r30, migration additive appliquée, URL joignable et permissions `softwareBilling.manage` / `customers.create` ; attendre le prochain scan après correction |
| Compte lié sans abonnement ou client | Profil de facturation incomplet ou plan non attribué ; compléter le profil ou sélectionner explicitement le client et le plan, sans attendre une facturation automatique |
| Conflit à la création | Référence externe déjà utilisée ; relire l'abonnement puis modifier plutôt que recréer |
| Pas de droits après le retour du paiement | Règlement complet réellement confirmé, période encore valide, clé de fonctionnalité incluse |
| `PAST_DUE` avec accès encore ouvert | Grâce active : vérifier `isInGracePeriod` et `validUntil`, sans assimiler cet accès à un règlement |
| Grâce absente ou déjà terminée | Valeur figée dans le cycle, début de période, `graceEndsAt`, résiliation ou remboursement ; changer le plan ne modifie pas l'historique |
| Nouveau plan ou nouvelle quantité non visible | Sans option immédiate, vérifier `pendingPlanId` et `pendingSeats` ; sinon relire les droits et la nouvelle demande à régler |
| Changement immédiat refusé | Absence de dette, permission `paymentRequests.create` et clé d'idempotence cohérente ; `subscriptions.update` exige aussi une période valide et la même périodicité, `subscriptions.activate` exige un vendeur V1-r33 migré |
| Offre encore en attente | Essai ou période émise en cours, plan gratuit, impayé bloquant ; consulter ses dates dans `offers` |
| Aucun lien de paiement | Cycle gratuit/d'essai ou origine publique du vendeur manquante |
| Facture attendue dès la demande mais absente | Choix vendeur figé lors de la création du cycle, services de génération/livraison disponibles et worker actif ; aucun effet rétroactif |
| Renouvellement absent | Worker actif, fin de période atteinte, absence d'échéance impayée bloquante |

Sur une installation de test préparée pour ce module, vérifiez un plan gratuit, un essai, un paiement incomplet puis complet, un forfait fixe avec différents effectifs et une offre par siège. Testez une grâce désactivée puis activée, sa limite exacte, le paiement pendant et après celle-ci, un remboursement et une modification qui ne change que les futurs cycles. Vérifiez les deux déclenchements de facture, l'absence de doublon après règlement et l'absence d'effet rétroactif sur les demandes existantes. Vérifiez aussi qu'un compte ne peut jamais consulter les droits ou échéances d'un autre compte en changeant un identifiant navigateur. Terminez par un changement programmé, une résiliation et une reprise sans nouvel essai, puis par une panne simulée de la connexion.

Pour les options V1-r29, testez une hausse puis une baisse immédiate, chaque mode de prorata, l'ajout de sièges et les retries de la même clé. Vérifiez une remise consommée sur une prochaine charge et l'absence de crédit après une hausse gratuite. Testez un mois offert sur un plan annuel, après un essai et avec une facture impayée, ainsi que le refus d'une seconde offre en attente. Vérifiez les nouvelles actions avec et sans leurs permissions financières.

Pour V1-r30, testez le démarrage avec une URL distante puis celle de la même instance, deux scans successifs, une fonctionnalité déjà définie chez le vendeur et une organisation créée après démarrage. Vérifiez les profils complets et incomplets, la réutilisation d'un client existant, l'absence de nouvel abonnement ou de nouvelle obligation financière et le refus des clés sans permission. Vérifiez aussi les sièges programmés et la reprise normale des factures attendues sur des demandes existantes. Simulez une panne : aucune valeur locale ni dérogation ne doit contourner les droits du fournisseur configuré.

Pour V1-r31, vérifiez que consulter les options ne crée aucun client ou abonnement, qu'un non-propriétaire ou une clé API ne peut pas configurer le plan, et qu'un changement d'organisation avant confirmation provoque un conflit. Testez la réutilisation d'un client lié, les données manquantes, le premier plan gratuit/avec essai, un changement différé et une reprise sans nouvel essai. Le choix ne doit accepter ni prix, ni client, ni quantité, ni option commerciale fournis par le navigateur.

Pour V1-r32, vérifiez le refus sans fournisseur pour une organisation non legacy, même avec un ancien plan ou une dérogation. Testez legacy avec un fournisseur absent ou en panne, une fonctionnalité inactive et un membre sans permission métier. Vérifiez que seul le superadministrateur modifie ce statut, que l'audit est enregistré et qu'aucun appel de compte, sièges ou choix de plan distant ne part pour legacy. Retirer l'exemption doit rétablir les contrôles externes sans effacer de dette ni créer d'abonnement.

Pour V1-r33, testez l'activation immédiate d'un plan gratuit ou payant, la mise à jour des fonctionnalités du plan courant, le supplément au prorata, le changement de périodicité et la reprise. Vérifiez le paiement proposé après confirmation et rechargement, le refus d'une nouvelle configuration en présence d'une dette, les nouvelles tentatives sans doublon et les anciens appels sans `activation` toujours différés. Un vendeur ancien doit refuser l'activation sans appliquer silencieusement un changement au renouvellement.

Les échéances, droits et factures sont pilotés par Bookelio. L'application consommatrice utilise l'API pour les consulter et les gérer ; elle ne modifie pas directement les cycles, les statuts de paiement ou la base de données de facturation.
