Configuration
Votre intégration déclare un schéma de configuration typé dans defineIntegration({ configSchema }). ShopiMind en génère automatiquement le formulaire d'installation dans l'interface marchand. Les valeurs saisies vous reviennent (déchiffrées) à travers le cycle de vie, et votre fonction parseSettings les transforme en réglages typés (ctx.settings) disponibles dans tous vos callbacks.
Le contrat
ConfigSchema, ConfigField, RemoteRef et les types associés sont exportés par @shopimind/integration-kit-js. Le schéma que vous écrivez est validé à la compilation : une forme incorrecte ne compile pas. Voir aussi → Le kit et → Synchroniser ses données.
Trois formes de schéma
ConfigSchema accepte l'une de ces trois structures :
steps— un assistant en plusieurs étapes (recommandé : saisir les identifiants, les tester, puis choisir les données).fields— une liste plate de champs, sur un seul écran.groups— des champs regroupés sous des intertitres ({ label?, fields }).
import type { ConfigSchema } from '@shopimind/integration-kit-js';
export const configSchema: ConfigSchema = {
steps: [
{
key: 'connection',
label: { fr: 'Connexion', en: 'Connection' },
fields: [
{ key: 'api_url', type: 'url', required: true, label: { fr: "URL de l'API", en: 'API URL' } },
{ key: 'api_token', type: 'password', required: true, sensitive: true, label: { fr: 'Jeton', en: 'Token' } },
],
on_complete: { action: 'test_connection' },
},
{
key: 'mapping',
label: { fr: 'Correspondances', en: 'Mapping' },
fields: [
{ key: 'list_id', type: 'select', required: true, label: { fr: 'Liste', en: 'List' },
remote: { resource: 'lists', label_field: 'label', value_field: 'value' } },
],
},
],
};Les label/help/description sont localisés : un objet { fr, en }. Pour un schéma plat, remplacez steps par fields: [ … ] ; pour des groupes, par groups: [{ label, fields }, …].
Types de champ
Champs scalaires (ConfigFieldType) :
text · password · email · url · number · checkbox · textarea · datetime.
Champs à options (ConfigSelectType) :
select · multiselect.
Un champ select porte ses options soit statiques (options: ConfigOption[], chaque entrée { value, label? }), soit dynamiques (remote: RemoteRef, voir plus bas). Il n'existe aucune autre forme.
// statique
{ key: 'default_lang', type: 'select', default: 'fr',
options: [{ value: 'fr', label: 'Français' }, { value: 'en', label: 'English' }],
label: { fr: 'Langue par défaut', en: 'Default language' } }Drapeaux d'un champ
Tous les champs partagent ces propriétés (en plus de key, type et label) :
| Drapeau | Type | Rôle |
|---|---|---|
required | boolean | Le champ est obligatoire à l'activation. |
default | string | number | boolean | Valeur par défaut pré-remplie. |
help | Localized | Texte d'aide affiché sous le champ. |
sensitive | boolean | Valeur chiffrée au repos, jamais exposée au front ni aux widgets. |
owner | 'merchant' | 'integrator' | Qui pose la valeur (voir plus bas). |
supports_variables | boolean | Le champ accepte une variable ShopiMind {var=…}. |
Champs sensibles
Marquez explicitement vos secrets (clé d'API, mot de passe) avec sensitive: true. La valeur est chiffrée au repos côté ShopiMind ; elle vous est transmise déchiffrée dans ctx.settings, jamais renvoyée au formulaire ni à un widget.
parseSettings — des réglages typés
Le kit vous transmet les valeurs brutes (RawConfigs) ; votre parseSettings les convertit en un objet typé qui devient ctx.settings partout. C'est l'endroit où normaliser (caster les booléens, parser un multiselect, etc.).
import type { RawConfigs } from '@shopimind/integration-kit-js';
export interface MySettings {
apiUrl: string;
apiToken: string;
listId: string;
}
export function parseSettings(raw: RawConfigs): MySettings {
return {
apiUrl: String(raw.api_url ?? '').trim(),
apiToken: String(raw.api_token ?? '').trim(),
listId: String(raw.list_id ?? ''),
};
}test_connection — valider les identifiants
Une étape peut se terminer par une action on_complete: { action: 'test_connection' }. À la validation de l'étape (ou au clic « Tester »), ShopiMind appelle votre serveur sur POST /webhook/test-connection (webhook signé). Le kit vérifie la signature, applique parseSettings, puis exécute le testConnection(ctx) que vous avez déclaré.
export const myIntegration = defineIntegration<MySettings>({
// …
testConnection: async (ctx) => {
// ctx.settings est déjà typé et déchiffré
const ok = await pingPartnerApi(ctx.settings.apiUrl, ctx.settings.apiToken);
return ok; // true ⇒ étape validée
},
});- Tant que
testConnectionn'a pas renvoyétrue, l'étape n'est pas considérée valide. - Modifier un champ d'une étape déjà validée invalide sa validation.
- La complétude de toutes les étapes est exigée à l'activation.
Vous ne gérez ni la route, ni la signature, ni le contrat de réponse : le kit s'en charge. Vous écrivez uniquement la fonction testConnection. Voir → Webhooks et cycle de vie.
Options dynamiques — remote: RemoteRef
Pour peupler un select / multiselect avec des valeurs venant de votre système (la liste des magasins du compte connecté, ses segments, etc.), référencez un résolveur distant au lieu d'options statiques :
{
key: 'store_ids',
type: 'multiselect',
required: true,
label: { fr: 'Points de vente à synchroniser', en: 'Stores to sync' },
remote: { resource: 'stores', label_field: 'label', value_field: 'value' },
}RemoteRef désigne la ressource et nomme les champs à lire dans vos options :
interface RemoteRef {
resource: string; // doit exister dans integration.remoteData
label_field: string; // champ affiché à l'utilisateur
value_field: string; // champ stocké comme valeur
description_field?: string;
}Côté intégration, vous fournissez le résolveur dans remoteData, indexé par resource. Il reçoit le ctx (identifiants déchiffrés inclus) et renvoie un tableau de RemoteOption ({ value, label }) :
export const myIntegration = defineIntegration<MySettings>({
// …
remoteData: {
stores: async (ctx) => {
const stores = await listStores(ctx.settings.apiUrl, ctx.settings.apiToken);
return stores.map((s) => ({ value: String(s.id), label: s.name }));
},
},
});Quand l'utilisateur ouvre le select, ShopiMind appelle POST /webhook/remote-data/{resource} ({resource} = stores ici). Le kit vérifie la signature, applique parseSettings, exécute integration.remoteData['stores'](ctx) et renvoie les options au formulaire. L'utilisateur voit les label, et le value est stocké comme valeur du champ.
Le value revient en chaîne
Les valeurs d'un multiselect reviennent telles quelles dans RawConfigs. Castez-les dans parseSettings (par ex. Number(...) pour des ids entiers) — ne supposez pas le type côté schéma.
Schémas par groupes
groups regroupe des champs sous des intertitres, sans logique d'étapes :
import type { ConfigSchema } from '@shopimind/integration-kit-js';
export const configSchema: ConfigSchema = {
groups: [
{
label: { fr: 'Identifiants', en: 'Credentials' },
fields: [
{ key: 'api_url', type: 'url', required: true, label: { fr: "URL de l'API", en: 'API URL' } },
{ key: 'api_token', type: 'password', required: true, sensitive: true, label: { fr: 'Jeton', en: 'Token' } },
],
},
{
label: { fr: 'Données', en: 'Data' },
fields: [
{ key: 'sync_customers', type: 'checkbox', default: false, label: { fr: 'Clients', en: 'Customers' } },
{ key: 'sync_orders', type: 'checkbox', default: false, label: { fr: 'Commandes', en: 'Orders' } },
],
},
],
};Config intégrateur (owner: 'integrator')
Un champ peut être posé non par le marchand mais par l'intégrateur, par boutique, via le SDK. Marquez-le owner: 'integrator' : il n'apparaît pas dans le formulaire marchand, et c'est vous qui posez sa valeur pour chaque boutique avec votre clé d'API. La valeur se résout au rendu en {integration.<key>} (le default déclaré sert de repli tant qu'elle n'est pas posée).
Utile pour une valeur propre à l'intégrateur mais spécifique à la boutique : URL de tracking, identifiant de compte, base d'API calculée…
// dans configSchema.fields (ou steps[].fields / groups[].fields)
{ key: 'tracking_base', type: 'text', owner: 'integrator', default: 'https://t.partner.io/p',
label: { fr: 'Base de tracking', en: 'Tracking base' } }Posez / relisez ces valeurs avec le SDK (authentifié par la clé API de la boutique, en-tête spm-api-key) :
import { SpmClient, SpmIntegrationConfig } from '@shopimind/sdk-js';
const client = SpmClient.getClient('v1', process.env.SHOPIMIND_API_KEY!);
await SpmIntegrationConfig.set(client, { tracking_base: 'https://t.partner.io/p' });
const res = await SpmIntegrationConfig.get(client);
console.log(res.data.data); // ⚠️ payload métier double-niché : res.data.dataSeules les clés déclarées owner: 'integrator' sont acceptées par SpmIntegrationConfig ; le marchand ne peut jamais les modifier. Voir → Le SDK JavaScript et la ressource SpmIntegrationConfig dans → les ressources du SDK.
Récapitulatif
- Déclarez
configSchema(steps|fields|groups) dansdefineIntegration. - Transformez les valeurs brutes en réglages typés via
parseSettings. - Validez les identifiants avec
on_complete: { action: 'test_connection' }+testConnection. - Peuplez les selects dynamiques avec
remote: RemoteRef+ un résolveurremoteData[resource]. - Réservez
owner: 'integrator'aux valeurs que vous posez par boutique viaSpmIntegrationConfig.
Pour voir un schéma complet et réel de bout en bout (assistant connexion + magasins distants + bascules de données), voir → Exemple : Hiboutik POS. Pour générer visuellement votre JSON, voir → Studio.