API d'intégration
Cette API permet à un connecteur de boutique en ligne de lire le catalogue et le stock de Nadigit IMS, de réserver du stock pour un panier et d'enregistrer des commandes. Nadigit IMS notifie en retour le connecteur par des webhooks signés. C'est l'API qu'utilise le connecteur Bagisto ; elle sert aussi à développer un connecteur pour une autre plateforme.
Cette page ne documente que l'API d'intégration : les autres API de Nadigit IMS sont réservées à la console web.
Avant de commencer
- Formule Enterprise.
- Une intégration déclarée et provisionnée dans Intégrations e-commerce. Le provisionnement fournit la configuration du connecteur. Voir Intégration e-commerce.
| Champ de la configuration | Usage |
|---|---|
backendUrl | Adresse de base de l'API : les chemins ci-dessous s'y ajoutent |
keycloakTokenUrl | Adresse d'obtention des jetons |
keycloakClientId, keycloakClientSecret | Identifiants du connecteur |
webhookSecret | Secret de vérification des webhooks |
Authentification
Le connecteur obtient un jeton d'accès par le flux OAuth 2.0 client
credentials, puis l'envoie dans l'en-tête Authorization de chaque appel.
curl -s -X POST "$KEYCLOAK_TOKEN_URL" \
-d grant_type=client_credentials \
-d client_id="$CLIENT_ID" \
-d client_secret="$CLIENT_SECRET"
GET /api/ecommerce/catalog HTTP/1.1
Authorization: Bearer <access_token>
- Le jeton a une durée de vie courte (
expires_in, en secondes) : conservez-le et renouvelez-le juste avant son expiration, plutôt qu'à chaque appel. - Périmètre. Le connecteur n'accède qu'à l'API d'intégration, et seulement pour l'entrepôt et le magasin liés à l'intégration. L'entrepôt n'est jamais un paramètre de requête : il est fixé par l'identité du connecteur.
Conventions
- Échanges en JSON, encodage UTF-8.
- Le SKU est la référence de l'article dans Nadigit IMS, unique dans l'entrepôt.
- Les quantités sont exprimées dans l'unité de vente de l'article, avec des
décimales pour les articles vendus au poids ou à la mesure (
fractional). - Les montants sont dans la devise de l'organisation.
Points d'accès
| Méthode | Chemin | Rôle |
|---|---|---|
GET | /api/ecommerce/catalog | Catalogue publiable, paginé |
GET | /api/ecommerce/catalog/{sku} | Un article |
GET | /api/ecommerce/stock?skus=… | Stock de 200 articles au plus |
PUT | /api/ecommerce/reservations/{cartId} | Réserver le stock d'un panier |
POST | /api/ecommerce/orders | Enregistrer une commande |
POST | /api/ecommerce/orders/{reference}/cancel | Annuler une commande |
Catalogue
GET /api/ecommerce/catalog?page=0&size=100
GET /api/ecommerce/catalog/ACC-POIG-BL
Le catalogue contient les articles actifs et physiques de l'entrepôt
lié ; les services en sont exclus. La liste est paginée (page à partir de 0,
size de 1 à 200, 100 par défaut) et triée par SKU. Un SKU inconnu, inactif ou
d'un autre entrepôt renvoie 404.
{
"sku": "ACC-POIG-BL",
"name": "Poignée de fenêtre à crémone – blanc",
"description": "…",
"category": "Accessoires",
"categoryPath": ["Accessoires"],
"price": 45.0,
"taxRate": 20.0,
"taxClass": "…",
"taxInclusive": false,
"allocatable": 146.0,
"measureUnit": "Unité",
"fractional": false,
"quantityPrecision": 0,
"primaryImageUrl": "https://…",
"imageUrls": ["https://…"],
"barcodes": ["…"]
}
La liste renvoie ces objets dans une page (content, totalElements,
totalPages, number, size). Le catalogue ne propose pas encore de filtre
« modifié depuis » : synchronisez-le entièrement, puis appuyez-vous sur les
webhooks pour les changements.
Stock
GET /api/ecommerce/stock?skus=ACC-POIG-BL,SIL-NEU-280,INCONNU
[
{ "sku": "ACC-POIG-BL", "found": true, "sellable": 148.0, "reservedByOthers": 2.0, "allocatable": 146.0 },
{ "sku": "INCONNU", "found": false, "sellable": null, "reservedByOthers": null, "allocatable": null }
]
| Champ | Signification |
|---|---|
sellable | Stock vendable dans l'entrepôt |
reservedByOthers | Quantité réservée par d'autres paniers ou ventes en cours |
allocatable | Ce qui peut encore être vendu : c'est la quantité à afficher |
found | false pour un SKU inconnu : retirez-le de la vente |
Jusqu'à 200 SKU par appel, séparés par des virgules.
Réservations
Réserver le stock d'un panier évite de vendre en ligne un article déjà parti. Appelez ce point d'accès à chaque modification du panier et juste avant le paiement.
PUT /api/ecommerce/reservations/panier-8841
Content-Type: application/json
{ "lines": [ { "sku": "ACC-POIG-BL", "quantity": 2 } ] }
204: la réservation du panier est remplacée par ces lignes.409: le paiement doit être bloqué.
{
"code": "insufficient_stock",
"message": "…",
"lines": [ { "sku": "ACC-POIG-BL", "requested": 2, "allocatable": 1 } ]
}
code vaut insufficient_stock (stock insuffisant, ligne par ligne) ou
reservations_disabled (les réservations souples sont désactivées dans Nadigit
IMS). Une réservation expire d'elle-même : un panier abandonné libère son stock
sans action du connecteur.
Commandes
POST /api/ecommerce/orders
Content-Type: application/json
{
"reference": "WEB-1000231",
"cartId": "panier-8841",
"orderDate": "2026-09-24T18:40:00",
"customer": { "email": "client@exemple.ma", "firstName": "Salma", "lastName": "Idrissi", "phone": "+212600000000" },
"lines": [ { "sku": "ACC-POIG-BL", "quantity": 2, "unitPrice": 45.0 } ],
"declaredTotal": 108.0,
"paymentMethod": "card",
"paymentReference": "ch_3Q…"
}
| Champ | Obligatoire | Remarque |
|---|---|---|
reference | Oui | Référence de la commande sur la boutique, 64 caractères au plus. Sert de clé d'unicité |
cartId | Non | Le panier dont la réservation est consommée |
customer | Non | Client retrouvé par e-mail, ou créé |
lines | Oui | SKU et quantités. unitPrice est indicatif |
shipping, declaredTotal | Non | Indicatifs, comparés au total recalculé |
paymentMethod | Non | Voir ci-dessous |
paymentReference | Non | Référence de la transaction, conservée sur le paiement |
Comportement :
- Nadigit IMS recalcule les prix et les taxes. Si
declaredTotaldiffère, la commande est acceptée etpriceMismatchvauttrue. - La commande sort le stock de l'entrepôt et est rattachée au magasin lié.
- Idempotence : renvoyer la même
referencerenvoie la commande existante (alreadyExisted: true), sans créer de doublon. Un connecteur peut donc rejouer une commande en toute sécurité. paymentMethod:card,wallet,stripe,paypal,online,transferoucashenregistrent un paiement : la commande est payée.cod(paiement à la livraison), une valeur vide ou inconnue n'enregistrent rien : la commande reste impayée.
{
"reference": "WEB-1000231",
"status": "Processing",
"paymentStatus": "PAID",
"totalAmount": 108.0,
"dutyFreeAmount": 90.0,
"taxAmount": 18.0,
"alreadyExisted": false,
"priceMismatch": false,
"declaredTotal": 108.0
}
Annulation
POST /api/ecommerce/orders/WEB-1000231/cancel
Annule la commande et remet son stock dans l'entrepôt. Une commande déjà
annulée renvoie un succès avec alreadyExisted: true ; une référence inconnue
dans l'entrepôt renvoie 404. Le remboursement éventuel n'est pas automatique.
Webhooks
Nadigit IMS appelle le connecteur à l'adresse URL de la boutique + Chemin du
webhook déclarés dans l'intégration, en POST, après chaque changement
enregistré.
Événements
product.updated : un article a changé (stock, prix, état).
{ "event": "product.updated", "action": "upsert", "sku": "ACC-POIG-BL", "occurredAt": "2026-09-24T18:41:07Z" }
action: "upsert": relisez l'article avecGET /api/ecommerce/catalog/{sku}et appliquez-le.action: "unpublish": retirez l'article de la vente (désactivé, épuisé ou absent de l'entrepôt).
order.status : une commande web a changé de statut.
{ "event": "order.status", "reference": "WEB-1000231", "status": "Delivered", "paymentStatus": "PAID", "occurredAt": "2026-09-25T10:02:13Z" }
status | Signification |
|---|---|
Processing | En cours de préparation (statut à l'enregistrement) |
Delivered, Completed | Livrée, terminée |
Canceled | Annulée |
Return_Pending, Returned, Partial_Return | Retour en cours, retournée, partiellement retournée |
paymentStatus vaut UNPAID, PARTIALLY_PAID ou PAID.
Vérifier la signature
Chaque webhook porte deux en-têtes :
X-Timestamp: l'heure d'envoi, en secondes depuis le 1er janvier 1970 ;X-Signature:HMAC-SHA256(webhookSecret, X-Timestamp + "." + corps), en hexadécimal minuscule.
Recalculez la signature sur le corps brut reçu, comparez-la en temps constant, et refusez un horodatage trop ancien (par exemple plus de cinq minutes).
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $ts . '.' . $body, $webhookSecret);
if (!hash_equals($expected, $sig) || abs(time() - (int) $ts) > 300) {
http_response_code(401);
exit;
}
const crypto = require('crypto');
function isValid(rawBody, ts, sig, secret) {
const expected = crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
return sig.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
&& Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
}
Réponse et nouvelles tentatives
Répondez 2xx rapidement, puis traitez le webhook. En cas d'échec ou de
non-réponse, Nadigit IMS réessaie plusieurs fois en espaçant les tentatives.
Chaque tentative est tracée côté Nadigit IMS. Traitez les webhooks de façon
idempotente : le même événement peut arriver deux fois.
Codes d'erreur
| Code | Cause |
|---|---|
400 | Requête invalide (champ manquant ou mal formé) ; le corps détaille les champs |
401 | Jeton absent, expiré ou invalide : renouvelez le jeton |
403 | Accès refusé, ou fonction non incluse dans la formule de l'installation |
404 | SKU ou commande inconnus dans l'entrepôt lié |
409 | Stock insuffisant ou réservations désactivées (voir Réservations) |
503 | Installation sans licence active |
Les erreurs autres que 409 renvoient un corps de la forme :
{ "timestamp": "…", "status": 403, "error": "Forbidden", "message": "…", "path": "/api/ecommerce/catalog", "errorCode": "…" }