Blog
E-commerce19 min czytania

Saleor Storefront — minimalistyczny frontend sklepu z checkoutem v2

Oficjalny szablon frontendu Saleora, od niedawna pod nazwą Paper: Next.js 16, React 19, checkout przepisany na Server Components i wspólną sesję BFF, adresy `/{locale}/{channel}/` z polskim wśród siedmiu języków. Rozbieramy wzorzec „katalog z cache, checkout na żywo", kroki checkoutu w adresie URL i zamrożoną stronę potwierdzenia z HMAC. Najważniejsze na wstępie: szablon jest na licencji FSL, nie na open source — analizujemy dokładnie, co wolno agencji, a czego nie.

W sklepie headless podział wygląda prosto: backend daje API, front je konsumuje. Prosto wygląda tylko na diagramie. Katalog produktów to rzeczywiście kilka zapytań i lista kart — ale checkout to inna kategoria pracy. Formularze adresowe dla dwudziestu krajów, w których nie tylko nazwy pól, ale i ich obecność jest różna. Integracja z bramką płatności, która ma własny przepływ przekierowań. Sesja gościa, która musi przeżyć powrót z bramki. Wieloetapowość, w której przycisk Wstecz w przeglądarce nie może zepsuć zamówienia. I cache, który na katalogu musi być agresywny, a na koszyku żaden.

Napisanie tego od zera to kwartał pracy zespołu, a większość tego kwartału idzie na przypadki brzegowe, których nie widać w wycenie. Dlatego oficjalne szablony frontendu mają w e-commerce inną wartość niż w innych kategoriach oprogramowania.

Saleor Storefront (github.com/saleor/storefront), od niedawna występujący pod nazwą Paper, jest właśnie takim szablonem — i ma jedną cechę, którą trzeba omówić przed resztą wpisu, a nie po niej: nie jest oprogramowaniem open source w rozumieniu OSI. Zaczynamy więc od licencji, bo to ona decyduje, czy dalsza lektura ma dla kogoś sens.

Licencja, czyli rzecz do przeczytania przed pierwszym klonem

Szablon jest rozpowszechniany na FSL-1.1-ALv2 — Functional Source License w wersji 1.1, z przyszłą licencją Apache 2.0. Nota mówi „Copyright 2026 Saleor Commerce". Rozbierzmy to na części, bo diabeł jest w definicji jednego pojęcia.

Dozwolony jest każdy cel poza „Competing Use". Konkurencyjne wykorzystanie oznacza udostępnianie oprogramowania innym w komercyjnym produkcie lub usłudze, która:

  1. zastępuje to oprogramowanie,
  2. zastępuje inny produkt lub usługę Saleora opartą o to oprogramowanie i istniejącą w dniu jego udostępnienia,
  3. oferuje tę samą albo zasadniczo podobną funkcjonalność.

I teraz najważniejsze zdanie dla każdej agencji. Licencja wprost wylicza jako dozwolone: użytek i dostęp wewnętrzny, niekomercyjną edukację, niekomercyjne badania oraz — dosłownie — użycie oprogramowania „w związku z usługami profesjonalnymi, które świadczysz licencjobiorcy korzystającemu z oprogramowania zgodnie z tymi warunkami".

Budowanie sklepu klientowi na tym szablonie jest więc dozwolone wprost, nie przez interpretację — o ile klient również jest licencjobiorcą działającym w tych warunkach. To jest dokładnie ten scenariusz, w którym agencja pracuje, i licencja go przewiduje.

Czego nie wolno: zrobić z tego własnego produktu — hostowanej platformy sklepowej, „storefrontu jako usługi" ani niczego, co konkuruje z ofertą Saleora. README nie owija tego w bawełnę i kończy sekcję o licencji zaproszeniem: „Chcesz oferować to jako usługę zarządzaną? Porozmawiajmy".

