API de integración
Esta API permite a un conector de tienda en línea leer el catálogo y el stock de Nadigit IMS, reservar stock para un carrito y registrar pedidos. A cambio, Nadigit IMS avisa al conector mediante webhooks firmados. Es la API que usa el conector Bagisto, y la que sirve para desarrollar un conector para otra plataforma.
Esta página solo documenta la API de integración: las demás API de Nadigit IMS están reservadas a la consola web.
Antes de empezar
- Plan Enterprise.
- Una integración declarada y aprovisionada en Integraciones de e-commerce. El aprovisionamiento proporciona la configuración del conector. Vea Integración e-commerce.
| Campo de la configuración | Uso |
|---|---|
backendUrl | Dirección base de la API: las rutas siguientes se añaden a ella |
keycloakTokenUrl | Dirección para obtener los tokens |
keycloakClientId, keycloakClientSecret | Credenciales del conector |
webhookSecret | Secreto para verificar los webhooks |
Autenticación
El conector obtiene un token de acceso con el flujo OAuth 2.0 client
credentials y lo envía en la cabecera Authorization de cada llamada.
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>
- El token es de corta duración (
expires_in, en segundos): consérvelo y renuévelo justo antes de que caduque, en lugar de pedir uno en cada llamada. - Alcance. El conector solo accede a la API de integración, y solo para el almacén y la tienda vinculados a la integración. El almacén nunca es un parámetro de la petición: lo fija la identidad del conector.
Convenciones
- Intercambios en JSON, codificación UTF-8.
- El SKU es la referencia del artículo en Nadigit IMS, única en el almacén.
- Las cantidades se expresan en la unidad de venta del artículo, con
decimales para los artículos vendidos por peso o medida (
fractional). - Los importes están en la moneda de la organización.
Puntos de acceso
| Método | Ruta | Papel |
|---|---|---|
GET | /api/ecommerce/catalog | Catálogo publicable, paginado |
GET | /api/ecommerce/catalog/{sku} | Un artículo |
GET | /api/ecommerce/stock?skus=… | Stock de 200 artículos como máximo |
PUT | /api/ecommerce/reservations/{cartId} | Reservar el stock de un carrito |
POST | /api/ecommerce/orders | Registrar un pedido |
POST | /api/ecommerce/orders/{reference}/cancel | Anular un pedido |
Catálogo
GET /api/ecommerce/catalog?page=0&size=100
GET /api/ecommerce/catalog/ACC-POIG-BL
El catálogo contiene los artículos activos y físicos del almacén
vinculado; los servicios quedan excluidos. La lista está paginada (page desde
0, size de 1 a 200, 100 por defecto) y ordenada por SKU. Un SKU desconocido,
inactivo o de otro almacén devuelve 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 lista devuelve estos objetos en una página (content, totalElements,
totalPages, number, size). El catálogo aún no ofrece un filtro «modificado
desde»: sincronícelo completo y apóyese en los webhooks para los cambios.
Stock
GET /api/ecommerce/stock?skus=ACC-POIG-BL,SIL-NEU-280,DESCONOCIDO
[
{ "sku": "ACC-POIG-BL", "found": true, "sellable": 148.0, "reservedByOthers": 2.0, "allocatable": 146.0 },
{ "sku": "DESCONOCIDO", "found": false, "sellable": null, "reservedByOthers": null, "allocatable": null }
]
| Campo | Significado |
|---|---|
sellable | Stock vendible en el almacén |
reservedByOthers | Cantidad reservada por otros carritos o ventas en curso |
allocatable | Lo que aún puede venderse: la cantidad que mostrar |
found | false para un SKU desconocido: retírelo de la venta |
Hasta 200 SKU por llamada, separados por comas.
Reservas
Reservar el stock de un carrito evita vender en línea un artículo que ya no está. Llame a este punto de acceso en cada cambio del carrito y justo antes del pago.
PUT /api/ecommerce/reservations/carrito-8841
Content-Type: application/json
{ "lines": [ { "sku": "ACC-POIG-BL", "quantity": 2 } ] }
204: la reserva del carrito se sustituye por estas líneas.409: el pago debe bloquearse.
{
"code": "insufficient_stock",
"message": "…",
"lines": [ { "sku": "ACC-POIG-BL", "requested": 2, "allocatable": 1 } ]
}
code vale insufficient_stock (stock insuficiente, línea por línea) o
reservations_disabled (las reservas flexibles están desactivadas en Nadigit
IMS). Una reserva caduca por sí sola: un carrito abandonado libera su stock sin
acción del conector.
Pedidos
POST /api/ecommerce/orders
Content-Type: application/json
{
"reference": "WEB-1000231",
"cartId": "carrito-8841",
"orderDate": "2026-09-24T18:40:00",
"customer": { "email": "cliente@ejemplo.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…"
}
| Campo | Obligatorio | Nota |
|---|---|---|
reference | Sí | Referencia del pedido en la tienda, 64 caracteres como máximo. Sirve de clave de unicidad |
cartId | No | El carrito cuya reserva se consume |
customer | No | Cliente encontrado por correo, o creado |
lines | Sí | SKU y cantidades. unitPrice es indicativo |
shipping, declaredTotal | No | Indicativos, comparados con el total recalculado |
paymentMethod | No | Vea más abajo |
paymentReference | No | Referencia de la transacción, guardada en el pago |
Comportamiento:
- Nadigit IMS recalcula precios e impuestos. Si
declaredTotaldifiere, el pedido se acepta ypriceMismatchvaletrue. - El pedido descuenta el stock del almacén y se vincula a la tienda.
- Idempotencia: reenviar la misma
referencedevuelve el pedido existente (alreadyExisted: true), sin crear un duplicado. Un conector puede repetir un pedido con total seguridad. paymentMethod:card,wallet,stripe,paypal,online,transferocashregistran un pago: el pedido queda pagado.cod(contra reembolso), un valor vacío o desconocido no registran nada: el pedido queda impagado.
{
"reference": "WEB-1000231",
"status": "Processing",
"paymentStatus": "PAID",
"totalAmount": 108.0,
"dutyFreeAmount": 90.0,
"taxAmount": 18.0,
"alreadyExisted": false,
"priceMismatch": false,
"declaredTotal": 108.0
}
Anulación
POST /api/ecommerce/orders/WEB-1000231/cancel
Anula el pedido y devuelve su stock al almacén. Un pedido ya anulado devuelve
éxito con alreadyExisted: true; una referencia desconocida en el almacén
devuelve 404. El reembolso eventual no es automático.
Webhooks
Nadigit IMS llama al conector en la URL de la tienda + Ruta del webhook
declaradas en la integración, con POST, tras cada cambio registrado.
Eventos
product.updated: un artículo ha cambiado (stock, precio, estado).
{ "event": "product.updated", "action": "upsert", "sku": "ACC-POIG-BL", "occurredAt": "2026-09-24T18:41:07Z" }
action: "upsert": vuelva a leer el artículo conGET /api/ecommerce/catalog/{sku}y aplíquelo.action: "unpublish": retire el artículo de la venta (desactivado, agotado o ausente del almacén).
order.status: un pedido web ha cambiado de estado.
{ "event": "order.status", "reference": "WEB-1000231", "status": "Delivered", "paymentStatus": "PAID", "occurredAt": "2026-09-25T10:02:13Z" }
status | Significado |
|---|---|
Processing | En preparación (estado al registrarse) |
Delivered, Completed | Entregado, terminado |
Canceled | Anulado |
Return_Pending, Returned, Partial_Return | Devolución en curso, devuelto, devuelto parcialmente |
paymentStatus vale UNPAID, PARTIALLY_PAID o PAID.
Verificar la firma
Cada webhook lleva dos cabeceras:
X-Timestamp: la hora de envío, en segundos desde el 1 de enero de 1970;X-Signature:HMAC-SHA256(webhookSecret, X-Timestamp + "." + cuerpo), en hexadecimal en minúsculas.
Recalcule la firma sobre el cuerpo bruto recibido, compárela en tiempo constante y rechace una marca de tiempo demasiado antigua (por ejemplo, más de cinco minutos).
$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;
}
Respuesta y reintentos
Responda 2xx rápidamente y procese después el webhook. En caso de fallo o sin
respuesta, Nadigit IMS reintenta varias veces espaciando los intentos. Cada
intento queda registrado en Nadigit IMS. Procese los webhooks de forma
idempotente: el mismo evento puede llegar dos veces.
Códigos de error
| Código | Causa |
|---|---|
400 | Petición no válida (campo que falta o mal formado); el cuerpo detalla los campos |
401 | Token ausente, caducado o no válido: renueve el token |
403 | Acceso denegado, o función no incluida en el plan de la instalación |
404 | SKU o pedido desconocido en el almacén vinculado |
409 | Stock insuficiente o reservas desactivadas (vea Reservas) |
503 | Instalación sin licencia activa |
Los errores distintos de 409 devuelven un cuerpo de esta forma:
{ "timestamp": "…", "status": 403, "error": "Forbidden", "message": "…", "path": "/api/ecommerce/catalog", "errorCode": "…" }