Cycle de vie & webhooks
ShopiMind notifie votre serveur des événements d'une boutique via des webhooks signés. Vous déclarez une URL par événement ; ShopiMind y envoie un POST.
Vérification de la signature (HMAC)
Chaque webhook porte deux en-têtes :
X-Shopimind-Signature : <hex sha256>
X-Shopimind-Timestamp : <secondes unix>La signature est :
HMAC-SHA256("<timestamp>.<corps brut>", WEBHOOK_SECRET) → digest hexVérifiez sur les octets bruts
Calculez le HMAC sur le corps brut exact de la requête (avant tout JSON.parse), sinon la signature ne concordera pas. Comparez en temps constant.
const crypto = require('crypto');
function verify(rawBody, signature, timestamp, secret, toleranceSeconds = 300) {
// Anti-rejeu : rejeter les requêtes trop anciennes
const skew = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
if (skew > toleranceSeconds) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
const a = Buffer.from(signature, 'hex');
const b = Buffer.from(expected, 'hex');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Le WEBHOOK_SECRET vous est communiqué par ShopiMind lors de l'enregistrement de votre intégration. La tolérance d'horloge (anti-rejeu) est de votre ressort — 5 minutes est une valeur sûre.
Si la vérification échoue, répondez 401. ShopiMind ne retente pas les erreurs 4xx.
Contrat de réponse
Chaque webhook (hors étapes OAuth) doit répondre :
- HTTP
200exactement —201/204sont traités comme un échec ; - un corps JSON contenant
{ "success": true }.
En cas d'impossibilité de traitement, renvoyez 200 avec { "success": false, "error": "…" } : ShopiMind traite l'échec sans vous « retry-bomber ».
| Événement | Retry en cas d'échec |
|---|---|
install | Aucun — un échec déclenche un rollback côté ShopiMind (installation annulée, clé supprimée) |
autres (activate, deactivate, …) | 1 nouvelle tentative (~2 s) sur erreur transitoire (timeout, 5xx, connexion) |
Les événements
Tous les événements de cycle de vie arrivent sur la même URL (install, uninstall, activate, deactivate, config_updated), distingués par le champ event.
install — Type A
{
"event": "integration.installed",
"id_shop": 12345,
"id_shop_integration": 678,
"integration_slug": "my-integration",
"shop_domain": "https://shop.example.com",
"shop_name": "Boutique Example",
"access_token": "int4f2a9.dGhpcy1pcy1hLXNlY3JldA",
"installed_at": "2026-05-27T10:15:00.000Z"
}Persistez immédiatement le access_token (clé API) et le id_shop_integration. Statut initial : inactif (la clé est encore révoquée).
activate
{
"event": "integration.activated",
"id_shop": 12345,
"id_shop_integration": 678,
"integration_slug": "my-integration",
"activated_at": "2026-05-27T10:20:00.000Z",
"configs": { "api_url": "https://...", "api_token": "valeur-déchiffrée" }
}Les configs sont transmis déchiffrés. À partir d'ici, votre access_token est utilisable pour appeler l'API ShopiMind.
config_updated
{
"event": "integration.config_updated",
"id_shop_integration": 678,
"integration_slug": "my-integration",
"configs": { "...": "..." },
"updated_at": "2026-05-27T10:35:00.000Z"
}deactivate / uninstall
{ "event": "integration.deactivated", "id_shop_integration": 678, "deactivated_at": "..." }{ "event": "integration.uninstalled", "id_shop_integration": 678, "uninstalled_at": "..." }Après deactivate, la clé API est re-révoquée ; après uninstall, configuration supprimée et clé révoquée définitivement.
Type B — OAuth
À utiliser si l'utilisateur doit d'abord s'authentifier chez vous. Le flux :
L'utilisateur clique « Installer » dans ShopiMind.
ShopiMind redirige le navigateur vers votre page de consentement (
oauth_connect_url) avec deux paramètres :state(jeton opaque, durée de vie 10 min) etredirect_uri.Vous authentifiez l'utilisateur (votre propre UI).
Vous redirigez le navigateur vers le
redirect_urifourni, en renvoyant lestateinchangé et en ajoutantexternal_account_id(+external_account_name) :<redirect_uri>?state=<inchangé>&external_account_id=acct_123&external_account_name=Compte%20de%20JeanEn cas de refus :
<redirect_uri>?state=<inchangé>&error=access_denied.ShopiMind valide le
state, génère la clé API et vous envoie le webhookinstall— variante OAuth, sansid_shopniid_shop_integration:json{ "event": "integration.installed", "external_account_id": "acct_123", "external_account_name": "Compte de Jean", "shop_domain": "https://shop.example.com", "access_token": "int4f2a9.…", "installed_at": "2026-05-27T10:15:00.000Z" }
Spécificités Type B
- Indexez l'installation sur
external_account_idjusqu'à ce queactivateapporte leid_shop_integration. - Transmettez le
statetel quel (ne le décodez pas, ne le journalisez pas). - N'hardcodez jamais le
redirect_uri— utilisez celui reçu à chaque appel.
Le starter implémente ce flux de bout en bout (src/routes/oauth.js).
Hooks de configuration
Deux webhooks supplémentaires servent l'interface de configuration ShopiMind — détaillés dans Configuration :
test_connection— vérifier la connectivité avec les identifiants saisis.remote-data/<resource>— peupler dynamiquement des listes déroulantes.