Trzy pozostałe elementy warto znać:

  • Konwersja na Apache-2.0 po dwóch latach. Licencja przyszła jest udzielona nieodwołalnie i wchodzi w życie „w drugą rocznicę dnia udostępnienia oprogramowania". Praktycznie: ograniczenie dotyczy zawsze tylko dwóch ostatnich lat rozwoju, a wszystko starsze jest już na w pełni permisywnej licencji,
  • Warunki przechodzą na pochodne. Kopie, modyfikacje i dzieła pochodne są objęte tymi samymi warunkami; przy redystrybucji trzeba dołączyć licencję albo odnośnik do niej i nie usuwać not o prawach autorskich,
  • Klauzula patentowa jest dwustronna. Licencja obejmuje patenty w zakresie dozwolonego użytku, ale pozew patentowy przeciw komukolwiek o naruszenie przez to oprogramowanie kończy naszą licencję patentową natychmiast. Do tego brak prawa do znaków towarowych Saleora poza wskazaniem pochodzenia.

I rozdźwięk, który trzeba znać, bo bywa źródłem nieporozumień: rdzeń Saleora jest na BSD-3-Clause, czyli w pełni open source — to osobne repozytorium, w Pythonie, z 23 307 gwiazdkami. Pod FSL jest oficjalny szablon frontendu, nie silnik sklepu. Backend można więc hostować i modyfikować bez żadnych ograniczeń; ograniczenia dotyczą sprzedawania cudzego frontu jako własnego produktu.

Stan projektu

Dane z API GitHuba na 18 września 2026:

  • 1600 gwiazdek i — co ciekawsze — 887 forków. Ten stosunek jest nietypowy i mówi o naturze projektu więcej niż liczby bezwzględne: szablonów się nie gwiazdkuje, szablony się forkuje. Ponad połowa osób, które w ogóle zwróciły uwagę na to repozytorium, zrobiła z niego własną kopię,
  • kod w TypeScripcie, repozytorium założone 20 sierpnia 2021, ostatni commit z 7 września 2026,
  • tylko 8 otwartych zgłoszeń — i to też jest konsekwencja bycia szablonem, o czym w pułapkach.

Warto też zacytować samoocenę z README, bo jest uczciwa i rzadka: „Paper is a new release — expect some rough edges". Projekt sam siebie opisuje jako nowe wydanie z szorstkimi krawędziami, dodając, że każdy komponent jest zbudowany z myślą o realnym e-commerce. To lepszy punkt startowy do rozmowy z klientem niż obietnica gotowego produktu.

Stack

README nazywa go „bleeding edge" i jest to opis trafny, nie marketingowy:

  • Next.js 16 z App Router i Server Components,
  • React 19 z najnowszymi funkcjami współbieżnymi,
  • TypeScript w trybie ścisłym,
  • Tailwind CSS z tokenami projektowymi — kolory w OKLCH i zmienne CSS,
  • GraphQL Codegen do typowanych wywołań API Saleora.

Uruchomienie jest krótkie. Przez CLI Saleora albo ręcznie:

npm i -g @saleor/cli@latest
saleor storefront create --url https://{TWOJA_INSTANCJA}/graphql/

# albo
git clone https://github.com/saleor/storefront.git
cd storefront && cp .env.example .env && pnpm install
pnpm dev

Wymagane są dwie zmienne — adres GraphQL instancji i slug kanału:

NEXT_PUBLIC_SALEOR_API_URL=https://twoja-instancja.saleor.cloud/graphql/
NEXT_PUBLIC_DEFAULT_CHANNEL=default-channel

Checkout v2

To jest sedno tego wydania i powód, dla którego warto o nim pisać. Poprzednia wersja checkoutu żyła obok reszty aplikacji: opierała się na urql po stronie klienta i na tokenach Saleora w przeglądarce. Wersja druga zrównuje go z resztą stacku — App Router, Server Components, akcje serwerowe i ta sama sesja BFF co storefront.

