Aller au contenu principal
Documentation API

Documentation de l'API

Encaissez par carte ou en espèces, gérez clients, abonnements et remboursements depuis votre propre système. Chaque module expliqué, chaque endpoint détaillé, tout testable en sandbox.

modules
12modules
endpoints
62endpoints
objets documentés
48objets documentés
version de l'API
v1.0.0version de l'API

Référence générée depuis la spécification OpenAPI publiée par l'API — elle ne peut pas diverger du contrat.

Environnements

Une seule base d'URL — la même en test et en production. C'est votre clé qui choisit l'environnement : une clé de test ne peut pas toucher la production, et l'inverse est vrai aussi. Vous ne changez donc jamais d'URL entre vos essais et votre mise en service — vous changez de clé.

https://api-psp.charipay.ma

Sandboxchari_sk_test_…

Aucun mouvement d'argent réel. La carte de test est acceptée, les webhooks partent normalement, et l'endpoint de forçage d'échéance d'abonnement n'existe que là.

Productionchari_sk_live_…

Argent réel. L'accès doit être activé sur votre compte : tant qu'il ne l'est pas, l'API répond 403 avec le code PRODUCTION_ACCESS_NOT_ENABLED.

Carte de test sandbox

La sandbox tourne sur de vrais rails, contre un environnement de test : les parcours sont réels, l'argent non. Une seule carte y est acceptée.

Seul ce PAN est accepté. Tout autre numéro — y compris les 4242… d'autres plateformes — est rejeté en amont — en général avec un 502 — et le code stable BAAS_CHARI_ERROR. Si vous rencontrez cette erreur en test, vérifiez d'abord la carte saisie.

Ce que l'API vous permet de faire

En clair

Tout ce que vous faites dans le portail ChariPay — encaisser, rembourser, suivre l'argent — votre logiciel peut le faire seul, par l'API. Cette page vous dit par où commencer selon votre produit.

Vous n'avez pas besoin de tout intégrer : choisissez la porte qui correspond à votre façon de vendre, le reste de l'API s'ajoute quand votre produit le demande.

Le lien de paiement

Pour qui

Vous vendez sans site web — par WhatsApp, au téléphone, sur devis ou au comptoir.

Techniquement

POST /v1/payment-links renvoie une URL de paiement hébergée à partager, avec QR code et affiche PDF inclus. Un appel suffit.

La session de paiement

Pour qui

Vous avez un site e-commerce et voulez un paiement dans le fil de la commande.

Techniquement

Une commande devient une session : vous redirigez l'acheteur vers le checkout hébergé et confirmez par webhook. Aucune donnée de carte chez vous.

Le checkout direct

Pour qui

Vous voulez dessiner votre propre écran de paiement, champ par champ.

Techniquement

Verify → submit → 3-D Secure → return : vous pilotez chaque étape. En contrepartie, la conformité carte entre dans votre périmètre.

Démarrer — du néant à votre premier appel

En clair

Créez un compte de test gratuit et faites votre premier appel en quelques minutes — sans parler à personne, sans engagement, sans carte bancaire.

Vous n'avez besoin de rien pour commencer : ni compte existant, ni e-mail au support, ni rendez-vous commercial. Huit étapes séparent une page blanche d'un appel authentifié en sandbox.

1. Inscrivez-vous. POST /api/public/sandbox-signup est public. Vous y déclarez votre e-mail professionnel ; nous vous envoyons un lien d'activation.

2. Activez votre compte. Le lien reçu porte un jeton d'activation, que vous passez à POST /api/public/sandbox-signup/set-password avec le mot de passe que vous choisissez. Ce jeton n'est jamais renvoyé par une réponse d'API : il n'existe que dans l'e-mail.

3. Connectez-vous au portail. POST /api/v1/auth/login vérifie vos identifiants. Selon les permissions de votre compte, le portail peut d'abord vous faire enrôler une application TOTP (QR code, puis un code à six chiffres à chaque connexion) — conservez vos codes de récupération.

4. Choisissez votre entreprise. POST /api/v1/auth/select-company confirme l'entreprise sur laquelle vous travaillez : c'est cette réponse — pas le login — qui porte votre jeton de session (JWT).

5. Passez la ré-authentification. Créer une clé d'API est une action sensible : POST /api/v1/auth/step-up émet un jeton de confirmation à courte durée de vie, exigé à l'étape 7.

