La plateforme SelSup · Les guides
Extensions et intégrations sur mesure
Construisez une page HTML/JavaScript régulière en utilisant n'importe quel framework. SelSup l'intègre dans un iframe; il n'exécute pas votre code React à l'intérieur du placard.
Construisez une page HTML/JavaScript régulière en utilisant n'importe quel framework. SelSup l'intègre dans un iframe; il n'exécute pas votre code React à l'intérieur du placard.
Des développeurs d'applications et des partenaires technologiques1
Choisissez le type d'intégration
- 1
Service de backend externe
Le jeton reste sur votre serveur et les données se déplacent à travers API et les webhooks.
- 2
Panneau de l'iframe de l"armoire
Construisez une page HTML/JavaScript régulière en utilisant n'importe quel framework. SelSup l'intègre dans un iframe; il n'exécute pas votre code React à l'intérieur du placard.
- 3
Module de rétroaction SelSup
Requérée pour une intégration de marché ou de livraison approfondie participant à des processus partagés de fond SelSup.
Mettre en œuvre un flux de travail complet et son observabilité d'abord. Ajouter des importations en vrac, UI et plus de types d'événements après la réconciliation est stable.
2
Architecture d'applications externes
Gardez le jeton dans un gestionnaire de secrets et appelez SelSup uniquement depuis votre backend.
Séparer l'adaptateur SelSup, la file d'attente et le modèle de domaine.
Utilisez une boîte de réception pour les événements entrants et une boite de sortie pour les changements sortants.
SelSup webhook → inbox → domain worker → ERP/WMS
ERP/WMS change → outbox → SelSup API → reconciliation
Points d'extrémité API connexes
GET/api/product/findExemple de lecture du dictionnaireGET/api/order/findExemple de synchronisation en augmentation
3
Panneau de l'iframe de l"armoire
Construisez une page HTML/JavaScript régulière en utilisant n'importe quel framework. SelSup l'intègre dans un iframe; il n'exécute pas votre code React à l'intérieur du placard.
Accueillez le panneau sur un site HTTPS séparé qui ne partage pas un domaine de cookie avec l'armoire. Ouvrez les intégrations → Mes extensions → Ajouter l'extension et sélectionnez un nom, une icône, une URL, une section de menu et une position. Enregistrer un panneil privé sans autorisations, puis activer uniquement les autorisations dont il a besoin. La publication du magasin nécessite une révision SelSup.
4
Module d'intégration côté serveur
Une intégration de marché couvre généralement des jetons, des cartes, des prix, des stocks, des commandes et des mises à jour de l'état des antécédents.
Ce module modifie le backend principal SelSup et suit son processus d'examen, de test, de localisation et de sortie.
Convertir les défaillances externes API en valeurs stables d'ApplicationError et les jetons externes annulés ou invalides en AccessException selon les règles du projet backend.
Ne pas ajouter de champs spécifiques au marché aux entités partagées.
5
Libération et exploitation
- 1
Créer un compte de test
Les données de production des clients doivent être fournies par des tests séparés, des entrepôts, des produits et des commandes.
- 2
Ajouter l'observabilité
Suivez le retard de la file d'attente, les erreurs par code, les recensements et les divergences de réconciliation.
- 3
Plan de migration par contrat
Soutenir les anciens champs pendant une transition et déployer les consommateurs avant de désactiver l'ancien format.
- 4
Déconnexion du document
Les utilisateurs doivent savoir comment révoquer un jeton, supprimer un webhook et arrêter la synchronisation sans perdre de données.
6
Les onglets produits et commandes
Choisissez une page de menu (MENU_PAGE), un onglet de carte de produit (PRODUCT_TAB) ou un ongleton de commande (ORDER_TAB). La section de menu et la position s'appliquent uniquement à MENU_PAGE. Les onglets nécessitent une entité enregistrée avec une ID.
7
Context et autorisations
Seuls les champs de contexte accordés sont partagés. Utilisateur fournit user.id et user.locale; CLIENT fournit client.id; ORGANIZATION fournit un identifiant et un nom d'organisation accessibles. PRODUCT accorde séparément product.modelId sur une carte produit; ORDER accorde order.id sur une commande. Les enregistrements complets, contacts, mots de passe et jetons sont exclus. Ce sont des identifiants de la page déjà ouverte, pas une preuve d'accès pour votre backend.
{
"type": "selsup.extension.context",
"version": 1,
"context": {
"version": 1,
"extensionId": "example-extension-id",
"placement": "MENU_PAGE",
"user": {"id": 101, "locale": "ru"},
"client": {"id": 202},
"organizations": [{"id": 303, "name": "Demo"}]
}
}
[
{
"version": 1,
"extensionId": "example",
"placement": "PRODUCT_TAB",
"apiMethods": [],
"product": {
"modelId": 404
}
},
{
"version": 1,
"extensionId": "example",
"placement": "ORDER_TAB",
"apiMethods": [],
"order": {
"id": 505
}
}
]
8
messages v1 post
Vérifiez l'événement exact.origine, événement.source === window.parent, type de message, version et structure. Envoyez prêt sur la connexion et le contexte de demande pour être rafraîchi. SelSup vérifie l'accès et fusionne les demandes fréquentes. Ne jamais passer des jetons dans les URL, le panneau localStorage ou les messages.
const allowedOrigins = new Set(['https://your-cabinet.example.com']);
window.addEventListener('message', (event) => {
if (event.source !== window.parent || !allowedOrigins.has(event.origin)) return;
const message = event.data;
if (!message || message.type !== 'selsup.extension.context' || message.version !== 1) return;
const context = message.context;
if (!context || context.version !== 1 || !['MENU_PAGE', 'PRODUCT_TAB', 'ORDER_TAB'].includes(context.placement)) return;
// The complete sample also validates fields and strips unknown properties.
document.querySelector('#context').textContent = JSON.stringify(context, null, 2);
});
for (const origin of allowedOrigins) {
window.parent.postMessage({type: 'selsup.extension.ready', version: 1}, origin);
}
// Refresh after connecting: use the validated origin, never "*".
window.parent.postMessage({type: 'selsup.extension.request-context', version: 1},
'https://your-cabinet.example.com');
9
API à travers le pont iframe
L'auteur déclare les méthodes requises dans les paramètres d'extension. Chaque client d'installation accorde explicitement des méthodes de cette déclaration. API accès par défaut à aucune, y compris les installations existantes. Modifier la liste demandée nécessite une autre révision; les copies installées ne reçoivent jamais de nouvelles autorisations automatiquement.
L'hôte ne fait appel que aux méthodes ci-dessous via les services SelSupFront avec les autorisations des employés actuels. Les contrôles d'accès au serveur existants s'appliquent toujours. ClientService et authService sont totalement interdits; toutes les méthodes en dehors de cette liste sont également interdites. Les applications ne peuvent pas fournir d'URL, d'en-têtes, de jetons ou de configuration arbitraire d'Axios. Le serveur vérifie l'installation actuelle et ses subventions sur la demande API réelle.
Les subventions API sont indépendantes des autorisations de contexte. context.apiMethods énumère les méthodes accordées. Une subvention s'applique à la méthode dans l'ensemble du cabinet dans laccès du salarié, plutôt que uniquement à lentité dans l\'onglet courant. Les résultats ne contiennent que les champs documentés, jamais les objets ou les informations didentification Axios.
Une demande en vol et au plus une démarrage par seconde. Les 256 dernières ID de demande sont protégées contre les duplicates. Erreurs hôtes: ACCESS_DENIED, INVALID_REQUEST, ERATE_LIMITED, DUPLICATE_REQUEST, EREQUEST_FAILED. Erreur SDK comprennent également NOT_CONNECTED, DISCONNECTED et TIMEOUT (20 secondes). Ne jamais réessayer des mutations automatiquement: une error, une échéance ou une fermeture d'iframe ne signifie pas que l'opération a été rallée.
tagService.findTags {query?, page?, limit?} => {rows: [{id, name}]}
tagService.createTag {name} => {id, name}
tagService.updateTag {id, name} => {id, name}
tagService.deleteTag {id} => null
productService.updateFavourite {id, favourite} => null
id: positive safe integer; updateFavourite.id = product.modelId
favourite: boolean
name: 1..100 characters (trimmed); query: 0..100 characters
page: 1..10000, default 1; limit: 1..100, default 30
{
"type": "selsup.extension.api-request",
"version": 1,
"requestId": "request-1",
"method": "tagService.createTag",
"params": {
"name": "Demo"
}
}
{
"type": "selsup.extension.api-response",
"version": 1,
"requestId": "request-1",
"ok": true,
"result": {
"id": 1,
"name": "Demo"
}
}
// connection = connectExtension(...) from selsup-extension.js
// Run from a button click after receiving context.apiMethods.
const tag = await connection.callApi('tagService.createTag', {name: 'Demo'});
console.log(tag.id, tag.name);
10
Exemple d'application et d'hébergement
L'échantillon peut être copié dans un référentiel GitHub indépendant. Avec Node.js 22+, test npm et npm démarrer les tests et un cabinet mock local sans clés API ou données réelles. Configurez config.js et déployez cinq fichiers statiques à HTTPS hébergement. Téléchargez l'archivage lié ci-dessous et copiez son contenu dans votre référencement GitHub.
cd selsup-extension-example
npm test
npm start
# Mock host: http://localhost:4174/
# Panel: http://127.0.0.1:4173/
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; connect-src 'none'; form-action 'none'; base-uri 'none'; frame-ancestors https://your-cabinet.example.com
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
https://api.selsup.ru/assets/examples/selsup-extension-example.zip
11
Les limites de sécurité
La boîte à sable bloque les formulaires, les fenêtres contextuelles, les téléchargements, la navigation supérieure et les API de navigateur sensibles. L'origine du panneau est affichée au-dessus de l'iframe. Une extension ne doit jamais demander un mot de passe SelSup. Son site peut toujours voir l'IP du visiteur et conserver ou rediriger les données qui lui sont accordées.
SelSup lui-même autorise les appels de pont iframe; les cookies et les jetons n'atteignent jamais l'application. Votre propre backend a toujours besoin de sa propre authentification: le contexte ne prouve pas d'identité. Gardez uniquement les jeton SelSup de longue durée sur votre serveur.