Pięć decyzji projektowych wartych obejrzenia niezależnie od tego, czy kiedykolwiek dotkniemy Saleora:

  • Koszyk najpierw po stronie serwera. Komponent serwerowy wczytuje checkout i dane użytkownika przy wejściu; kontekst po stronie klienta jest tylko cache'em prawdy z serwera, a nie osobnym stanem. To zdanie z README warto powiesić nad biurkiem — większość błędów w koszykach wynika z dwóch niezależnych źródeł prawdy,
  • Kroki w adresie URL. ?step=contact|shipping|payment, aktualizowane płasko, bez pełnego przeładowania danych przy każdym kliknięciu. Skutek: przycisk Wstecz w przeglądarce przechodzi lejek, zamiast wyrzucać użytkownika ze sklepu,
  • Dedykowana strona potwierdzenia pod /order/{hmac} po płatności — adres jest zamrożony, a status i dane przesyłki odświeżają się z Saleora. Wariant /order/{saleorId}, którego użytkownik dostaje mailem, jest zredagowany, dopóki system go nie rozpozna. Kto kiedyś wystawił potwierdzenie zamówienia pod adresem z sekwencyjnym identyfikatorem, wie, dlaczego to jest istotne,
  • Rejestr bramek płatności (INTEGRATED_GATEWAYS) ze Stripe i bramką atrapową do testów; kolejne dodaje się przez opisany mechanizm,
  • Wspólna sesja BFF. Logowanie idzie przez /api/auth/login, a sesja jest rozwiązywana po stronie serwera z trzema stanami: gość, uwierzytelniony, niedostępny. Żadnych tokenów Saleora w przeglądarce — i to jest największa różnica bezpieczeństwa względem wersji pierwszej.

Dla forków siedzących jeszcze na starym checkoucie przygotowano opisaną procedurę migracji, a stare adresy /checkout/complete?order= przekierowują.

Płatności: sekrety poza frontem

To jest decyzja architektoniczna warta osobnego rozdziału, bo rozwiązuje problem, na którym potyka się większość integracji płatności. Paper celowo trzyma sekrety poza storefrontem i klucze Stripe'a nie trafiają do jego pliku .env nigdy. Podział jest trójdzielny:

  • Aplikacja Stripe w panelu Saleoraklucz tajny, klucz publikowalny i sekret podpisywania webhooków. Wszystko, co jest sekretem, leży tu,
  • .env storefrontu — wyłącznie flagi funkcji (NEXT_PUBLIC_ENABLE_STRIPE_PAYMENTS i pokrewne). Żadnych kluczy,
  • Czas działania — storefront otrzymuje klucz publikowalny od Saleora przez mutację paymentGatewayInitialize.

„Skonfiguruj Stripe'a" oznacza więc: ustaw klucze w aplikacji Stripe w Saleorze, a we froncie przestaw flagę. W praktyce sprowadza się to do dwóch linii:

NEXT_PUBLIC_ENABLE_STRIPE_PAYMENTS=true
ENABLE_STRIPE_PAYMENTS=true   # serwerowe odbicie dla zabezpieczenia transakcji

Zwróć uwagę na tę parę: flaga istnieje w dwóch wariantach, klienckim i serwerowym, bo zgoda na bramkę musi być weryfikowalna po stronie serwera, a nie tylko wyłączać przycisk w interfejsie. Przycisków płatności portfelowych (Apple Pay, Google Pay, Link) dotyczy osobna flaga, domyślnie włączona razem ze Stripe'em.

W środowisku deweloperskim Stripe włącza się sam, a do sprawdzenia przepływu bez konfigurowania czegokolwiek jest bramka atrapowa, również automatycznie aktywna lokalnie:

ALLOW_DUMMY_PAYMENT=true
NEXT_PUBLIC_ALLOW_DUMMY_PAYMENT=true

Dokumentacja podaje przy tym konkret, który oszczędza pół godziny szukania: karta 4000 0027 6000 3184 wywołuje przepływ z przekierowaniem 3DS. To ten scenariusz, który psuje sesję gościa w źle napisanym checkoucie — i właśnie dlatego warto go przetestować przed wdrożeniem, a nie po pierwszej reklamacji.

Struktura projektu

Układ katalogów jest tu informacją architektoniczną, nie porządkową, bo pokazuje granice odpowiedzialności:

src/
├── app/
│   ├── (storefront)/[locale]/[channel]/   # przeglądanie, koszyk, konto
│   └── (checkout)/checkout/               # checkout, osobna trasa
├── session-bridge/        # przekazanie sesji storefront ↔ checkout
├── checkout/              # interfejs checkoutu, rejestr bramek płatności
├── graphql/               # zapytania
├── gql/                   # typy generowane (nie edytować)
├── lib/
│   ├── catalog/           # cache'owane zapytania katalogu
│   ├── menus/             # nawigacja i stopka
│   ├── cache-manifest.ts  # rejestr tagów + mapowanie cacheLife
│   └── cache-life-profiles.ts
├── ui/components/         # pdp, plp, cart, account, primitywy
└── styles/brand.css       # tokeny projektowe

