+420 728 089 029EN

API doplňku Sestav si vlastní balíček: dokumentace pro vývojáře

7 min čtení

Doplněk Sestav si vlastní balíček je headless. Naše strana drží konfigurace balíčků, synchronizuje produktová data ze Shoptetu, počítá závaznou cenu a zakládá sady. Jak bude konfigurátor vypadat a chovat se, je čistě na vás.

Není to ideologie, ale praktické rozhodnutí. Agentura, která spravuje klientův e-shop, tak nemusí do jeho šablony pouštět cizí kód a widget si vyladí přesně podle designu. E-shop s vlastním vývojářem si zase může postavit UI, které sedne jeho sortimentu — něco jiného potřebuje prodejce kávy a něco jiného dárkové koše. My dodáme data a závaznou cenu, vy zbytek.

Tenhle text je průvodce rozhraním, ne jeho úplná specifikace. Kompletní referenci pole po poli najdete na GitHubu.

Dvě volání, žádné klíče

Celé frontendové API tvoří dva endpointy:

Metoda Účel
GET …/multipack/configuration-products/{shortcode}[/{priceListId}] načtení konfigurace balíčku a nabídky produktů
POST …/multipack/product-set odeslání výběru zákazníka a založení sady

Základ adresy je https://shoptet.webotvurci.cz/eshop-endpoints/{projectId}/multipack/, kde projectId je ID e-shopu ze Shoptet dataLayeru. E-shop se identifikuje výhradně tímhle číslem — žádný API klíč, token ani podpis se neposílá a CORS je otevřený, takže voláte přímo z prohlížeče včetně vývojového localhost. Proxy přes vlastní backend stavět nemusíte.

Konfiguraci identifikuje shortcode ve tvaru w-multipack-ABC12 (prefix plus pět znaků, celkem 17). V administraci se zobrazuje v hranatých závorkách, protože v té podobě ho obchodník vkládá do stránky — do API se posílá bez nich.

Doporučujeme volat s credentials: 'omit'. Session doplněk nepotřebuje a ušetříte si starosti s cookies třetích stran a SameSite.

Načtení konfigurace

První volání proběhne jednou při inicializaci widgetu a vrátí všechno naráz: parametry balíčku i kompletní nabídku produktů. Skládání balíčku pak běží čistě v prohlížeči, bez dalších požadavků.

curl -H 'Accept: application/json' \
  'https://shoptet.webotvurci.cz/eshop-endpoints/123456/multipack/configuration-products/w-multipack-ABC12'

Poslední segment s priceListId připojte jen tehdy, když má přihlášený zákazník přiřazenou cenovou hladinu (shoptet.customer.priceListId v dataLayeru je číslo). Ceny v odpovědi pak odpovídají jeho ceníku. Když hladinu nemá, segment vynechte — nikdy do adresy nedávejte literál null.

Na kořenové úrovni odpovědi (zabalené v data) najdete čtyři hodnoty:

  • product_count (2–5) — kolik produktů musí zákazník vybrat. Je to zdroj pravdy pro počet slotů, neodvozujte ho odjinud.
  • discount (1–100) — sleva na celý balíček v procentech.
  • list_modesame_lists, nebo separate_lists.
  • categories — pole kategorií s produkty.

Režim seznamů určuje render. U same_lists vykreslíte product_count slotů a do každého dáte všechny kategorie i produkty. U separate_lists seskupíte kategorie podle jejich column_number (1 až product_count) a slot N naplníte jen kategoriemi s odpovídajícím číslem.

Každý produkt v kategorii nese to, co konfigurátor potřebuje k vykreslení:

{
  "guid": "a1b2c3d4-0001-4000-9000-000000000001",
  "name": "Zrnková káva Brazílie 250 g",
  "url": "https://www.example.cz/zrnkova-kava-brazilie-250-g/",
  "image": "1021-1_zrnkova-kava-brazilie-250-g.jpg?ff=1&x=1024&y=768&q=85&ts=6b0c1d2e&sg=1a2b3c4d",
  "stock": "42.000",
  "rating": 4.6,
  "ratingCount": 18,
  "price_CZK": 289,
  "price_EUR": 11.56
}

