Documentation API
Le Shoppi SDK, l'API JSON et les TagCodes : tout ce qu'il faut à un partenaire pour construire sur une boutique Shoppi.
Fonctionnement
Chaque boutique Shoppi Cloud s'étend avec le Shoppi SDK, une bibliothèque JavaScript servie par la plateforme sur chaque domaine hébergé par Shoppi, et avec les TagCodes, des balises côté serveur que le moteur de pages résout avant que la page n'arrive au navigateur. Les deux parlent à la même API JSON, utilisable aussi directement.
- Aucune clé API dans le navigateur. La boutique est reconnue par le domaine d'où part la requête : le SDK fonctionne sur le domaine de la boutique sans secret dans le code.
- Les paiements passent par Stripe. Les sessions de paiement sont créées sur le compte Stripe du marchand (Stripe Connect) ; Shoppi ne détient jamais l'argent des clients du marchand. Les remboursements passent eux aussi entièrement par Stripe.
- Deux bases de données. Le catalogue est lu dans la base de données Shoppi, celle que le marchand gère depuis l'app ; chaque domaine de boutique a aussi sa propre base de données. Vous choisissez dans laquelle lit chaque API (voir Catalogue). Les comptes clients sont toujours dans la base de données propre au domaine de la boutique, séparés des autres boutiques.
- Version actuelle du SDK : 2.9.0. Les réponses ont toujours la même forme :
{"result": "ok" | "ko", "feedback": …}.
Installation
Incluez le SDK une seule fois, dans le <head> de la page. Au chargement, il crée l'objet global window.Shoppi.
<script>
window.ShoppiConfig = {
pageId: "%_ID%", // store id; the page engine fills it in (use %_ID% inside JS, not {id})
currency: "EUR", // default currency for cart and checkout
defaultLocale: "it", // language used when the URL has no /xx/ prefix
baseUrl: "", // leave empty: API calls go to the same origin
sources: "shoppi" // catalog database: "shoppi" (default) or "local"
};
</script>
<script src="/shoppi/shoppi-sdk.js?v=2.9.0"></script>
Tous les réglages sont facultatifs : sans ShoppiConfig, le SDK reconnaît la boutique par le domaine et utilise l'euro. Augmentez le paramètre ?v= pour que les navigateurs chargent un SDK plus récent.
Boutique et contenus — Shoppi.site
| Méthode | Renvoie |
|---|---|
Shoppi.site.getInfo() |
Profil de la boutique : nom, logo, couverture, description, catégorie, ville, pays, langue. |
Shoppi.site.getPage(slug) |
Une page de contenu par son slug : titre, contenu HTML et mots-clés, ou null. |
Shoppi.site.getPages() |
Les sections de la boutique : pages de contenu et catégories des produits. |
Shoppi.site.getProducts(options) |
Comme Shoppi.catalog.products(). |
Shoppi.site.getProduct(idOrSlug) |
Comme Shoppi.catalog.product(). |
Shoppi.site.getCategories() |
Comme Shoppi.catalog.categories(). |
Shoppi.site.search(q) / getSuggestions(q) |
Comme Shoppi.catalog.search() et suggestions(). |
const info = await Shoppi.site.getInfo();
document.getElementById('store-name').textContent = info.title;
Catalogue — Shoppi.catalog
Produits, catégories, photos et recherche : les données que le marchand gère dans l'app Shoppi, prêtes pour construire une vitrine. Chaque article revient sous la même forme, quel que soit son type :
{
id: 9610389, type: 'products', // products | services | files | offers
slug: 'curtirisorisobasmati2×500g',
title: 'Curtiriso Riso Basmati 2 × 500 g',
description: '', content: 'Basmati', // content may contain HTML
price: 4.79, currency: 'EUR', price_format: '4,79 €',
photo: 'https://storage.shoppi.app/…', photo_hires: '…', photos: ['…'],
quantity: 5, in_stock: true, ship_cost: 0, code: 'CX-DIS-000001',
category: 'Dispensa', keywords: ['dispensa'], link: '…', date: 1791312283
}
| Méthode | Renvoie |
|---|---|
Shoppi.catalog.products(options) |
Renvoie { items, count, limit, skip, hasMore }. Options : type (products, services, files, offers, all), limit (24), skip, sort (newest, updated, popular, price_asc, price_desc, title), category (id, slug ou titre), q, ids, inStock. |
Shoppi.catalog.product(idOrSlug, { type }) |
Un article, ou null. Un article d'une autre boutique n'est jamais renvoyé. |
Shoppi.catalog.categories({ type }) |
sections (par défaut : les catégories auxquelles les produits sont rattachés), albums ou tree. |
Shoppi.catalog.category(ref) |
Une catégorie par id, slug ou titre. |
photos(), pages(), page(slug), news(), videos() |
Les contenus de la boutique. |
Shoppi.catalog.search(q, { type, limit, minPrice, maxPrice }) |
Renvoie { items, count, suggestions, did_you_mean }. |
Shoppi.catalog.suggestions(q) |
Titres pour une saisie semi-automatique. |
Shoppi.catalog.info() |
Profil de la boutique. |
const page = await Shoppi.catalog.products({ limit: 12 });
const cats = await Shoppi.catalog.categories(); // [{ id: 600, slug: 'dispensa', title: 'Dispensa' }, …]
const food = await Shoppi.catalog.products({ category: 'dispensa' });
const one = await Shoppi.catalog.product(9610389); // or by slug
const hits = await Shoppi.catalog.search('riso'); // { items, count, suggestions }
Quelle base de données : par API, par appel
'shoppi' (par défaut) lit la base de données Shoppi, où le marchand gère ses produits depuis l'app. 'local' lit la base de données propre au domaine de la boutique, vide tant que vous n'y écrivez pas. Choisissez par API dans ShoppiConfig.sources, à l'exécution avec Shoppi.catalog.use() ou pour un seul appel avec { source }. Une source peut aussi être l'URL de base d'un autre hôte ou une fonction qui renvoie vos propres données. Panier, paiement, comptes clients et e-mail passent toujours par le domaine de la boutique.
window.ShoppiConfig = {
pageId: "%_ID%",
sources: { products: 'shoppi', categories: 'local' } // or one value: 'local'
};
Shoppi.catalog.use('search', 'local'); // at runtime, one API
await Shoppi.catalog.products({ limit: 12 }, { source: 'local' }); // for one call
Rendu et événements
Shoppi.render(target, items, template) remplit un élément avec une liste : le modèle est une fonction, une chaîne Handlebars, ou rien pour la carte intégrée avec son bouton d'ajout au panier. Les éléments avec data-shoppi-list se remplissent seuls au chargement de la page ; leurs autres attributs data-shoppi-* sont les options de la méthode correspondante. Événements sur document : shoppi:cart:added, shoppi:cart:updated, shoppi:checkout:started, shoppi:rendered.
const r = await Shoppi.catalog.products({ limit: 12 });
Shoppi.render('grid', r); // built-in card with add-to-cart button
Shoppi.render('grid', r, p => `<h3>${p.title}</h3><p>${p.price_format}</p>`);
<div data-shoppi-list="products" data-shoppi-limit="12" data-shoppi-category="dispensa">
<template><div class="card"><img src="{{photo}}"><h3>{{title}}</h3>
<button data-shoppi-add="{{id}}" data-shoppi-price="{{price}}"
data-shoppi-title="{{title}}" data-shoppi-image="{{photo}}">Add</button></div></template>
</div>
document.addEventListener('shoppi:cart:added', e => console.log(e.detail.title));
Panier — Shoppi.cart
Le panier vit dans le navigateur (localStorage) et survit au rechargement. Les prix et titres transmis vont jusqu'au paiement : renseignez-les toujours.
| Appel | Ce qu'il fait |
|---|---|
Shoppi.cart.add(id, qty, { price, title, image }) |
Ajoute un produit (quantité 1 par défaut). |
Shoppi.cart.updateQty(id, qty) |
Fixe la quantité ; 0 supprime la ligne. |
Shoppi.cart.remove(id) |
Supprime une ligne. |
Shoppi.cart.clear() |
Vide le panier. |
Shoppi.cart.setCountry(code) |
Fixe le pays de livraison (code ISO, par exemple 'IT'). |
Shoppi.cart.data |
État actuel : { items, count, total, country }. |
Sans JavaScript, avec des attributs HTML :
<button data-shoppi-add="10034"
data-shoppi-price="29.90"
data-shoppi-title="Leather wallet"
data-shoppi-image="/img/wallet.jpg">Add to cart</button>
<span class="shoppi-cart-count"></span> <!-- item count, updated automatically -->
<span class="shoppi-cart-total"></span> <!-- total, formatted in the store currency -->
<select data-shoppi-country> <!-- shipping country -->
<option value="IT">Italia</option>
<option value="DE">Deutschland</option>
</select>
<button data-shoppi-checkout>Checkout</button>
Paiement — Shoppi.checkout
| Méthode | Ce qu'il fait |
|---|---|
Shoppi.checkout.start() |
Crée une session Stripe Checkout pour le panier sur le compte Stripe du marchand et y redirige le client. |
Shoppi.checkout.startDirect(productId, metadata) |
Achat en un clic d'un seul produit, sans panier (services, réservations, téléchargements). |
Shoppi.checkout.createOnboardingLink() |
Renvoie le lien d'onboarding Stripe de la boutique, pour connecter ou compléter son compte Stripe. |
document.querySelector('#buy').addEventListener('click', () => Shoppi.checkout.start());
// Stripe onboarding for the merchant
const url = await Shoppi.checkout.createOnboardingLink();
window.location.href = url;
Tout bouton avec data-shoppi-checkout fait la même chose que Shoppi.checkout.start(). Les frais de paiement suivent l'offre de la boutique : frais Stripe plus les frais d'application Shoppi Pay de 0,4 % + 1 centime par paiement. Les remboursements, totaux ou partiels, en ligne ou au point de vente, passent aussi par Stripe.
Comptes clients — Shoppi.user
Comptes pour les clients de la boutique, enregistrés dans sa base de données. Après connexion, le jeton de session reste dans le navigateur et accompagne chaque requête du SDK dans l'en-tête X-User-Token. Les comptes sont liés au domaine de la boutique : deux boutiques ne voient jamais les clients l'une de l'autre.
| Méthode | Ce qu'il fait |
|---|---|
Shoppi.user.register(email, password, firstName, lastName) |
Crée un compte et connecte. |
Shoppi.user.login(email, password) |
Connecte. |
Shoppi.user.getProfile() |
Profil du client connecté (null s'il n'est pas connecté). |
Shoppi.user.updateProfile(data) |
Met à jour des champs du profil, par exemple { phone }. |
Shoppi.user.saveAddress(type, address) |
Enregistre une adresse ; type vaut par exemple 'shipping'. |
Shoppi.user.logout() |
Ferme la session et recharge la page. |
Navigation et traductions — Shoppi.router, Shoppi.lang
Les URL suivent le schéma /<language>/<page>, par exemple /it/contatti. Shoppi.router.navigate(slug, locale) charge une page sans rechargement complet et l'affiche dans <main> ; les liens avec data-shoppi-link font de même.
<a href="/it/contatti" data-shoppi-link="contatti">Contatti</a>
Shoppi.lang.load(locale) charge les traductions de la boutique ; Shoppi.lang.t(key, vars) renvoie un texte et remplace les balises écrites %_NAME%.
Annuaire local — Shoppi.local
| Méthode | Renvoie |
|---|---|
Shoppi.local.getCities() |
Villes disponibles dans l'annuaire local. |
Shoppi.local.getBusinesses(cityId, categoryId) |
Commerces d'une ville, avec filtre facultatif par catégorie. |
E-mail — Shoppi.mailer
Envoie un e-mail transactionnel depuis la boutique, par exemple depuis un formulaire de contact. Limité à 10 e-mails par jour et par appareil.
await Shoppi.mailer.send({
to: '[email protected]',
subject: 'New request from the website',
body: '<p>Hello!</p>',
replyTo: '[email protected]' // optional
});
Shoppi.mailer.remaining(); // emails left today on this device
API JSON
Chaque méthode du SDK est un appel à l'API JSON, utilisable directement avec Shoppi.call(endpoint, params, method) ou un simple fetch : le schéma est /api/<module>/<method>/json, sur le domaine de la boutique.
| Endpoint | Usage |
|---|---|
GET /api/website/info/json |
Profil de la boutique |
GET /api/website/section/json?clear_title=<slug> |
Page de contenu |
POST /api/checkout/createStripeSession/json |
Session Stripe Checkout depuis un panier |
POST /api/checkout/startDirect/json |
Achat direct d'un produit |
GET /api/checkout/createOnboardingLink/json |
Lien d'onboarding Stripe |
POST /api/frontend_user/register/json |
Inscription client |
POST /api/frontend_user/login/json |
Connexion client |
GET /api/local/getCities/json |
Annuaire local : villes |
GET /api/website/products/json |
Produits (aussi listServices, listFiles, listOffers) |
GET /api/website/product/json?id= |
Un produit, par id ou clear_title
|
GET /api/website/sections/json |
Sections et catégories |
GET /api/website/listSells/json?parent= |
Articles rattachés à une section |
GET /api/search/results/json?query= |
Recherche |
GET /api/search/suggestions/json?query= |
Saisie semi-automatique |
const res = await fetch('/api/website/info/json', { credentials: 'include' });
const data = await res.json(); // { "result": "ok", ... } or { "result": "ko", "feedback": "reason" }
Les endpoints du catalogue répondent sur le domaine de la boutique (sa propre base de données) et sur https://shoppi.app (la base de données Shoppi). Pour la source 'shoppi', le SDK appelle shoppi.app avec l'id de la boutique : un id qui n'appartient pas au domaine appelant est refusé. Shoppi.call(endpoint, params, method, { base }) choisit l'hôte.
Shoppi.call() renvoie la partie utile d'une réponse ok et lève une Error avec le message feedback en cas de ko.
TagCodes (côté serveur)
Les TagCodes sont des balises dans les templates HTML de la boutique, résolues sur le serveur avant l'envoi de la page : le contenu est dans le HTML que voient les moteurs de recherche.
| TagCode | Sortie |
|---|---|
{css} / {js}
|
Feuilles de style et scripts de la page et de ses plugins |
{page_title}, {page_description}
|
Titre et description SEO |
{page_name}, {page_logo}
|
Nom et logo de la boutique |
{locale}, {currency}, {country}
|
Langue, devise et pays du visiteur |
{title}, {content}, {photo}, {price}, {link}
|
Champs du produit ou de l'article en cours |
{cfield:title}, {cfield:description}, {cfield:cover}
|
Champs du profil de la boutique |
{cart:act=drawer} |
Panier latéral |
{search:nout=true} |
Recherche, initialisée sans sortie par défaut |
{cookieconsent} |
Bandeau de consentement aux cookies |
{offers:act=seo} |
Données structurées JSON-LD pour les produits |
Dans un objet JavaScript, utilisez %_ID% pour l'identifiant de la boutique : les accolades y seraient lues comme un TagCode.
Accès aux fichiers et aux données
Les fichiers de chaque boutique sont accessibles en WebDAV (aussi via l'app de bureau Shoppi Go) et les données restent exportables : templates HTML standard, base de données de la boutique et API JSON ci-dessus. Les identifiants d'accès sont dans l'espace compte de la boutique.
Une question ou un cas non couvert ici ? Réservez un appel avec l'équipe partenaires, ou revenez au kit partenaire.