Saltar al contenido principal

Descripción general

Accede a los datos de productos a través de dos métodos:
  • getStore() - Devuelve todos los productos de la tienda (incluidos en la respuesta de la tienda)
  • getProduct() - Obtiene un único producto por ID o slug
  • getProductReviews() - Obtiene reseñas paginadas para un producto

Obtener todos los productos

Los productos están incluidos en la respuesta de la tienda:
const response = await komerza.getStore();

if (response.success) {
  const products = response.data.products;

  products.forEach((product) => {
    console.log(product.name, product.variants.length, "variantes");
  });
}

Obtener un producto individual

Obtén un producto por ID o slug:
const response = await komerza.getProduct("product-id-or-slug");

Parámetros

idOrSlug
string
requerido
El ID único del producto (UUID) o el slug de URL

Ejemplo

// Por ID
const byId = await komerza.getProduct("550e8400-e29b-41d4-a716-446655440000");

// Por 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);
}

Objeto Product

interface Product {
  id: string; // Identificador único
  name: string; // Nombre del producto
  description: string; // Descripción (admite markdown)
  slug?: string; // Slug compatible con URL
  imageNames: string[]; // Nombres de archivo de imágenes del producto (ver página Images)
  rating: number; // Valoración media de reseñas
  order: number; // Orden de visualización
  visibility: number; // 0=Público, 1=No listado, 2=Privado
  storeId: string; // ID de la tienda padre
  variants: Variant[]; // Variantes del producto
  dateCreated: string; // Marca de tiempo ISO 8601
}

Variantes

Cada producto tiene una o más variantes. Cada variante tiene su propio precio, stock y configuración:
interface Variant {
  id: string; // ID de la variante
  name: string; // Nombre de la variante
  productId: string; // ID del producto padre
  storeId: string; // ID de la tienda
  cost: number; // Precio en la moneda de la tienda
  stock: number; // Stock disponible
  stockMode: number; // Modo de cálculo de stock (ver abajo)
  minimumQuantity: number; // Cantidad mínima de compra (por defecto: 1)
  maximumQuantity: number; // Cantidad máxima de compra (-1 = ilimitada)
  order: number; // Orden de visualización
  imageNames: string[]; // Nombres de archivo de imágenes específicas de la variante
  type: number; // 0=Único, 1=Suscripción
  subscriptionPeriod?: string; // Duración de la suscripción si aplica
  requireDiscordAuthorization: boolean;
  dateCreated: string;
}

Modo de cálculo de stock

El campo stockMode determina cómo se calcula la disponibilidad de stock:
ValorModoDescripción
0CalculadoEl stock se calcula automáticamente a partir de artículos disponibles
1IgnoradoEl stock no se rastrea — siempre disponible
2FijoEl stock se establece manualmente y se decrementa al comprar
Consulta el enum Stock Calculation Mode para más detalles.

Mostrar imágenes

Los imageNames de productos y variantes contienen nombres de archivo, no URLs completas. Consulta la página Images para saber cómo construir URLs de imagen completas.

Mostrar variantes

async function mostrarProducto(productId) {
  const [productResponse, formatter] = await Promise.all([
    komerza.getProduct(productId),
    komerza.createFormatter(),
  ]);

  if (!productResponse.success) return;

  const product = productResponse.data;

  // Ordenar variantes por orden
  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" : "Sin stock"}`);
    console.log(`  Cant. mín: ${variant.minimumQuantity}`);
    console.log(
      `  Cant. máx: ${
        variant.maximumQuantity === -1 ? "Ilimitada" : variant.maximumQuantity
      }`,
    );
  });
}

Selección de variante

<select id="variant-select" onchange="updatePrice()">
  <!-- Rellenado dinámicamente -->
</select>
<span id="price"></span>
<button onclick="addToCart()">Añadir al carrito</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>

Reseñas del producto

Obtén reseñas paginadas para un producto:
const response = await komerza.getProductReviews(productId, page);

Parámetros

productId
string
requerido
El ID único del producto
page
number
requerido
Número de página (indexado desde 1)

Retorna

Retorna PaginatedApiResponse<Review>:
interface PaginatedApiResponse<Review> {
  success: boolean;
  pages: number; // Número total de páginas
  data?: Review[]; // Array de reseñas
  message?: string;
}

interface Review {
  id: string;
  rating: number; // 1-5 estrellas
  reason: string; // Texto de la reseña
  productId: string;
  reply?: string; // Respuesta del comerciante si hay
  dateCreated: string;
}