Čtyři detaily, na kterých se dá snadno naběhnout:

  • Cena má dynamický klíč. Pro každou měnu e-shopu vzniká jeden — price_CZK, price_EUR a tak dál. Nehardcodujte price_CZK, sahejte na product['price_' + ctx.currency].
  • stock je řetězec s desetinnou částí ("42.000"), ne číslo. Před porovnáním použijte parseFloat.
  • image není URL, ale jen název souboru včetně podepsaného query stringu. Ten se nesmí odstraňovat ani znovu enkódovat. Výslednou adresu složíte jako https://cdn.myshoptet.com/usr/{hostname}/user/shop/orig/{image}, kde hostname berete z pole url produktu (new URL(product.url).hostname). Počítejte s tím, že image i url mohou být null — mějte připravený placeholder.
  • Pořadí kategorií i produktů si seřaďte sami. Pokud na něm v UI záleží (a zpravidla ano), setřiďte je podle column_number a názvu.

Hodnota rating je průměr 0–5, kde nula znamená „zatím nehodnoceno" (spolu s ratingCount: 0) — nezobrazujte ji jako nejhorší možné hodnocení.

Založení sady

Druhé volání odešlete až v okamžiku, kdy zákazník výběr skutečně potvrdí. Tělo je minimalistické:

{
  "shortcode": "w-multipack-ABC12",
  "guids": [
    "a1b2c3d4-0001-4000-9000-000000000001",
    "a1b2c3d4-0002-4000-9000-000000000002",
    "a1b2c3d4-0003-4000-9000-000000000003"
  ]
}

Pole guids musí obsahovat přesně product_count položek. A ještě jedno pravidlo, na které se nezapomíná dvakrát: GUIDy před odesláním deterministicky seřaďte. Backend rozpoznává už existující sady podle kombinace GUIDů v pořadí, v jakém dorazily, takže táž trojice v jiném pořadí vyrobí druhou sadu se stejným obsahem. Jeden .sort() navíc a máte klid.

Odpověď je plochá, bez obálky:

{
  "code": "WT-SET-00042",
  "price": 779.45,
  "products": [
    { "code": "KAVA-BR-250", "name": "Zrnková káva Brazílie 250 g", "image": "1021-1_zrnkova-kava-brazilie-250-g.jpg?ff=1&x=1024&y=768&q=85&ts=6b0c1d2e&sg=1a2b3c4d" },
    { "code": "KAVA-ET-250", "name": "Zrnková káva Etiopie 250 g", "image": "1022-1_zrnkova-kava-etiopie-250-g.jpg?ff=1&x=1024&y=768&q=85&ts=6b0c1d2f&sg=1a2b3c4d" },
    { "code": "KAVA-KO-250", "name": "Zrnková káva Kolumbie 250 g", "image": "1023-1_zrnkova-kava-kolumbie-250-g.jpg?ff=1&x=1024&y=768&q=85&ts=6b0c1d30&sg=1a2b3c4d" }
  ]
}
  • code je kód sady — přesně tohle předáte do košíku Shoptetu.
  • price je závazná cena po slevě (917 Kč mínus 15 % = 779,45 Kč).
  • products je rozpis položek v pořadí odeslaných GUIDů. Hodí se pro potvrzovací obrazovku nebo pro hezčí rozpad balíčku v košíku, kde je jinak jedna položka se spojeným názvem.

Stavový kód 201 znamená, že sada právě vznikla, 200 že se stejná už dřív sestavila a vrací se uložená. Tělo je v obou případech identické, takže vám ve frontendu stačí testovat response.ok.

Zakládání sady probíhá synchronně a zahrnuje několik volání Shoptet API, takže počítejte s odezvou ve stovkách milisekund až jednotkách sekund. Tlačítko po kliknutí zablokujte, ukažte stav načítání a ošetřete dvojklik. Endpoint nevolejte spekulativně při každé změně výběru — jen při skutečném potvrzení.

