Aperçu
Accédez aux données des produits via deux méthodes :getStore()- Retourne tous les produits de la boutique (inclus dans la réponse de la boutique)getProduct()- Récupère un seul produit par ID ou sluggetProductReviews()- Obtient les avis paginés pour un produit
Obtenir tous les produits
Les produits sont inclus dans la réponse de la boutique :const response = await komerza.getStore();
if (response.success) {
const products = response.data.products;
products.forEach((product) => {
console.log(product.name, product.variants.length, "variantes");
});
}
Obtenir un produit individuel
Récupérer un produit par ID ou slug :const response = await komerza.getProduct("product-id-or-slug");
Paramètres
L’ID unique du produit (UUID) ou le slug URL
Exemple
// Par ID
const byId = await komerza.getProduct("550e8400-e29b-41d4-a716-446655440000");
// Par slug
const bySlug = await komerza.getProduct("premium-license");
if (bySlug.success) {
const product = bySlug.data;
console.log(product.name);
console.log(product.description);
console.log(product.variants);
}
Objet Product
interface Product {
id: string; // Identifiant unique
name: string; // Nom du produit
description: string; // Description (supporte le markdown)
slug?: string; // Slug adapté à l'URL
imageNames: string[]; // Noms de fichiers des images du produit (voir page Images)
rating: number; // Note moyenne des avis
order: number; // Ordre d'affichage
visibility: number; // 0=Public, 1=Non répertorié, 2=Privé
storeId: string; // ID de la boutique parente
variants: Variant[]; // Variantes du produit
dateCreated: string; // Horodatage ISO 8601
}
Variantes
Chaque produit possède une ou plusieurs variantes. Chaque variante a son propre prix, stock et paramètres :interface Variant {
id: string; // ID de la variante
name: string; // Nom de la variante
productId: string; // ID du produit parent
storeId: string; // ID de la boutique
cost: number; // Prix dans la devise de la boutique
stock: number; // Stock disponible
stockMode: number; // Mode de calcul du stock (voir ci-dessous)
minimumQuantity: number; // Quantité minimale d'achat (par défaut : 1)
maximumQuantity: number; // Quantité maximale d'achat (-1 = illimité)
order: number; // Ordre d'affichage
imageNames: string[]; // Noms de fichiers des images spécifiques à la variante
type: number; // 0=Unique, 1=Abonnement
subscriptionPeriod?: string; // Durée de l'abonnement si applicable
requireDiscordAuthorization: boolean;
dateCreated: string;
}
Mode de calcul du stock
Le champstockMode détermine comment la disponibilité du stock est calculée :
| Valeur | Mode | Description |
|---|---|---|
| 0 | Calculé | Le stock est automatiquement calculé à partir des articles disponibles |
| 1 | Ignoré | Le stock n’est pas suivi — toujours disponible |
| 2 | Fixe | Le stock est défini manuellement et décrémenté à l’achat |
Affichage des images
LesimageNames des produits et variantes contiennent des noms de fichiers, pas des URLs complètes. Consultez la page Images pour savoir comment construire les URLs complètes.
Afficher les variantes
async function afficherProduit(productId) {
const [productResponse, formatter] = await Promise.all([
komerza.getProduct(productId),
komerza.createFormatter(),
]);
if (!productResponse.success) return;
const product = productResponse.data;
// Trier les variantes par ordre
const variants = product.variants.sort((a, b) => a.order - b.order);
variants.forEach((variant) => {
const price = formatter.format(variant.cost);
const inStock = variant.stockMode === 1 || variant.stock > 0;
console.log(`${variant.name}: ${price}`);
console.log(` Stock: ${inStock ? "Disponible" : "Rupture de stock"}`);
console.log(` Qté min: ${variant.minimumQuantity}`);
console.log(
` Qté max: ${
variant.maximumQuantity === -1 ? "Illimité" : variant.maximumQuantity
}`,
);
});
}
Sélection de variante
<select id="variant-select" onchange="updatePrice()">
<!-- Rempli dynamiquement -->
</select>
<span id="price"></span>
<button onclick="addToCart()">Ajouter au panier</button>
<script>
let currentProduct;
let formatter;
async function loadProduct(productId) {
[{ data: currentProduct }, formatter] = await Promise.all([
komerza.getProduct(productId),
komerza.createFormatter(),
]);
const select = document.getElementById("variant-select");
select.innerHTML = currentProduct.variants
.map(
(v) =>
`<option value="${v.id}">${v.name} - ${formatter.format(
v.cost,
)}</option>`,
)
.join("");
updatePrice();
}
function updatePrice() {
const variantId = document.getElementById("variant-select").value;
const variant = currentProduct.variants.find((v) => v.id === variantId);
document.getElementById("price").textContent = formatter.format(
variant.cost,
);
}
function addToCart() {
const variantId = document.getElementById("variant-select").value;
komerza.addToBasket(currentProduct.id, variantId);
}
</script>
Avis produit
Obtenez les avis paginés pour un produit :const response = await komerza.getProductReviews(productId, page);
Paramètres
L’ID unique du produit
Numéro de page (indexé à partir de 1)
Retours
RetournePaginatedApiResponse<Review> :
interface PaginatedApiResponse<Review> {
success: boolean;
pages: number; // Nombre total de pages
data?: Review[]; // Tableau d'avis
message?: string;
}
interface Review {
id: string;
rating: number; // 1-5 étoiles
reason: string; // Texte de l'avis
productId: string;
reply?: string; // Réponse du marchand si disponible
dateCreated: string;
}
Afficher les avis
async function afficherAvis(productId, page = 1) {
const response = await komerza.getProductReviews(productId, page);
if (!response.success) return;
const container = document.getElementById("reviews");
container.innerHTML = response.data
.map(
(review) => `
<div class="review">
<div class="rating">${"★".repeat(review.rating)}${"☆".repeat(
5 - review.rating,
)}</div>
<p>${review.reason}</p>
${
review.reply
? `<p class="reply"><strong>Réponse :</strong> ${review.reply}</p>`
: ""
}
<time>${new Date(review.dateCreated).toLocaleDateString()}</time>
</div>
`,
)
.join("");
// Pagination
if (response.pages > 1) {
container.innerHTML += `
<div class="pagination">
${
page > 1
? `<button onclick="afficherAvis('${productId}', ${
page - 1
})">Précédent</button>`
: ""
}
<span>Page ${page} sur ${response.pages}</span>
${
page < response.pages
? `<button onclick="afficherAvis('${productId}', ${
page + 1
})">Suivant</button>`
: ""
}
</div>
`;
}
}
Images produit
Les images produit sont référencées par nom de fichier. Construisez l’URL complète :function getProductImageUrl(storeId, productId, imageName) {
return `https://cdn.komerza.com/stores/${storeId}/products/${productId}/${imageName}`;
}
// Utilisation
const product = response.data;
const imageUrl = getProductImageUrl(
product.storeId,
product.id,
product.imageNames[0],
);
Galerie d’images
function afficherImagesProduct(product) {
const gallery = document.getElementById("gallery");
// Utiliser les images de variante si disponibles, sinon les images produit
const images =
product.variants[0].imageNames.length > 0
? product.variants[0].imageNames
: product.imageNames;
gallery.innerHTML = images
.map(
(img) => `
<img
src="https://cdn.komerza.com/stores/${product.storeId}/products/${product.id}/${img}"
alt="${product.name}"
loading="lazy"
/>
`,
)
.join("");
}
Gestion du stock
Vérifiez la disponibilité du stock avant d’ajouter au panier :function peutAjouterAuPanier(variant, quantity) {
// stockMode 1 = Ignoré (illimité)
if (variant.stockMode === 1) return true;
// Vérifier le stock disponible
if (variant.stock < quantity) return false;
// Vérifier les limites de quantité
if (quantity < variant.minimumQuantity) return false;
if (variant.maximumQuantity !== -1 && quantity > variant.maximumQuantity)
return false;
return true;
}
function getStatutStock(variant) {
if (variant.stockMode === 1) return "En stock";
if (variant.stock === 0) return "Rupture de stock";
if (variant.stock < 10) return `Plus que ${variant.stock}`;
return "En stock";
}
Exemple complet
<!DOCTYPE html>
<html>
<head>
<title>Page produit</title>
<script src="https://cdn.komerza.com/komerza.min.js"></script>
<style>
.product {
max-width: 600px;
margin: 0 auto;
}
.gallery img {
max-width: 100%;
}
.variants {
margin: 20px 0;
}
.reviews {
margin-top: 40px;
}
.review {
border-bottom: 1px solid #eee;
padding: 15px 0;
}
.rating {
color: gold;
}
</style>
</head>
<body>
<div class="product">
<div id="gallery"></div>
<h1 id="name"></h1>
<p id="description"></p>
<p id="rating"></p>
<div class="variants">
<select id="variant-select"></select>
<span id="price"></span>
<span id="stock"></span>
</div>
<div>
<label
>Quantité : <input type="number" id="quantity" value="1" min="1"
/></label>
<button id="add-btn" onclick="addToCart()">Ajouter au panier</button>
</div>
<div class="reviews">
<h2>Avis</h2>
<div id="reviews"></div>
</div>
</div>
<script>
komerza.init("your-store-id");
let product;
let formatter;
async function loadProduct(idOrSlug) {
const [productResponse, fmt] = await Promise.all([
komerza.getProduct(idOrSlug),
komerza.createFormatter(),
]);
if (!productResponse.success) {
alert("Produit introuvable");
return;
}
product = productResponse.data;
formatter = fmt;
// Afficher les informations du produit
document.getElementById("name").textContent = product.name;
document.getElementById("description").textContent =
product.description;
document.getElementById("rating").textContent =
`Note : ${product.rating}★`;
// Afficher les images
const images = product.imageNames;
document.getElementById("gallery").innerHTML = images
.map(
(img) =>
`<img src="https://cdn.komerza.com/stores/${product.storeId}/products/${product.id}/${img}" />`,
)
.join("");
// Remplir les variantes
const select = document.getElementById("variant-select");
select.innerHTML = product.variants
.map((v) => `<option value="${v.id}">${v.name}</option>`)
.join("");
select.onchange = updateVariantDisplay;
updateVariantDisplay();
loadReviews(1);
}
function updateVariantDisplay() {
const variantId = document.getElementById("variant-select").value;
const variant = product.variants.find((v) => v.id === variantId);
document.getElementById("price").textContent = formatter.format(
variant.cost,
);
// Statut du stock
let stockText = "En stock";
if (variant.stockMode !== 1) {
if (variant.stock === 0) stockText = "Rupture de stock";
else if (variant.stock < 10) stockText = `Plus que ${variant.stock}`;
}
document.getElementById("stock").textContent = stockText;
// Mettre à jour les limites de quantité
const qtyInput = document.getElementById("quantity");
qtyInput.min = variant.minimumQuantity;
qtyInput.max =
variant.maximumQuantity === -1 ? 999 : variant.maximumQuantity;
qtyInput.value = variant.minimumQuantity;
// Désactiver le bouton si rupture de stock
document.getElementById("add-btn").disabled =
variant.stockMode !== 1 && variant.stock === 0;
}
function addToCart() {
const variantId = document.getElementById("variant-select").value;
const quantity = parseInt(document.getElementById("quantity").value);
komerza.addToBasket(product.id, variantId, quantity);
alert("Ajouté au panier !");
}
async function loadReviews(page) {
const response = await komerza.getProductReviews(product.id, page);
if (!response.success || response.data.length === 0) {
document.getElementById("reviews").innerHTML =
"<p>Aucun avis pour l'instant</p>";
return;
}
document.getElementById("reviews").innerHTML = response.data
.map(
(review) => `
<div class="review">
<div class="rating">${"★".repeat(review.rating)}${"☆".repeat(
5 - review.rating,
)}</div>
<p>${review.reason}</p>
${review.reply ? `<p><em>Réponse : ${review.reply}</em></p>` : ""}
</div>
`,
)
.join("");
}
// Charger le produit depuis l'URL ou par défaut
const urlParams = new URLSearchParams(window.location.search);
const productId = urlParams.get("product") || "your-default-product-slug";
loadProduct(productId);
</script>
</body>
</html>
Étapes suivantes
Formatage des devises
Formater les prix dans la devise de votre boutique
Gestion du panier
Ajouter des produits au panier