Mostrar reseñas

async function mostrarResenas(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>Respuesta:</strong> ${review.reply}</p>`
          : ""
      }
      <time>${new Date(review.dateCreated).toLocaleDateString()}</time>
    </div>
  `,
    )
    .join("");

  // Paginación
  if (response.pages > 1) {
    container.innerHTML += `
      <div class="pagination">
        ${
          page > 1
            ? `<button onclick="mostrarResenas('${productId}', ${
                page - 1
              })">Anterior</button>`
            : ""
        }
        <span>Página ${page} de ${response.pages}</span>
        ${
          page < response.pages
            ? `<button onclick="mostrarResenas('${productId}', ${
                page + 1
              })">Siguiente</button>`
            : ""
        }
      </div>
    `;
  }
}

Imágenes del producto

Las imágenes del producto se referencian por nombre de archivo. Construye la URL completa:
function getProductImageUrl(storeId, productId, imageName) {
  return `https://cdn.komerza.com/stores/${storeId}/products/${productId}/${imageName}`;
}

// Uso
const product = response.data;
const imageUrl = getProductImageUrl(
  product.storeId,
  product.id,
  product.imageNames[0],
);

Galería de imágenes

function mostrarGaleriaImagenes(product) {
  const gallery = document.getElementById("gallery");

  // Usar imágenes de variante si están disponibles, si no las del producto
  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("");
}

Gestión de stock

Comprueba la disponibilidad de stock antes de añadir al carrito:
function puedeAnadirAlCarrito(variant, quantity) {
  // stockMode 1 = Ignorado (ilimitado)
  if (variant.stockMode === 1) return true;

  // Comprobar stock disponible
  if (variant.stock < quantity) return false;

  // Comprobar límites de cantidad
  if (quantity < variant.minimumQuantity) return false;
  if (variant.maximumQuantity !== -1 && quantity > variant.maximumQuantity)
    return false;

  return true;
}

function getEstadoStock(variant) {
  if (variant.stockMode === 1) return "En stock";
  if (variant.stock === 0) return "Sin stock";
  if (variant.stock < 10) return `Solo quedan ${variant.stock}`;
  return "En stock";
}

Ejemplo completo

<!DOCTYPE html>
<html>
  <head>
    <title>Página de producto</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
          >Cantidad: <input type="number" id="quantity" value="1" min="1"
        /></label>
        <button id="add-btn" onclick="addToCart()">Añadir al carrito</button>
      </div>

      <div class="reviews">
        <h2>Reseñas</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("Producto no encontrado");
          return;
        }

        product = productResponse.data;
        formatter = fmt;

        // Mostrar información del producto
        document.getElementById("name").textContent = product.name;
        document.getElementById("description").textContent =
          product.description;
        document.getElementById("rating").textContent =
          `Valoración: ${product.rating}★`;

        // Mostrar imágenes
        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("");

        // Poblar 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,
        );

        // Estado del stock
        let stockText = "En stock";
        if (variant.stockMode !== 1) {
          if (variant.stock === 0) stockText = "Sin stock";
          else if (variant.stock < 10)
            stockText = `Solo quedan ${variant.stock}`;
        }
        document.getElementById("stock").textContent = stockText;

        // Actualizar límites de cantidad
        const qtyInput = document.getElementById("quantity");
        qtyInput.min = variant.minimumQuantity;
        qtyInput.max =
          variant.maximumQuantity === -1 ? 999 : variant.maximumQuantity;
        qtyInput.value = variant.minimumQuantity;

        // Deshabilitar botón si sin 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("¡Añadido al carrito!");
      }

      async function loadReviews(page) {
        const response = await komerza.getProductReviews(product.id, page);

        if (!response.success || response.data.length === 0) {
          document.getElementById("reviews").innerHTML =
            "<p>Sin reseñas aún</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>Respuesta: ${review.reply}</em></p>` : ""}
        </div>
      `,
          )
          .join("");
      }

      // Cargar producto desde URL o por defecto
      const urlParams = new URLSearchParams(window.location.search);
      const productId = urlParams.get("product") || "your-default-product-slug";
      loadProduct(productId);
    </script>
  </body>
</html>

Próximos pasos

Formato de moneda

Formatea precios en la moneda de tu tienda

Gestión del carrito

Añade productos al carrito