Tableau de bord ↗ العربية

Le moteur d’abonnements de lahsab, c’est Stripe où collectionMethod vaut toujours send_invoice : on émet une facture datée, on relance, et le client paie par virement CCP / BaridiMob quand il veut. Il n’y a pas de carte à débiter, donc rien ne se prélève tout seul, et la mécanique tout entière découle de là.

La règle d’or

Stripe-carte peut avancer l’horloge dès le renouvellement, parce qu’il a une carte à débiter : le paiement est présumé. lahsab n’a rien à débiter. Avancer la période sur une facture émise mais non payée, ce serait accorder de l’accès contre une promesse, et vous laisser croire qu’un client est à jour alors que l’argent n’est jamais arrivé.

Donc l’horloge ne bouge jamais sur une promesse. Elle bouge sur un paiement.

En pratique, votre code d’entitlement se réduit à une comparaison :

const aAcces = user.paidUntil && user.paidUntil > new Date();

paidUntil est le currentPeriodEnd que vous avez écrit en recevant invoice.paid.

Qui possède quoi

C’est la frontière à avoir en tête avant d’écrire la première ligne.

lahsab possèdeVous possédez
QuoiL’argent et le tempsL’entitlement
C’est-à-direQui doit quoi, pour quelle période, est-ce payé, depuis quand c’est en retard.Ce qu’un plan débloque, les quotas, les limites, les rôles.

lahsab ne sait pas ce que débloque votre plan « Pro » : ni combien de projets il autorise, ni quelles fonctionnalités il ouvre. Exactement comme Stripe. On vous dit « payé jusqu’au 13 septembre » ; ce que vous en faites vous regarde.

Le modèle

Cinq objets, empruntés à Stripe : si vous connaissez l’un, vous connaissez l’autre.

ObjetRôle
CustomerVotre client. externalId = son identifiant chez vous (unique par marchand et environnement). phone est le canal de relance.
ProductCe que vous vendez. « Abonnement Pro ».
PriceUn tarif pour un produit : montant, one_time ou recurring, et la cadence. Immuable.
SubscriptionL’engagement d’un client sur un prix récurrent. Porte currentPeriodEnd.
InvoiceCe qui est dû, pour une période, avec une échéance. C’est l’objet qui se paie.

Souscrire, en un appel

POST /subscriptions est le point d’entrée du parcours d’achat. En un seul appel, il crée l’abonnement, émet immédiatement la première facture, et vous renvoie le lien sur lequel rediriger l’acheteur. C’est votre Stripe Checkout.

  1. Résolvez le client. Cherchez-le par votre propre identifiant (GET /customers?externalId=), créez-le s’il n’existe pas. C’est le motif de lookup / upsert.

  2. Créez l’abonnement. Un POST /subscriptions avec le customerId et le priceId. L’abonnement naît incomplete et la première facture est émise.

  3. Redirigez l’acheteur vers latestInvoice.hostedInvoiceUrl, la page de facture hébergée par lahsab, où il choisit son moyen de paiement, voit les coordonnées correspondantes et dépose sa preuve.

  4. Attendez invoice.paid. C’est là, et seulement là, que l’accès s’accorde : l’abonnement passe active et currentPeriodEnd avance.

Résoudre le client
# 1. Le client existe-t-il déjà ?
curl "https://api.lahsab.com/customers?externalId=user_abc123" \
  -H "x-api-key: lsk_live_CLE"

# 2. Sinon, on le crée.
curl -X POST https://api.lahsab.com/customers \
  -H "x-api-key: lsk_live_CLE" \
  -H "content-type: application/json" \
  -H "Idempotency-Key: cust-user_abc123" \
  -d '{
    "externalId": "user_abc123",
    "name": "Yacine B.",
    "phone": "0770000000",
    "defaultMethod": "baridimob"
  }'
Souscrire
curl -X POST https://api.lahsab.com/subscriptions \
  -H "x-api-key: lsk_live_CLE" \
  -H "content-type: application/json" \
  -H "Idempotency-Key: sub-user_abc123-pro-mensuel" \
  -d '{
    "customerId": "c0ffee00-1111-4222-8333-444455556666",
    "priceId": "9f8e7d6c-5b4a-4321-9876-543210fedcba",
    "daysUntilDue": 7
  }'

