# Nasazení balíčku na Shoptet krok za krokem

> Od nahrání souborů na FTP přes HTML vložky a shortcode až po ověřovací checklist — celý postup, jak dostat konfigurátor balíčků na živý e-shop.

- **Zdroj:** https://www.webotvurci.cz/navody/sestav-si-vlastni-balicek/nasazeni-balicku-na-shoptet-krok-za-krokem/
- **Doplněk / kategorie:** Sestav si vlastní balíček (https://www.webotvurci.cz/navody/sestav-si-vlastni-balicek/)
- **Datum:** 2026-08-12
- **Autor:** Webotvůrci (https://www.webotvurci.cz/)

---

Konfigurátor balíčků nemá vlastní šablonu ani routu — vykresluje se do obsahu běžné stránky e-shopu. Nasazení je proto instalatérská práce: nahrát dva soubory, zaregistrovat je v administraci, vložit shortcode do stránky a ověřit, že vše sedí. Tenhle návod je psaný pro vývojáře nebo agenturu, která e-shop spravuje.

Ve všech ukázkách používáme smyšlený e-shop `www.example.cz` s `projectId` **123456** a konfiguraci **Kávový balíček** se shortcodem `[w-multipack-ABC12]`.

Pokud ještě nevíte, co doplněk dělá a jak vzniká sada, začněte návodem [Jak funguje doplněk Sestav si vlastní balíček](https://www.webotvurci.cz/navody/sestav-si-vlastni-balicek/jak-funguje-doplnek-sestav-si-vlastni-balicek/).

## Co potřebujete, než začnete

| Co | Poznámka |
|---|---|
| **Shoptet Premium** | doplněk pracuje se Shoptet API a s nativním košíkovým rozhraním šablony |
| **Aktivní licence doplňku** | po instalaci doběhne úloha, která v e-shopu připraví skrytou kategorii pro generované sady |
| **Přístup do administrace balíčků** | webová administrace na naší straně, kde se zakládají konfigurace |
| **Shortcode konfigurace** | vygeneruje se při jejím založení, v administraci se zobrazuje v hranatých závorkách |
| **Testovací konfiguraci** | vyžádejte si ji u nás — zakládání sad vytváří v e-shopu reálné produkty, takže se netestuje na ostré konfiguraci |
| **FTP nebo souborový manažer Shoptetu** | pro nahrání JS a CSS |

Co naopak **nepotřebujete**: API klíč, token ani podpis požadavku. Veřejné endpointy doplňku jsou neautentizované a e-shop se identifikuje číslem `projectId` v adrese.

## Krok 1: Nahrání JS a CSS na Shoptet FTP

Shoptet má vlastní úložiště statických souborů dostupné přes FTP; obsah se servíruje z domény e-shopu pod cestou `/user/documents/`. Doporučujeme vlastní podadresář:

```
/user/documents/multipack/
├── multipack.js
├── multipack.css
└── img/placeholder.svg
```

Výsledné veřejné adresy pak vypadají takto:

```
https://www.example.cz/user/documents/multipack/multipack.js
https://www.example.cz/user/documents/multipack/multipack.css
```

Podadresář používejte vždycky. Kořen `/user/documents/` bývá na starších e-shopech skládka po předchozích dodavatelích a přepsat tam cizí soubor je otázka jednoho nepozorného přetažení.

Počítejte s tím, že jde o **čistě statické úložiště** — žádné PHP, žádné přepisování cest. Nahrání se navíc neprojeví okamžitě, protože soubory jdou přes CDN vrstvu s dlouhým TTL; proto cache busting v dalším kroku. FTP taky nemá verzování ani rollback, takže si zdrojáky držte v gitu a na FTP je jen deployujte. Alternativou je hostovat assety na vlastní CDN — dává to smysl, když máte build pipeline nebo spravujete stejný doplněk na víc e-shopech.

## Krok 2: Registrace v HTML vložkách

Shoptet umí vložit vlastní HTML do hlavičky a do patičky každé stránky e-shopu. V administraci to najdete pod vzhledem e-shopu jako **HTML vložky / HTML kód** (název položky se mezi verzemi liší) — jsou tam dvě pole: jedno se vkládá před `</head>`, druhé před `</body>`.

Do hlavičky patří CSS:

```html
<!-- Sestav si vlastní balíček – konfigurátor -->
<link rel="stylesheet" href="https://www.webotvurci.cz/user/documents/multipack/multipack.css?v=1.00">
```

Do patičky skript:

```html
<!-- Sestav si vlastní balíček – konfigurátor -->
<script src="https://www.webotvurci.cz/user/documents/multipack/multipack.js?v=1.00" defer></script>
```

Používejte **relativní cesty**, ne absolutní s doménou. E-shop může běžet na víc doménách (jazykové mutace, testovací doména na `myshoptet.com`) a absolutní adresa by je rozbila.

### Proč `defer` i v patičce

Atribut `defer` zajistí, že se skript stáhne paralelně s parsováním dokumentu a spustí se až po jeho dokončení. Tím pádem neblokuje vykreslování (což se pozná na LCP) a zároveň má k dispozici hotový DOM i objekty Shoptetu. Uvnitř skriptu se přesto navažte na `DOMContentLoaded`, aby kód přežil i to, že by někdo `defer` odstranil nebo vložku přesunul.

### Proč `?v=` na konci adresy

Soubory z `/user/documents/` jdou přes CDN a browser cache s dlouhým TTL. Bez změny adresy se nová verze nemusí projevit hodiny a klient bude oprávněně tvrdit, že jste nic nenasadili. Řešení je triviální: při **každé** změně souboru zvyšte číslo verze v HTML vložce.

Tři věci, které se vyplatí dodržet:

- **Verzujte JS a CSS společně** jedním číslem. Rozjeté verze (CSS `1.02`, JS `1.07`) jsou klasický zdroj hlášek typu „u mě to vypadá jinak".
- Ideálně použijte **hash obsahu** generovaný buildem místo ručně psaného čísla.
- Pamatujte, že deploy je vždy dvoukrokový — nahrát soubor **a** upravit vložku. Když zapomenete na druhý krok, nestane se vůbec nic, a to je ta nejhorší varianta.

Ještě jedna věc: HTML vložky se aplikují na **všechny stránky e-shopu**, včetně košíku a objednávky. Skript proto musí sám poznat, jestli má na dané stránce co dělat — typicky se ptá „je v obsahu shortcode?" — a jinak se okamžitě ukončit. Žádná těžká práce ani volání API „pro jistotu" na každém zobrazení stránky.

## Krok 3: Stránka se shortcodem

1. V administraci Shoptetu založte novou stránku, například `/kavovy-balicek/`.
2. Do editoru napište shortcode **jako prostý text**: `[w-multipack-ABC12]`.
3. Kolem něj si napište libovolný marketingový obsah — nadpis, odrážky s výhodami, badge se slevou, fotky. Nahradí se jen samotný shortcode, zbytek stránky zůstane.
4. Uložte a otevřete stránku na frontendu.

Shortcode přitom nemusí být jen na statické stránce. Funguje všude, kde se renderuje uživatelský obsah — v článku blogu, v popisu kategorie, v textovém bloku na homepage. Z toho plyne důležité pravidlo pro váš skript: **podmínkou spuštění ať je přítomnost shortcodu, ne typ stránky.** Statické stránky mají v dataLayeru jiný `pageType` než kategorie nebo homepage a navázání na něj vás dřív nebo později zradí.

Při hledání shortcodu nahrazujte **konkrétní textový uzel**, ne `innerHTML` celého kontejneru obsahu. Přepis `innerHTML` sice funguje, ale zabije event listenery všeho uvnitř — jiných doplňků i nativních komponent šablony.

## Krok 4: Kontext ze Shoptet dataLayeru

Doplněk nemá vlastní konfigurační objekt. Vše potřebné je ve standardním dataLayeru Shoptetu, což je oficiální a stabilní rozhraní: `projectId` do adresy API, `currency` pro výběr správné cenové hodnoty, `currencyInfo` pro formátování a `customer.priceListId` pro ceny podle cenové hladiny přihlášeného zákazníka.

```js
function getShoptetContext() {
  if (typeof getShoptetDataLayer === 'function') {
    const data = getShoptetDataLayer();
    if (data) return data;
  }
  if (Array.isArray(window.dataLayer)) {
    const entry = window.dataLayer.find(
      (item) => item && item.shoptet && item.shoptet.projectId !== undefined
    );
    if (entry) return entry.shoptet;
  }
  return {};
}

const ctx = getShoptetContext();
const projectId = ctx.projectId;              // 123456
const currency  = ctx.currency || 'CZK';      // "CZK"
const priceKey  = 'price_' + currency;        // "price_CZK"

const rawPriceList = ctx.customer && ctx.customer.priceListId;
const priceListId  = Number.isInteger(rawPriceList) ? rawPriceList : null;

if (!projectId) return; // dataLayer nedorazil, nemá smysl volat API
```

Kód je obranný schválně. Oficiální helper `getShoptetDataLayer()` nemusí být v každé verzi šablony k dispozici, proto ten fallback na syrové pole. A `priceListId` **může chybět nebo být `null`** — pak se poslední segment adresy prostě vynechá. Právě tady vzniká nejčastější chyba celé integrace: zápis `${priceListId ?? null}` vložený rovnou do šablonového řetězce vyrobí v adrese literál `/null`, konfigurace se nenačte a na stránce zůstane viditelný holý shortcode.

Formátování cen berte také z dataLayeru (`symbol`, `symbolLeft`, `decimalSeparator`, `thousandSeparator`, `priceDecimalPlaces`), ne natvrdo. Jinak bude widget psát `779,45 Kč` na e-shopu, který všude jinde ukazuje `779 Kč`. Zákazník si toho nevšimne, klient na první schůzce ano.

## Krok 5: Vložení do košíku

Backend vrátí kód nově vytvořené sady (v našem příkladu `WT-SET-00042`). Ten předejte **nativní funkci Shoptetu**:

```js
function addSetToCart(code, amount) {
  const ok = typeof shoptet !== 'undefined'
    && shoptet.cartShared
    && typeof shoptet.cartShared.addToCart === 'function';

  if (!ok) {
    showError('Košík se nepodařilo otevřít, zkuste stránku načíst znovu.');
    return false;
  }

  shoptet.cartShared.addToCart({ productCode: code, amount: amount });
  return true;
}
```

Vlastní `fetch` na košíkový endpoint Shoptetu vypadá jako kratší cesta, ale nefunguje spolehlivě a nefunguje dobře. Důvody jsou čtyři:

1. **CSRF ochrana.** Košíkové akce Shoptetu chrání rotující token, který nativní funkce dodá za vás.
2. **Přepočet košíku.** Po přidání se aktualizuje mezisoučet, doprava zdarma, dárky, slevové mechaniky i ikona košíku v hlavičce. Vlastní požadavek tohle neudělá a zákazník uvidí nesouhlasící čísla.
3. **Zpětná vazba pro zákazníka.** Šablona zobrazí své standardní potvrzení, které zákazník zná ze zbytku e-shopu — a vy nemusíte nic stavět.
4. **Odolnost.** Interní košíkové endpointy se mohou změnit, `shoptet.cartShared` je součást veřejného šablonového API.

Pokud potřebujete na přidání zareagovat (zavřít konfigurátor, odeslat událost do GA4), poslouchejte DOM události Shoptetu, například `ShoptetDOMCartContentLoaded`.

## Ověřovací checklist po nasazení

Vše kontrolujte v DevTools a na **testovací konfiguraci**:

- **Assety:** `multipack.css` i `multipack.js` vrací 200, adresa obsahuje aktuální `?v=`, skript se načítá právě jednou.
- **Mount:** na stránce není vidět holý text `[w-multipack-ABC12]`, zbytek obsahu stránky zůstal nedotčený, konzole je čistá.
- **Konfigurace:** právě jeden požadavek na konfiguraci se stavem 200, adresa neobsahuje `/null` ani `/undefined`, počet slotů odpovídá hodnotě `product_count`.
- **Výběr:** během skládání balíčku neproběhne žádný síťový požadavek, orientační cena sedí (289 + 329 + 299 = 917 Kč, po 15 % → 779,45 Kč), tlačítko do košíku je nedostupné, dokud není obsazený každý slot.
- **Košík:** po kliknutí odejde požadavek na založení sady, zobrazená závazná cena je ta z odpovědi, položka je v košíku a ikona košíku se aktualizovala. Rychlý dvojklik nesmí vytvořit dvě položky.
- **Napříč:** mobilní zařízení, pomalé připojení (musí být vidět stav načítání, ne prázdno), přepnutí měny, přihlášený zákazník s cenovou hladinou.
- **Objednávka:** jednu testovací objednávku dokončete a zkontrolujte, že se sada správně propsala.

## Když to nefunguje

- **Konfigurátor se nevykreslí vůbec.** Zkontrolujte v Network, jestli se `multipack.js` načetl. Stav 404 znamená překlep v cestě, žádný požadavek znamená neuloženou HTML vložku — nebo vložku uloženou u jiné jazykové mutace.
- **Na stránce zůstal viditelný shortcode.** Nejčastěji ho rozbil WYSIWYG editor. Otevřete zdrojový kód stránky a najděte `w-multipack`: uvidíte tam entity místo hranatých závorek, `&nbsp;` uvnitř nebo text rozsekaný do několika elementů po vložení z Wordu. Spolehlivý postup je přepnout editor do režimu zdrojového kódu a napsat shortcode ručně na samostatný řádek. Pozor, shortcode je **case-sensitive** — pět znaků za prefixem jsou vždy velká písmena a číslice.
- **Konfigurace se nenačte.** Zkontrolujte adresu požadavku: nesmí obsahovat `/null` (viz krok 4) a shortcode v ní musí být **bez hranatých závorek**. Odpověď 404 znamená, že dvojice `projectId` a shortcode neodpovídá žádné konfiguraci — typicky testujete na jiném e-shopu, než kde je založená.
- **Nejde vložit do košíku.** Pokud `shoptet.cartShared` není definovaný, běží váš skript dřív než skripty šablony — zkontrolujte `defer` a to, že jste v patičce, ne v hlavičce.
- **Konflikt se šablonou.** Konfigurátor přestal fungovat po přidání jiného doplňku? Podezřelý číslo jedna je cizí skript, který přepisuje `innerHTML` kontejneru obsahu. Své CSS mějte zanořené pod kořenovou třídou widgetu, ať nestylujete holé selektory na celém e-shopu, a init si pojistěte globálním příznakem pro případ, že by se skript načetl ze dvou vložek.

Podrobnější rozpad symptomů, příčin a řešení je ve [FAQ dokumentu na GitHubu](https://github.com/Webotvurci-s-r-o/shoptet-premium-multipack/blob/main/docs/06-faq-a-reseni-problemu.md).

## Kam dál

- [API doplňku pro vývojáře](https://www.webotvurci.cz/navody/sestav-si-vlastni-balicek/api-doplnku-sestav-si-vlastni-balicek-pro-vyvojare/) — kontrakt obou endpointů a minimální implementace.
- [Starter widget na GitHubu](https://github.com/Webotvurci-s-r-o/shoptet-premium-multipack/tree/main/examples/starter) — funkční vanilla JS implementace včetně HTML vložek k převzetí. Celý repozitář najdete na [github.com/Webotvurci-s-r-o/shoptet-premium-multipack](https://github.com/Webotvurci-s-r-o/shoptet-premium-multipack).
- [Sestav si vlastní balíček](https://www.webotvurci.cz/sluzby/shoptet/shoptet-premium-e-shop-bez-limitu/dynamicke-sady-balicky-na-miru/) — produktová stránka doplňku.

Potřebujete testovací konfiguraci nebo se někde zaseklo nasazení? Napište na **jsme@webotvurci.cz**.

---

*Návod pochází z webu Webotvůrci — Zlatý a Premium partner Shoptetu.*
*Všechny návody: https://www.webotvurci.cz/navody/ · Kontakt: jsme@webotvurci.cz*