Dwie rzeczy warte odnotowania. Checkout jest osobną grupą tras ((checkout)), poza segmentem języka i kanału — a most sesji między nimi jest wydzielonym modułem. To jest właściwy podział: checkout ma inne wymagania co do cache'u, sesji i bezpieczeństwa niż katalog, więc nie dzieli z nim layoutu. I drugie: rejestr tagów cache'u razem z mapowaniem poziomów świeżości siedzi w jednym pliku, a nie jest rozsypany po komponentach. Przy inwalidacji z webhooków to różnica między jednym miejscem do sprawdzenia a polowaniem po repozytorium.

Personalizacja wygląda sensownie: kolory i typografia w jednym pliku tokenów (src/styles/brand.css), komponenty w src/ui/components/, przepływ checkoutu w osobnym katalogu. Tokeny to zmienne CSS, więc — jak pisze README — całą paletę podmienia się kilkoma linijkami.

Cache: katalog zamrożony, checkout na żywo

Ten rozdział jest najbardziej przenośny z całego wpisu, bo opisuje wzorzec, nie bibliotekę. Model nazywa się w dokumentacji „display-cached, checkout-live" i sprowadza się do jednego rozstrzygnięcia: strony służące do przeglądania są cache'owane agresywnie, a wszystko, co dotyczy koszyka i płatności, nie jest cache'owane w ogóle (cache: "no-cache").

Poziomy świeżości mają nazwy i konkretne wartości:

  • catalog — około 5 minut. Produkty, kategorie, kolekcje, strona główna,
  • menus — około godziny. Nawigacja, stopka, treści edytowalne przez sprzedawcę,
  • channels — około doby. Metadane kanałów w stopce,
  • koszyk i checkout — zawsze na żywo, z rzeczywistymi sumami liczonymi po stronie Saleora.

I najlepsze zdanie projektowe w całej tej dokumentacji: webhook z revalidateTag czyści dane natychmiast, a TTL jest siatką bezpieczeństwa na wypadek, gdy webhook nie dojdzie. To jest właściwy sposób myślenia o cache'u treści, którego wiele projektów nie ma: TTL nie jest mechanizmem odświeżania, jest zabezpieczeniem przed brakiem zdarzenia. Odświeża zdarzenie; czas tylko pilnuje, żeby awaria zdarzeń nie zamroziła sklepu na zawsze.

Technicznie stoi to na Cache Components z Next.js 16 — dyrektywie "use cache" oraz funkcjach cacheLife i cacheTag, włączanych flagą cacheComponents: true. Cache'owane zapytania GraphQL są przy tym trzymane w osobnych modułach (src/lib/catalog/, src/lib/menus/, src/lib/channels/), a nie w komponentach stron — strony są cienkimi orkiestratorami z zagnieżdżonymi granicami Suspense.

Na stronie produktu daje to częściowe prerenderowanie: nazwa produktu, atrybuty i dane SEO siedzą w statycznej, cache'owanej skorupie, a galeria wariantów i sekcja dodania do koszyka doczytują się przez Suspense, gdy zmienią się parametry zapytania. Użytkownik widzi treść natychmiast, a interaktywne części dochodzą — bez migotania całej strony przy każdej zmianie wariantu.

Wielojęzyczność i wielokanałowość

Tu jest coś, co dla polskiego rynku ma bezpośrednie znaczenie. Adresy przeglądania mają postać /{locale}/{channel}/…, a przykład podany wprost w README to /pl/pl/products/bluza — polski język, polski kanał, ceny w złotówkach i przetłumaczony slug katalogowy, jeśli został ustawiony w Saleorze. Obok tego /en/us/products/hoodie dla rynku amerykańskiego. Stare adresy w formie /{channel}/… przekierowują automatycznie.

