Kontrakt witryny
storefront-contract v3: backend sklepu to pakiet @delieta/shop-runtime, dwie cienkie nakładki, klucz publikowalny po stronie serwera, podpisane odświeżanie.
Kontrakt witryny (storefront-contract@v3) opisuje sklep na Delieta. Backend sklepu to npm-owy pakiet @delieta/shop-runtime (wersja 1.6.0), a nie pliki w repozytorium sklepu. Repozytorium sklepu trzyma wygląd i dwie cienkie nakładki. Reszta (dostęp do Partner API, koszyk, checkout, odświeżanie) przychodzi z pakietu, więc poprawka runtime trafia do wszystkich sklepów jedną publikacją.
Cztery wartości env
DELIETA_API_URL=https://delieta.pl
DELIETA_PUBLISHABLE_KEY=dlt_live_…
DELIETA_REVALIDATE_SECRET=…
DELIETA_DATA_MODE=live
DELIETA_API_URL to sam origin: bez /api/v1, bez końcowego slasha. DELIETA_PUBLISHABLE_KEY to klucz catalog:read + checkout:create. DELIETA_REVALIDATE_SECRET podpisuje sygnał odświeżenia i weryfikuje linki podglądu na sklepie z danymi przykładowymi. DELIETA_DATA_MODE jest opcjonalny: sample albo live, brak znaczy live, a build odrzuca każdą inną wartość. Żadna z nich nie ma prefiksu NEXT_PUBLIC_.
Brak env → strona „skonfiguruj sklep”, nie pusta witryna udająca sklep. Env jest, a Delieta nie odpowiada → strona „produkty niedostępne”, nie menu z zerem pozycji, które wygląda jak wdrożenie.
Klucz publikowalny: tylko po stronie serwera
Klucz publikowalny to dokładnie catalog:read + checkout:create. Może tworzyć koszyk, więc nie wolno go wypuścić do przeglądarki. Runtime trzyma go po stronie serwera i wysyła sam. delieta-shop-assert po buildzie przerywa, gdy klucz wyciekł do kodu JS dla klienta. Klucz sekretny (orders:write, eta:quote, reservations:write, catering:write) nie trafia ani do sklepu, ani do jego repozytorium. dlt_test_… też nie jest bezpiecznym sekretem w HTML.
Klucz wskazuje firmę. Nie wysyłaj companyId.
Trzy powierzchnie
| Obszar | Skąd | Uwagi |
|---|---|---|
| Produkty i marka | GET /api/v1/menu i GET /api/v1/shop-config kluczem publikowalnym, czytane bez cache. | Grosze, PLN. Pozycje niedostępne pominięte. Marka, godziny, moduły i zasady zamówień idą z panelu. |
| Koszyk | { itemId, quantity, options? }[]. Suma na ekranie z listy produktów. | Opcje wybiera się przez options: [{ group, option }]. Klient wysyła co, nigdy za ile. |
| Kasa | POST /api/v1/checkout przez shim /api/delieta/checkout, wynik z GET /api/v1/checkout/result. | Delieta wycenia z katalogu; wraca adres płatności albo, przy gotówce, trackingUrl. |
flowchart TD env["Env: origin, klucz publikowalny, sekret odswiezania"] --> rt["@delieta/shop-runtime"] rt --> menu["GET /menu i /shop-config bez cache"] menu --> cart["Koszyk: itemId, ilosc, opcje"] cart --> pipe["/api/delieta/checkout"] pipe --> checkout["POST /checkout, ceny liczy Delieta"] checkout --> result["GET /checkout/result i status"] secret["Klucz sekretny"] -.->|"nigdy w sklepie"| rt push["Delieta: podpisany POST /api/revalidate"] --> rt
Klucz jest „publikowalny” tylko w tym sensie, że nie może czytać zamówień, kierowców ani klientów. Mimo to zostaje na serwerze.
Dwie nakładki w repozytorium sklepu
Sklep ma dokładnie dwa pliki od nas. Każdy to jeden export; nie edytujesz ich, a delieta-shop-assert kończy się błędem, gdy ktoś to zrobi.
// app/api/delieta/[...path]/route.ts
export { GET, POST } from "@delieta/shop-runtime/proxy";
// app/api/revalidate/route.ts
export { POST } from "@delieta/shop-runtime/revalidate";
Pierwszy to pośrednik /api/delieta/… z listą dozwolonych ścieżek: GET plan sali, dostępność rezerwacji, terminy checkoutu i status zamówienia po krótkim kodzie, POST checkout, rezerwacje i zapytania. Wszystko inne jest odrzucane. Przeglądarka gościa wywołuje pośrednika, a on dokłada klucz po stronie serwera. Drugi przyjmuje podpisane odświeżenie.
Odświeżanie, heartbeat i guard
Dane Delieta runtime czyta bez cache. Gdy właściciel zmieni menu, ustawienia albo plan sali, Delieta wysyła podpisany POST /api/revalidate, a sklep unieważnia swoje tagi (menu, shop-config, floor-plan). Po next build delieta-shop-assert sprawdza trzy rzeczy: nakładki są nietknięte, aplikacja nie jest statycznym eksportem, klucz publikowalny nie wyciekł do kodu klienta. Wysyła też heartbeat z wersją runtime. Aplikacja będąca statycznym eksportem nie ma serwera, więc nie spełnia kontraktu v3.
Pieniądze i błędy
Kwoty są w całkowitych groszach; formatuj je dopiero przy wyświetlaniu. Błędy Partner API to application/problem+json; rozpoznawaj je po code, na przykład unauthorized, scope_required, rate_limited, shop_closed, slot_unavailable. Pełna lista kodów i wszystkie operacje, których sklep używa, są w Partner API.

