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

ObszarSkądUwagi
Produkty i markaGET /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.
KasaPOST /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.