6. Regardez les permissions disponibles. GET /api/v1/api-keys/available-permissions liste les portées que votre compte peut accorder à une clé. N'accordez que ce que votre intégration appelle réellement.

7. Créez votre clé. POST /api/v1/api-keys la génère avec les permissions choisies. La clé complète ne s'affiche qu'une fois, à cet instant — mettez-la immédiatement dans un coffre ou une variable d'environnement.

8. Vérifiez que tout tient. GET /v1/wallet avec votre nouvelle clé : si le solde vous répond, votre intégration est authentifiée et vous pouvez commencer.

Notez que les étapes 1 à 3 ne sont pas authentifiées, que les étapes 4 à 7 relèvent de la session du portail, et que l'étape 8 est la première à utiliser la clé d'API. C'est le seul endroit de l'API où ces trois régimes se croisent.

Un détail qui évite une heure de confusion : les endpoints d'inscription et du portail (étapes 1 à 7) ont leur propre format d'erreur, avec des codes ERR-XXXX. L'enveloppe décrite au guide Erreurs vaut pour l'API marchande — celle que votre clé appelle.

Authentification

En clair

Une clé secrète identifie votre logiciel à chaque appel, comme un badge d'accès. La clé de test et la clé de production sont deux badges différents : impossible de toucher l'argent réel avec la clé de test.

Chaque requête porte votre clé d'API dans un en-tête. Il n'y a ni jeton à rafraîchir, ni flux OAuth : la clé suffit, et c'est elle qui détermine l'environnement.

Une clé de sandbox commence par chari_sk_test_, une clé de production par chari_sk_live_. Vous ne choisissez donc pas l'environnement dans l'URL ni dans un en-tête : vous le choisissez en choisissant la clé. C'est volontaire — cela rend impossible d'envoyer par erreur une requête de test vers la production.

Une clé est un secret. Elle vit sur votre serveur, dans une variable d'environnement ou un coffre. Elle ne doit jamais apparaître dans du code envoyé au navigateur, dans une application mobile, dans un dépôt git, ni dans une capture d'écran.