Réponse 201, l’abonnement avec sa première facture déjà émise :

{
  "id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
  "environment": "live",
  "status": "incomplete",
  "collectionMethod": "send_invoice",
  "daysUntilDue": 7,
  "invoiceLeadDays": 7,
  "currentPeriodStart": "2026-07-13T00:00:00.000Z",
  "currentPeriodEnd": "2026-08-13T00:00:00.000Z",
  "cancelAtPeriodEnd": false,
  "latestInvoice": {
    "id": "f1e2d3c4-b5a6-4978-8b1c-2d3e4f5a6b7c",
    "number": "LSB-2026-0042",
    "status": "open",
    "amountDue": 2500,
    "dueDate": "2026-07-20T00:00:00.000Z",
    "hostedInvoiceUrl": "https://dashboard.lahsab.com/invoice/f1e2d3c4-b5a6-4978-8b1c-2d3e4f5a6b7c"
  },
  "createdAt": "2026-07-13T10:00:00.000Z"
}

Les états

Facture

draftopen paidvoiduncollectible

draft est fugace : la facture est finalisée (donc numérotée) dans la foulée. paid, void et uncollectible sont terminaux.

StatutSens
draftCréée, pas encore numérotée ni payable.
openFinalisée, numérotée, payable.
paidPayée. Émet invoice.paid.
voidAnnulée par vous.
uncollectibleLa grâce est épuisée : vous ne comptez plus l’encaisser.

Abonnement

incompletetrialing activepast_due unpaidcanceled

Un abonnement entre par incomplete ou trialing, et sort par unpaid ou canceled.

StatutSens
incompletePremière facture émise, jamais payée. Aucun accès n’a jamais été accordé.
trialingEssai en cours. currentPeriodEnd vaut trialEnd.
activePayé jusqu’à currentPeriodEnd.
past_dueL’échéance est passée et la facture de renouvellement est toujours open.
unpaidGrâce épuisée, facture irrécouvrable.
canceledTerminé. endedAt est posé.

Numérotation

À la finalisation, chaque facture reçoit un numéro : LSB-{ANNÉE}-{séquence}.

LSB-2026-0001
LSB-2026-0002
LSB-2026-0003

La séquence est sans trou, et propre à chaque (marchand, environnement, année). Elle repart à 0001 au 1er janvier.

Impayés

lahsab ne relance pas votre client à votre place. Quand l’échéance est dépassée et que la facture reste open, il émet invoice.overdue : le payload porte le nom, le téléphone, le montant, l’échéance et le lien de paiement, de quoi composer votre propre rappel (un message, un appel) sans un appel API de plus. Ensuite, deux réglages du marchand, dans les réglages du tableau de bord, pilotent la suite :

RéglageDéfautRôle
gracePeriodDays7Le temps laissé au client pour payer après l’échéance.
dunningExhaustedBehaviorcancelCe qu’il advient de l’abonnement en fin de grâce.

En fin de grâce, la facture devient uncollectible, lahsab émet invoice.marked_uncollectible, et l’abonnement suit le réglage :

dunningExhaustedBehaviorEn fin de grâce
cancelL’abonnement passe canceled.
mark_unpaidIl passe unpaid, la facture devient uncollectible.
leave_activeOn ne touche à rien, vous gérez.

Le portail client

POST /portal-sessions renvoie une URL à durée de vie courte (1 heure) où votre client retrouve son abonnement, ses factures et un bouton pour payer. C’est la page « gérer mon abonnement » que vous n’avez pas à construire.

{
  "url": "https://dashboard.lahsab.com/portal/ps_7f3c2e1a9b4d4e6a8c1f2d3e",
  "expiresAt": "2026-07-13T11:00:00.000Z"
}

Et ensuite ?

  • Brancher invoice.paid et vérifier les signatures : Webhooks.
  • Tous les endpoints, champs et erreurs : Référence API.