Když se sadu založit nepodaří, nechte výběr zákazníka zachovaný a nabídněte opakování akce s obecnou hláškou („Balíček se teď nepodařilo připravit, zkuste to prosím znovu."). Automatické opakování ve smyčce nedělejte. Chyby si logujte i s projectId a shortcodem, ať je co dohledat. Validační odpovědi (stav 422) mají v těle klíč pole, kterého se týkají — používejte ho k diagnostice, ne jako text pro zákazníka, protože hlášky jsou anglicky a jejich znění není součástí kontraktu.

Pravidla, která je potřeba dodržet

Kontrakt je volný, ale čtyři body v něm jsou závazné:

  1. Závazná cena je ta z odpovědi. Průběžný přepočet ve widgetu je náhled pro zákazníka. Do souhrnu, do měření i do rozpisu v košíku patří hodnota price z odpovědi na založení sady. Obě čísla vznikají z trochu jiných vstupů — ceny v nabídce reflektují měnu a případný ceník zákazníka, cena sady se skládá ze základních cen v základní měně e-shopu. U e-shopu s jednou měnou a bez ceníků sedí, jinde se lišit mohou. Cenu proto nikdy neposílejte v požadavku a neslibujte nad orientačním číslem.
  2. Posílejte GUIDy z konfigurace. Do sady patří jen produkty, které přišly v odpovědi na první volání — a v režimu separate_lists jen ty ze správného slotu. Ohlídat to je součást kontraktu frontendu.
  3. Dostupnost hlídejte v UI. Pole stock máte k dispozici u každého produktu, takže vyprodané položky buď nenabízejte, nebo je viditelně označte a zablokujte jejich výběr. Finální kontrola pak proběhne standardními pravidly v košíku Shoptetu.
  4. Kód sady neukládejte. Je platný pro aktuální nákup, ne napořád — neobjednané sady se pravidelně uklízejí, aby se v katalogu nehromadily. Nepatří tedy do localStorage jako uložený balíček, do sdílených odkazů ani do zákaznického účtu. Ukládejte si GUIDy vybraných produktů a při návratu zákazníka sadu jednoduše založte znovu.

A jedna věc navíc, která s API nesouvisí, ale s bezpečností ano: data z API escapujte dřív, než je vložíte do DOM. Názvy produktů a kategorií jsou uživatelský obsah z administrace e-shopu, takže je skládejte přes textContent, ne konkatenací do innerHTML.

Minimální implementace

Celý flow se vejde do dvaceti pěti řádků. Chybí v nich jen UI a ošetření chyb, které si stejně napíšete po svém:

const BASE = 'https://shoptet.webotvurci.cz';
const SHORTCODE = 'w-multipack-ABC12';

const dl = dataLayer.find((d) => d?.shoptet?.projectId !== undefined).shoptet;
const projectId = dl.projectId;
const priceListId = dl.customer?.priceListId ?? null;

// 1) Konfigurace — segment s ceníkem přidáme jen když ceník existuje
const cfgBase = `${BASE}/eshop-endpoints/${projectId}/multipack/configuration-products/${SHORTCODE}`;
const cfgRes = await fetch(priceListId ? `${cfgBase}/${priceListId}` : cfgBase, {
  credentials: 'omit',
  headers: { Accept: 'application/json' },
});
if (!cfgRes.ok) throw new Error('Konfiguraci balíčku se nepodařilo načíst.');
const { data } = await cfgRes.json();

// 2) Výběr zákazníka — přesně data.product_count GUIDů z data.categories
const selectedGuids = getSelectedGuidsFromUi();

// 3) Založení sady — guids vždy seřazené
const setRes = await fetch(`${BASE}/eshop-endpoints/${projectId}/multipack/product-set`, {
  method: 'POST',
  credentials: 'omit',
  headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
  body: JSON.stringify({ shortcode: SHORTCODE, guids: [...selectedGuids].sort() }),
});
if (!setRes.ok) throw new Error('Sadu se nepodařilo vytvořit.');
const set = await setRes.json(); // { code, price, products[] }

// 4) Do košíku nativní funkcí Shoptetu; závazná cena je set.price
shoptet.cartShared.addToCart({ productCode: set.code, amount: 1 });

Poslední řádek stojí za zdůraznění: do košíku se vkládá nativní funkcí Shoptetu, ne vlastním požadavkem. Ta dodá CSRF token, přepočítá košík včetně dopravy zdarma a dárků a zobrazí standardní potvrzení šablony. Podrobněji to rozebírá návod o nasazení.

Kam dál

Chcete testovací konfiguraci nebo si nejste jistí návrhem integrace? Ozvěte se na jsme@webotvurci.cz, rádi to s vámi projdeme.