Wbudowanych języków jest siedem — en, pl, de, fr, fi, nb, ko — i rozszerza się je przez definicje w konfiguracji. Ciekawsze jest jednak to, że projekt rozdziela trzy niezależne systemy tekstów, co przy sklepach wielojęzycznych jest właściwym podziałem i rzadko bywa zrobione:

  1. tłumaczenia katalogu — żyją w Saleorze, bo to dane produktowe,
  2. treści edytowalne przez sprzedawcę — warstwa CMS: strona główna, komunikaty zaufania w koszyku, teksty redakcyjne w checkoucie,
  3. teksty interfejsu należące do kodu — przez next-intl, w plikach messages/{locale}.json.

Do tego przełącznik regionu w nagłówku, przestawiający jednocześnie język i kanał (a więc rynek i walutę), z przemapowaniem adresów katalogowych na kanoniczny slug danego języka, oraz metadane hreflang i canonical liczone per język.

Kanały są jawnym allowlistem i to jest dobra decyzja bezpieczeństwa: Saleor może mieć kanały B2B, hurtowe albo wewnętrzne, a storefront wystawia tylko te wymienione w STOREFRONT_CHANNELS. Adresy niedozwolonych kanałów zwracają 404. Dla sklepu jednokanałowego wystarczy sam NEXT_PUBLIC_DEFAULT_CHANNEL, a przełącznik w stopce ukrywa się sam.

STOREFRONT_CHANNELS=pl,de,uk
NEXT_PUBLIC_DEFAULT_CHANNEL=pl
NEXT_PUBLIC_STOREFRONT_LOCALES=pl,en,de
SALEOR_APP_TOKEN=...   # tylko serwerowo, metadane walut w stopce

Jest tu jedna zmienna, której nie należy włączać bez powodu: STOREFRONT_DISCOVER_CHANNELS=true odkrywa wszystkie aktywne kanały z API. Dokumentacja sama tego nie zaleca przy instancjach z wieloma kanałami — i słusznie, bo oznacza to wystawienie w adresach URL kanałów, o których istnieniu marketing nie wie.

Zmienne, które wymieniają pieniądze na świeżość

Rzecz, którą warto pochwalić osobno, bo w szablonach spotyka się ją rzadko: osobna grupa zmiennych środowiskowych opisana wprost jako kontrola kosztów, z komentarzem, że każda z nich wymienia pieniądze na świeżość danych:

  • NEXT_PUBLIC_PAPER_IMAGE_PIPELINE — czy obrazy katalogu idą przez optymalizator Next.js (i są rozliczane), czy prosto z Saleora,
  • IMAGE_ALLOWED_HOSTS — dodatkowe hosty, których obrazy wolno optymalizować, z dopiskiem „i za które zapłacisz",
  • NEXT_IMAGE_MIN_CACHE_TTL — domyślnie 31 dni, do obniżenia, jeśli katalog jest często przefotografowywany,
  • SALEOR_MIN_REQUEST_DELAY_MS — dławienie zapytań GraphQL w czasie działania, domyślnie 200 ms w trakcie budowania. Zabezpieczenie przed wyczerpaniem limitów instancji przy generowaniu statycznych stron.

Do tego REVALIDATE_SECRET do ręcznej inwalidacji cache'u i SALEOR_WEBHOOK_SECRET do weryfikacji HMAC webhooków — obie rzeczy, o których w szablonie łatwo zapomnieć, a które przy wystawieniu endpointu inwalidacji do internetu są obowiązkowe.

Baza kodu przygotowana pod agentów

Element, który w kontekście naszej pracy zasługuje na uwagę bardziej niż sam sklep. Projekt jawnie deklaruje, że jest zbudowany dla programistów front-endu i dla agentów AI, i robi to konkretnie:

  • AGENTS.md — przegląd architektury i szybka referencja dla asystentów,
  • 21 reguł zadaniowych w katalogu skills/saleor-paper-storefront/, obejmujących GraphQL, cache, internacjonalizację, wybór wariantów i checkout v2,
  • zewnętrzne skille z osobnego repozytorium saleor/agent-skills, ładowane komendą pnpm skills:bootstrap i zablokowane wersjami w pliku skills-lock.json,
  • ADR-y w docs/adr/ dokumentujące decyzje architektoniczne — routing języków i kanałów, rozdział treści CMS od tekstów w kodzie, przetłumaczone slugi.