Les endpoints /checkout/* font exception : ils sont portés par la session de paiement elle-même, protégée par une clé de vérification à usage unique. N'y envoyez jamais votre clé d'API — ce sont les seuls appels que le navigateur de l'acheteur exécute.

Idempotence — rejouer sans doubler

En clair

Si le réseau coupe et que votre système renvoie la même demande deux fois, votre client ne sera jamais débité deux fois. Voici le mécanisme qui le garantit — et comment bien s'en servir.

Un réseau qui coupe entre votre serveur et le nôtre vous laisse dans le doute : la création est-elle passée ? En paiement, réessayer à l'aveugle est la meilleure façon de facturer deux fois le même client.

Deux mécanismes indépendants répondent à deux pannes différentes. Ils se cumulent, et le bon réflexe est de comprendre lequel vous protège de quoi.

L'en-tête Idempotency-Key protège du retry réseau. Vous l'envoyez sur vos créations ; rejouer un appel avec la même valeur renvoie le premier résultat au lieu de créer un doublon. C'est la protection contre un timeout, une coupure, un client HTTP qui réessaie tout seul.

Le champ externalId protège de la ré-émission métier. Il est unique par compte et par environnement : créer une ressource avec un externalId qui existe déjà renvoie la ressource existante en 200 OK, là où une vraie création répond 201 Created. C'est la protection contre votre propre système qui ré-émet la même intention — un job relancé, une file rejouée, un double clic côté back-office.

Testez donc le statut, pas seulement le corps : 201 signifie « je viens de la créer », 200 signifie « elle existait déjà ». Les deux sont des succès.

Les remboursements utilisent refundReference pour la même raison. Dérivez-la de l'identifiant de la commande — jamais d'un aléatoire : une référence déterministe rend le rejeu sûr, une référence tirée au hasard rembourse deux fois. Et attention aux statuts : un remboursement frais répond 202 Accepted — il s'exécute en asynchrone — et un rejeu 200. Jamais 201.

Un même externalId peut exister une fois en sandbox et une fois en production sans conflit : sa déduplication est bornée à l'environnement. La barrière Idempotency-Key de création, elle, est bornée au compte — gardez donc des clés d'idempotence distinctes entre vos tests et votre production.

Erreurs

En clair

Quand un appel échoue, l'API répond toujours dans le même format : un code stable pour votre code, un message lisible pour un humain, et un identifiant à donner au support.

Toutes les erreurs de l'API marchande (/v1) partagent la même enveloppe : un code stable que votre code peut tester, un message lisible, et un correlationId à donner au support.

Testez le code, jamais le message. Le message peut être reformulé, traduit ou précisé ; le code, lui, fait partie du contrat.

Le correlationId est renvoyé sur toutes les réponses, y compris en succès. Journalisez-le systématiquement : c'est ce qui permet de retrouver une requête précise dans nos journaux.

Les codes qui reviennent le plus souvent : VALIDATION_ERROR (400, un champ ne passe pas), UNAUTHORIZED (401, clé absente ou invalide), FORBIDDEN (403, permission manquante sur la clé), PRODUCTION_ACCESS_NOT_ENABLED (403, clé de production sur un compte pas encore activé), WALLET_NOT_ACTIVE (422, la requête est valide mais une règle métier la bloque) et RATE_LIMITED (429, ralentissez et relisez Retry-After).

Les 5xx sont de notre côté : rejouez avec votre clé d'idempotence plutôt que de créer une nouvelle ressource.

Trois conventions valent sur toute l'API : envoyez votre propre X-Request-Id — il est répété sur la réponse et devient le correlationId ; sur les endpoints exposés au public, lisez X-RateLimit-Limit, X-RateLimit-Remaining et X-RateLimit-Reset pour piloter votre débit ; et toute URL que vous déclarez (retour, notification) doit être en https://, sous peine de 400. Enfin, tolérez les valeurs d'enum nouvelles et les champs optionnels absents — jamais null : c'est ainsi que l'API évolue sans vous casser.

json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "amount: must be greater than 0"
  },
  "correlationId": "b0c1e2d3-4f56-7890-abcd-ef0123456789"
}
StatutCodesSignification
400INVALID_IDEMPOTENCY_KEY · MISSING_PARAMETER · VALIDATION_ERRORRequête mal formée ou validation échouée (code = VALIDATION_ERROR / MISSING_PARAMETER).
401INVALID_TOKEN · UNAUTHORIZEDClé d'API manquante ou invalide (code = UNAUTHORIZED).
403FORBIDDEN · PRODUCTION_ACCESS_DENIEDAuthentifié, mais la clé ne dispose pas de la permission requise, ou l'accès à la production n'est pas activé (code = FORBIDDEN / PRODUCTION_ACCESS_DENIED).
404OPERATION_NOT_FOUND · ORDER_NOT_FOUND · SESSION_NOT_FOUNDAucun lien de ce type pour ce compte.
409IDEMPOTENCY_CONFLICT · SESSION_ALREADY_CONSUMED · SESSION_NOT_ACTIVESession déjà payée ou annulée (SESSION_ALREADY_CONSUMED / SESSION_NOT_ACTIVE).
410SESSION_EXPIREDSession expirée (SESSION_EXPIRED).
422PAYMENT_METHOD_CONSENT_REQUIRED · WALLET_NOT_ACTIVESyntaxiquement valide, mais une règle métier la bloque (p. ex. WALLET_NOT_ACTIVE).
429RATE_LIMITEDLimite de débit dépassée (code = RATE_LIMITED). Réessayez après le délai indiqué par l'en-tête Retry-After.

Listes et pagination

En clair

Les listes longues — transactions, clients — arrivent page par page. Voici comment les parcourir sans rien rater.

Les endpoints de liste sont paginés et renvoient toujours la même enveloppe : le contenu de la page, le numéro de page, sa taille, le total d'éléments et le total de pages.

La numérotation commence à zéro. Ne présumez jamais qu'une liste tient sur une page — parcourez-la jusqu'à totalPages.

Sur les transactions, préférez la pagination par curseur pour un parcours long : demandez limit, puis repassez cursor = nextCursor tant que hasMore est vrai. Elle ne saute ni ne répète aucune ligne quand de nouvelles transactions arrivent pendant votre parcours.

Pour un export volumineux, l'endpoint dédié de transactions renvoie un CSV en flux plutôt qu'une suite de pages (jusqu'à 10 000 lignes — au-delà, découpez par période avec from/to) : c'est plus rapide et plus sûr pour un rapprochement comptable.

Webhooks

En clair

Plutôt que d'interroger l'API en boucle pour savoir si un paiement a abouti, laissez-la vous prévenir : dès que l'argent bouge, ChariPay appelle votre serveur avec un message signé. C'est la pièce la plus importante d'une intégration fiable.

Un paiement n'aboutit pas au moment où vous l'appelez : l'acheteur passe par sa banque, valide un 3-D Secure, revient. Le résultat vous parvient donc par webhook, et c'est lui qui fait foi — pas la redirection du navigateur, qu'un acheteur peut interrompre en fermant son onglet. Le sondage est un repli, pas la source de vérité.

Vous déclarez vos URL de réception depuis l'API, avec une liste explicite d'événements. N'abonnez que ce que vous traitez : chaque événement inutile est une occasion de bug et une charge sur votre serveur.

L'URL doit être en HTTPS public, sur le port 443. localhost et les adresses privées sont refusés dès l'enregistrement — en développement, passez par un tunnel.

La signature. Chaque livraison porte X-CHARI-SIGNATURE : un HMAC-SHA256 en hexadécimal minuscule, calculé sur la chaîne horodatage + "." + corps brut. L'horodatage est dans X-CHARI-TIMESTAMP, en millisecondes depuis epoch.

Quatre règles font toute la sécurité de la vérification : calculez le HMAC sur les octets bruts avant tout parsing — un JSON re-sérialisé ne produit plus la même signature ; rejetez un horodatage décalé de plus de ±5 minutes, comme nous le faisons ; comparez en temps constant (timingSafeEqual), jamais avec === ; et pendant une rotation de secret, acceptez l'une ou l'autre des deux signatures.

Pendant une rotation, tant que l'ancien secret est dans sa fenêtre de grâce, nous envoyons le même corps signé deux fois : X-CHARI-SIGNATURE avec l'ancien secret et X-CHARI-SIGNATURE-NEXT avec le nouveau. Vérifiez avec le secret que vous détenez, puis basculez.

Dédupliquez sur Chari-Event-Id, pas sur Chari-Webhook-Id. C'est la distinction qui coûte le plus cher quand on se trompe : un même événement logique peut être livré plusieurs fois, chaque tentative reçoit son propre Chari-Webhook-Id — qui change donc à chaque essai — mais toutes portent le même Chari-Event-Id. Se dédupliquer sur le mauvais, c'est traiter deux fois le même paiement.

La livraison est au moins une fois, et peut arriver dans le désordre : vous verrez des répétitions, c'est par conception. Ne répondez 2xx qu'une fois la mise à jour métier et l'enregistrement de déduplication tous deux validés — toute réponse non-2xx est retentée. Faites le travail lourd en tâche de fond : un traitement de dix secondes finit par déclencher des relectures et, à terme, la suspension de votre endpoint. La première tentative part immédiatement, puis 1 min → 5 min → 30 min → 1 h → toutes les 6 h, jusqu'à 16 tentatives sur environ 72 heures ; au-delà, la livraison est marquée échouée. Après une indisponibilité plus longue, réconciliez par GET /v1/transactions plutôt que d'attendre un webhook qui ne reviendra plus.

Vos champs metadata et externalId reviennent sur les événements de la ressource qui les portait : c'est ce qui vous permet de rapprocher sans stocker nos références.

javascript
const crypto = require('crypto');

function verify(rawBody, signature, timestamp, secret) {
  // Fenêtre anti-rejeu de ±5 minutes — l'horodatage est en millisecondes.
  if (Math.abs(Date.now() - Number(timestamp)) > 5 * 60 * 1000) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  // Une signature malformée doit répondre false — jamais jeter (500 → retentatives).
  if (!/^[0-9a-f]{64}$/i.test(signature)) return false;

  // Comparaison en temps constant : jamais `===`.
  return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(signature, 'hex'));
}

Passer en production

En clair

Le jour du lancement, vous ne changez qu'une seule chose : la clé. Cette liste vérifie que tout le reste est prêt avant de basculer.

La sandbox est ouverte immédiatement ; la production s'active sur votre compte après vérification de votre entreprise. Tant qu'elle ne l'est pas, une clé de production reçoit un 403 explicite.

Avant de basculer, quelques vérifications valent le temps qu'elles prennent : votre traitement des webhooks est idempotent et vérifie la signature ; vos créations envoient une clé d'idempotence ; vous journalisez le correlationId de chaque appel ; vos clés vivent dans un coffre, pas dans votre dépôt ; et vous avez testé au moins un remboursement et un échec de paiement, pas seulement le cas nominal.

Le jour de la bascule, vous ne changez qu'une chose : la clé. Les URL, les payloads et les codes d'erreur sont identiques.

Les modules de l'API

Douze modules, ordonnés comme une intégration réelle : commencez par les liens de paiement ou le checkout, ajoutez les webhooks, puis le reste au rythme de votre produit. Chaque module a sa page, avec ses endpoints, ses champs et ses exemples dans quatre langages.

7 endpoints

Liens de paiement

Un montant, une description, un lien à envoyer. Le client paie sur une page hébergée par ChariPay — vous n'hébergez aucune donnée de carte.

Consulter le module

4 endpoints

Sessions de paiement

Une commande e-commerce devient une session : vous redirigez l'acheteur vers le checkout hébergé et vous récupérez le résultat.

Consulter le module

4 endpoints

Checkout direct

Les appels que la page de paiement exécute elle-même : vérification de la session, envoi des données de carte, retour de 3-D Secure.

Consulter le module

4 endpoints

Transactions

Tout ce qui est entré et sorti : paiements, remboursements, versements, frais. Avec le détail d'une opération et sa frise chronologique.

Consulter le module

3 endpoints

Remboursements

Rembourser tout ou partie d'un paiement abouti, avec motif et votre propre référence — débité du solde et tracé comme une opération liée au paiement d'origine.

Consulter le module

4 endpoints

Wallet

Le solde de votre compte de paiement, son RIB au nom du marchand, l'attestation à télécharger, et son alimentation.

Consulter le module

8 endpoints

Clients

Le référentiel de vos clients finaux et de leurs moyens de paiement enregistrés — réutilisés par les abonnements et les encaissements récurrents.

Consulter le module

6 endpoints

Produits

Un catalogue simple, avec ses commandes, pour vendre par lien de paiement ou checkout — sans construire de boutique.

Consulter le module

9 endpoints

Abonnements

Un prélèvement récurrent sur un moyen de paiement enregistré, avec ses échéances, ses pauses et ses relances.

Consulter le module

10 endpoints

Webhooks

Vos URL de réception, leurs secrets de signature, la liste des événements émis et le détail de chaque livraison.

Consulter le module

1 endpoint

Catalogue d'événements

La liste de tous les types d'événements auxquels vous pouvez vous abonner.

Consulter le module

2 endpoints

Parcours client

Ce qui s'est passé entre l'ouverture d'un lien ou d'une session et le paiement : les étapes franchies, les abandons, l'origine du trafic.

Consulter le module

Ressources à télécharger

De quoi travailler hors du navigateur : la collection à exécuter, le contrat à générer, et le pack à donner à un assistant de code.

Glossaire

Les mots que cette documentation emploie dans un sens précis.

Clé d'API
Le secret qui authentifie vos appels et choisit l'environnement. Préfixe chari_sk_test_ en sandbox, chari_sk_live_ en production.
externalId
Votre propre identifiant, que vous attachez à une ressource pour la retrouver depuis votre système. Unique par compte et par environnement.
Idempotency-Key
En-tête facultatif sur les créations. Deux appels portant la même clé produisent une seule ressource.
correlationId
Identifiant renvoyé sur chaque réponse. C'est ce que le support vous demandera pour retrouver un appel.
Lien de paiement
Une page de paiement hébergée par ChariPay, créée en un appel, partageable par lien, QR code ou affiche.
Session de paiement
Une commande e-commerce transformée en tunnel de paiement, avec des URL de retour qui vous appartiennent.
3-D Secure
L'authentification forte demandée par la banque de l'acheteur. Elle interrompt le parcours, ce qui est la raison d'être des webhooks.
Webhook
Une notification signée que nous envoyons à votre serveur quand un événement se produit. C'est la source de vérité d'un paiement.
Wallet
Le compte de paiement du marchand — tenu par Chari Money, établissement de paiement agréé par Bank Al-Maghrib — avec son solde et son RIB au nom du marchand.
MAD
Le dirham marocain. Tous les montants de l'API sont exprimés en unités principales — 249.00 signifie 249 dirhams.

Parler à un intégrateur

Une question sur l'intégration ?

Notre équipe technique répond aux intégrateurs, du premier appel en sandbox jusqu'à la mise en production.