Référence technique des connecteurs
L'API v1 des connecteurs, les événements, la signature des webhooks et les codes d'erreur, pour les développeurs et intégrateurs.
Cette page s'adresse aux développeurs et intégrateurs. Si vous utilisez le module Odoo, vous n'en avez pas besoin : le module fait tout cela pour vous. Elle sert à comprendre ce que fait le module, à diagnostiquer un problème, ou à brancher un logiciel qui n'a pas encore de connecteur.
Adresse et authentification#
https://pismile.com/api/connect/v1Chaque requête porte la clé de l'installation dans l'en-tête Authorization :
Authorization: Bearer pmc_test_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxPréfixe | Mode | Effet |
|---|---|---|
| test | Aucune demande réelle. Résultat simulé en une dizaine de secondes. Jamais facturé. |
| production | Vraies demandes PI-SPI, avec l'accès participant de l'organisation. Organisation vérifiée requise. |
Toutes les réponses sont en JSON, en UTF-8. Les montants sont des entiers en F CFA. Les dates sont au format ISO 8601 en UTC.
Vérifier la clé — GET /moi#
Renvoie ce que la clé permet. C'est ce qu'appelle le bouton Tester la connexion d'Odoo.
curl https://pismile.com/api/connect/v1/moi \
-H "Authorization: Bearer $PISMILE_CLE"{
"installation": { "id": "k3v9x2m1q8ab", "nom": "Odoo Boutique Awa", "mode": "test" },
"organisation": "Boutique Awa",
"connecteur": { "code": "odoo", "nom": "Odoo" },
"offre": { "code": "standard", "nom": "Standard" },
"fonctionnalites": ["DEMANDE_SUR_FACTURE", "FACTURE_PAYEE", "RAPPROCHEMENT"],
"encaissementsInclus": 500,
"bloquant": false
}encaissementsInclus est absent si l'offre est illimitée.
Créer une demande — POST /demandes#
Champ | Obligatoire | Description |
|---|---|---|
| oui | Numéro de facture. 1 à 64 caractères : lettres, chiffres et |
| oui | Entier en F CFA, de 1 à 5 000 000. |
| oui | Alias PI-SPI du client : téléphone ( |
| non | Nom du client dans votre logiciel (affichage dans la console). |
| non | Texte affiché dans la banque du client, après le nom de l'organisation. Par défaut « Facture <reference> ». |
| non | Date limite : |
| non | Jusqu'à 20 paires clé/valeur libres, renvoyées telles quelles dans les événements. |
curl -X POST https://pismile.com/api/connect/v1/demandes \
-H "Authorization: Bearer $PISMILE_CLE" \
-H "Content-Type: application/json" \
-d '{
"reference": "FAC/2026/00001",
"montant": 35000,
"payeurAlias": "+22890123456",
"payeurNom": "Kofi Mensah",
"metadonnees": { "odoo_move_id": "42" }
}'{
"id": "3f6c2a1e-9b7d-4c58-a0e2-7d41b9c86f15",
"reference": "FAC/2026/00001",
"montant": 35000,
"devise": "XOF",
"statut": "EN_ATTENTE",
"payeur": { "alias": "+228••••3456", "nom": "Kofi Mensah" },
"motif": "Boutique Awa - Facture FAC/2026/00001",
"creeLe": "2026-10-16T09:12:03Z",
"echeance": "2026-10-19T09:12:03Z",
"metadonnees": { "odoo_move_id": "42" },
"test": true
}Lire une demande#
# Par identifiant pismile
curl https://pismile.com/api/connect/v1/demandes/3f6c2a1e-9b7d-4c58-a0e2-7d41b9c86f15 -H "Authorization: Bearer $PISMILE_CLE"
# Par votre référence
curl "https://pismile.com/api/connect/v1/demandes?reference=FAC/2026/00001" -H "Authorization: Bearer $PISMILE_CLE"Statuts#
| Signification | Final |
|---|---|---|
| Envoyée, en attente de la réponse du client. | non |
| Payée. | oui |
| Refusée par le client ou sa banque. | oui |
| Annulée avant paiement. | oui |
| Date limite dépassée sans réponse. | oui |
| Refusée par le participant avant l'envoi au client. | oui |
referencePispi est l'identifiant de bout en bout PI-SPI : c'est lui qui figure sur le relevé bancaire, utilisez-le pour le rapprochement.
Les événements#
Un événement est émis quand une demande atteint un statut final, et quand vous cliquez sur Envoyer un test dans la console.
| Quand |
|---|---|
| Le client a payé. |
| Le client ou sa banque a refusé. |
| La demande a été annulée. |
| La date limite est passée. |
| Bouton « Envoyer un test » de la console. |
{
"id": "evt_5m2q8k1x9p3a",
"type": "demande.payee",
"creeLe": "2026-10-16T09:12:41Z",
"test": true,
"donnees": {
"id": "3f6c2a1e-9b7d-4c58-a0e2-7d41b9c86f15",
"reference": "FAC/2026/00001",
"montant": 35000,
"devise": "XOF",
"statut": "PAYE",
"payeLe": "2026-10-16T09:12:40Z",
"referencePispi": "E2E20261016091240XXXX",
"metadonnees": { "odoo_move_id": "42" },
"test": true
}
}Le champ donnees a le même format que la réponse de GET /demandes/{id}.
Recevoir par webhook#
Configurez l'adresse dans la console : installation → Événements. pismile envoie un POST JSON avec ces en-têtes :
En-tête | Contenu |
|---|---|
| Le type, par exemple |
| L'identifiant de l'événement. |
|
|
Répondez par un code 2xx en moins de 10 secondes. Sinon pismile réessaie après 1 min, 5 min, 30 min, 2 h, 6 h, 12 h et 24 h. L'adresse doit être en https et joignable depuis Internet.
Vérifier la signature#
v1 vaut HMAC-SHA256(secret, t + "." + corps) en hexadécimal, où secret est le whsec_… de l'installation et corps le corps brut de la requête, avant tout décodage JSON. Refusez un horodatage de plus de 5 minutes.
import hashlib, hmac, time
def signature_valide(secret: str, entete: str, corps_brut: bytes) -> bool:
parties = dict(p.split("=", 1) for p in (entete or "").split(",") if "=" in p)
try:
t = int(parties["t"])
except (KeyError, ValueError):
return False
if abs(time.time() - t) > 300:
return False
attendu = hmac.new(secret.encode(), f"{t}.".encode() + corps_brut, hashlib.sha256).hexdigest()
return hmac.compare_digest(attendu, parties.get("v1", ""))const crypto = require("crypto");
function signatureValide(secret, entete, corpsBrut) {
const parties = Object.fromEntries((entete || "").split(",").map((p) => p.split("=")));
const t = Number(parties.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
const attendu = crypto.createHmac("sha256", secret).update(`${t}.`).update(corpsBrut).digest("hex");
const recu = Buffer.from(parties.v1 || "", "utf8");
return recu.length === attendu.length && crypto.timingSafeEqual(Buffer.from(attendu, "utf8"), recu);
}function signatureValide(string $secret, ?string $entete, string $corpsBrut): bool {
parse_str(str_replace(',', '&', $entete ?? ''), $parties);
$t = (int) ($parties['t'] ?? 0);
if ($t === 0 || abs(time() - $t) > 300) return false;
$attendu = hash_hmac('sha256', $t . '.' . $corpsBrut, $secret);
return hash_equals($attendu, $parties['v1'] ?? '');
}Relire par curseur — GET /evenements#
Utile si votre logiciel n'est pas joignable depuis Internet, et comme filet de sécurité derrière le webhook (le module Odoo le fait toutes les 5 minutes).
Paramètre | Description |
|---|---|
| Le curseur |
| Nombre d'événements par page : 50 par défaut, 200 au plus. |
curl "https://pismile.com/api/connect/v1/evenements?apres=0&limite=50" \
-H "Authorization: Bearer $PISMILE_CLE"{
"evenements": [
{ "numero": 1287, "id": "evt_5m2q8k1x9p3a", "type": "demande.payee", "creeLe": "2026-10-16T09:12:41Z", "test": true, "donnees": { "…": "…" } }
],
"suivant": "1287",
"plus": false
}Enregistrez suivant après avoir traité les événements, et renvoyez-le au prochain appel. Si plus vaut true, rappelez tout de suite. La lecture par curseur demande la fonctionnalité « Facture payée » dans l'offre.
Erreurs#
Toutes les erreurs ont la même forme :
{ "erreur": "trop_de_demandes_en_attente", "message": "Ce client a déjà 3 demandes en attente de validation." }Le champ erreur est un code stable, à utiliser dans votre code. Le message est en français, destiné à être affiché à l'utilisateur.
HTTP |
| Que faire |
|---|---|---|
400 |
| Corrigez la requête, le message indique le champ. |
401 |
| Clé absente, invalide, révoquée ou remplacée. |
403 |
| Pas d'abonnement en cours pour ce connecteur : Plan et usage. |
403 |
| Fonction absente de l'offre. |
403 |
| Activez le module Encaissement dans la console. |
403 |
| Clé |
403 |
| Organisation désactivée : contactez le support. |
404 |
| Identifiant ou référence inconnus pour votre organisation. |
409 |
| Même référence avec un montant ou un client différent : nouvelle référence. |
409 |
| Une requête identique est en cours : relisez avec |
409 |
| Enregistrez et testez l'accès participant dans la console. |
409 |
| Quota mensuel de l'offre atteint (offre plafonnée). |
429 |
| Déjà 3 demandes en attente pour ce client. |
429 |
| Déjà 10 demandes à ce client en 24 heures. |
502 |
| Votre banque ou mobile money a refusé la demande : voir le message. |
503 |
| Service momentanément indisponible : réessayez plus tard. |
Mode test : résultats simulés#
Montant se terminant par | Résultat après ~10 s |
|---|---|
|
|
|
|
autre |
|
Cette page vous a-t-elle aidé ?
Merci, votre avis nous aide à améliorer la documentation.