Plik lock na instrukcje dla asystentów to rozwiązanie, którego jeszcze się prawie nie widzi, a które rozwiązuje realny problem: instrukcje pobierane z zewnętrznego repozytorium bez wersjonowania zmieniają zachowanie zespołu bez commita. Niezależnie od Saleora, ten wzorzec — reguły w repozytorium, wersjonowane razem z kodem, z blokadą wersji dla źródeł zewnętrznych — jest wart skopiowania.

Co z tego wynika dla zespołu w Laravelu

Trzeba to powiedzieć wprost: to nie jest projekt do wpięcia w nasz stack. Next.js 16, React 19, GraphQL i serwer w Node to inny świat niż Laravel z Inertią. Sens ma w dwóch sytuacjach.

Pierwsza: klient wybiera Saleora jako backend e-commerce — bo potrzebuje wielokanałowości, wielu walut albo API-first — i potrzebuje frontu. Wtedy ten szablon oszczędza kwartał pracy nad checkoutem i jest właściwym punktem startowym, a licencja na to pozwala wprost.

Druga: podpatrzenie rozwiązań, które są przenośne. Trzy warto wynieść:

  • Model „katalog z cache, checkout na żywo". W Laravelu to dokładnie ten sam podział: cache na widokach katalogu z inwalidacją przez zdarzenie modelu, zero cache'u na koszyku i podsumowaniu zamówienia, a TTL wyłącznie jako zabezpieczenie na wypadek zgubionego zdarzenia,
  • Kroki checkoutu w adresie URL. Problem „przycisk Wstecz psuje formularz wieloetapowy" w aplikacji na Inertii wygląda identycznie i ma identyczne rozwiązanie: krok w parametrze zapytania, aktualizowany bez pełnej wizyty,
  • Zamrożone potwierdzenie zamówienia z HMAC i redakcja danych. Adres /order/{id} z sekwencyjnym identyfikatorem to klasyczny wyciek danych zamówień. Wzorzec z Papera — adres oparty o HMAC dla gościa i redakcja treści, dopóki użytkownik nie zostanie rozpoznany — przenosi się na Laravela wprost, przez podpisane adresy URL.

Pułapki

  • Licencja nie jest open source. Do użytku wewnętrznego i do pracy dla klienta — w porządku, wprost. Do zbudowania własnej usługi sklepowej — nie, i to trzeba skonsultować, zanim powstanie model biznesowy. Ograniczenie znika po dwóch latach od udostępnienia danej wersji,
  • „Nowe wydanie, spodziewaj się szorstkich krawędzi" — samoocena z README, którą warto powtórzyć klientowi przed podpisaniem terminu,
  • Bardzo świeży stack. Next.js 16 z Cache Components za flagą oznacza, że część zachowań może się jeszcze zmienić po stronie frameworka. Przy sklepie, który ma stać trzy lata, to jest ryzyko do wpisania w plan utrzymania,
  • 887 forków przy 8 otwartych zgłoszeniach to nie dowód dojrzałości, a konsekwencja modelu: problemy rozwiązuje się we własnym forku, nie w upstreamie. Fork szablonu jest zobowiązaniem — nie ma automatycznych aktualizacji, a poprawki z góry trzeba wnosić ręcznie,
  • Migracja z checkoutu v1 to osobna procedura. Jeśli przejmujemy projekt oparty o starszy fork z checkoutem na urql, jest przygotowana instrukcja — ale jest to praca, nie aktualizacja zależności,
  • Domyślne zachowanie potrafi kosztować. Zmienne kontroli kosztów istnieją właśnie dlatego; przy dużym katalogu warto je przejrzeć przed pierwszym wdrożeniem, a nie po pierwszym rachunku,
  • Nie włączaj odkrywania wszystkich kanałów, jeśli instancja Saleora ma kanały wewnętrzne. Jawny allowlist jest tu jedynym bezpiecznym ustawieniem,
  • Autorzy sami wymieniają trzy luki i warto je znać przed wyceną: brak zlokalizowanych stron błędu i „nie znaleziono" dla globalnych granic błędów, filtrowanie produktów jeszcze nie pobiera atrybutów dynamicznie z API, a tryb podglądu treści nieopublikowanych nie jest zaimplementowany. Żadna z tych rzeczy nie blokuje wdrożenia, ale każda jest pracą po naszej stronie,
  • Skille dla asystentów wymagają jednej komendy po klonie. Katalog skills/ w korzeniu repozytorium nie jest skanowany automatycznie — trzeba odpalić pnpm skills:bootstrap. Dokumentacja ostrzega przy tym wprost, żeby nie instalować skilla przez kopiowanie, bo powstaje wtedy rozjeżdżająca się migawka zamiast dowiązania.

Podsumowanie

Saleor Storefront w wydaniu Paper jest dobrym przykładem szablonu, który rozwiązuje trudną część problemu, a nie łatwą. Katalog produktów napisze każdy; checkout z wieloetapowością, sesją gościa, rejestrem bramek płatności i bezpieczną stroną potwierdzenia to praca, której nikt nie wycenia poprawnie. Co z tego wynika:

  • Przeczytaj licencję pierwszą, nie ostatnią. FSL-1.1-ALv2 pozwala na użytek wewnętrzny i na usługi profesjonalne dla klienta będącego licencjobiorcą — wprost, w treści licencji. Zakazuje konkurencyjnego wykorzystania i konwertuje się na Apache-2.0 po dwóch latach,
  • Rdzeń Saleora to BSD-3-Clause — pełne open source. Pod FSL jest tylko oficjalny szablon frontu,
  • Checkout v2 zniósł tokeny Saleora z przeglądarki i przeniósł sesję na wspólną warstwę BFF. To jest różnica bezpieczeństwa, nie kosmetyka,
  • Model „display-cached, checkout-live" jest przenośny. Odświeża zdarzenie, nie czas; TTL jest zabezpieczeniem przed brakiem zdarzenia,
  • Kroki checkoutu trzymaj w adresie URL, żeby przycisk Wstecz nie wyrzucał użytkownika z lejka,
  • Nie wystawiaj potwierdzenia zamówienia pod sekwencyjnym identyfikatorem — HMAC plus redakcja danych do momentu rozpoznania użytkownika,
  • Polski jest jednym z siedmiu wbudowanych języków, a przykład z dokumentacji to wprost /pl/pl/products/bluza z przetłumaczonym slugiem i ceną w złotówkach,
  • Kanały wystawiaj jawnym allowlistem — niedozwolone adresy zwracają 404,
  • Przejrzyj zmienne kontroli kosztów przed wdrożeniem, zwłaszcza pipeline obrazów i dławienie zapytań przy budowaniu,
  • Sekretów płatności nie trzymaj we froncie — klucze w aplikacji Stripe w Saleorze, we storefroncie tylko flagi, klucz publikowalny dostarczany w czasie działania,
  • Fork to zobowiązanie — 887 forków przy 8 zgłoszeniach mówi, gdzie w tym modelu naprawia się błędy.

Licencja — podsumowanie: szablon jest rozpowszechniany na Functional Source License 1.1 z przyszłą licencją Apache 2.0 (FSL-1.1-ALv2). Nie jest to licencja open source zatwierdzona przez OSI, ale też nie jest to licencja restrykcyjna w codziennym użyciu: dozwolony jest każdy cel poza konkurencyjnym wykorzystaniem, a wśród celów dozwolonych licencja wprost wymienia świadczenie usług profesjonalnych klientowi korzystającemu z tego oprogramowania. Dla agencji budującej sklepy na zamówienie jest to więc zielone światło zapisane w treści dokumentu, nie kwestia interpretacji. Granica leży tam, gdzie zaczynamy sprzedawać sam szablon albo usługę, która zastępuje ofertę Saleora — i tam licencja odsyła do rozmowy z autorami. Dochodzi do tego mechanizm, który czyni FSL łagodniejszym niż wygląda: nieodwołalne przejście na Apache-2.0 w drugą rocznicę udostępnienia każdej wersji. Ograniczenie nie narasta i nie obejmuje historii — dotyczy zawsze tylko najnowszych dwóch lat pracy. Osobno warto pamiętać o klauzuli patentowej, która działa w obie strony, i o braku praw do znaków towarowych Saleora poza wskazaniem pochodzenia kodu.