diff --git a/docs/asp-terminia/02-api-ugovor-firmware.md b/docs/asp-terminia/02-api-ugovor-firmware.md index 9e02059..fc19cd9 100644 --- a/docs/asp-terminia/02-api-ugovor-firmware.md +++ b/docs/asp-terminia/02-api-ugovor-firmware.md @@ -66,10 +66,60 @@ HTTP ≠ 200 → firmware čita JSON polje `"error"` i prikazuje ga korisniku uz | # | Pitanje | Status | |---|---|---| -| P1 | Tačna formula `machine_fingerprint` u Go kodu (ulazni string, velika/mala slova, dvotačke?) | ⬜ proveriti u kodu servera | -| P2 | Da li `activate` na VEĆ aktiviran ključ + ISTI fingerprint vraća licencu ponovo (re-aktivacija posle brisanja NVS) ili `ALREADY_ACTIVATED`? Za teren nam treba: isti uređaj sme ponovo. | ⬜ proveriti/podesiti | +| P1 | Tačna formula `machine_fingerprint` u Go kodu (ulazni string, velika/mala slova, dvotačke?) | ✅ **nema je** — vidi ispod | +| P2 | Da li `activate` na VEĆ aktiviran ključ + ISTI fingerprint vraća licencu ponovo (re-aktivacija posle brisanja NVS) ili `ALREADY_ACTIVATED`? Za teren nam treba: isti uređaj sme ponovo. | ✅ **radi kako treba** — vidi ispod | | P3 | Tačan skup polja `license` objekta za ASP proizvod (limits/features imena) | ✅ odgovoreno uz Z1 (2026-07-25) — vidi ispod | -| P4 | `expires_at` format (ISO8601?) — firmware poredi prvih 10 znakova (`YYYY-MM-DD`) | ⬜ potvrditi | +| P4 | `expires_at` format (ISO8601?) — firmware poredi prvih 10 znakova (`YYYY-MM-DD`) | ✅ **RFC3339** — vidi ispod | + +### Odgovori P1, P2, P4 (2026-07-25, čitanjem Go koda) + +**P1 — formule NEMA. Server ne računa fingerprint.** +`internal/service/activation_service.go` prima `req.MachineFingerprint` i +poredi ga kao **običan string** (`existing.MachineFingerprint == req.MachineFingerprint`). +Znači: šta god firmware pošalje, to je fingerprint — samo mora biti +**dosledno isto** pri svakoj aktivaciji istog uređaja. Postojeći FW oblik +`sha256:` radi bez ijedne izmene na obe strane. +*Posledica: nema FW izmene za P1 — pitanje je bilo bespredmetno.* + +**P2 — isti uređaj SME ponovo. Radi kako teren traži.** +Ako postoji aktivna aktivacija sa **istim** fingerprintom → server osveži +`last_seen` i **ponovo vrati potpisanu licencu**. Dakle re-aktivacija posle +brisanja NVS-a / fabričkog reseta na ISTOM uređaju prolazi. +Sa **drugim** fingerprintom → `ALREADY_ACTIVATED` uz podatke gde je već +aktivirana. Ponašanje je tačno ono što nam treba, ništa se ne menja. + +**P4 — `expires_at` je RFC3339** (`2027-01-01T00:00:00Z`), prazan string kad je +licenca trajna. Počinje sa `YYYY-MM-DD`, pa poređenje prvih 10 znakova u +firmveru radi. Trajna licenca = prazno polje — **proveriti da FW to tumači +kao „bez roka", a ne kao istekla** (vidi Z2 korak 4). + +### Nalaz uz P3 — potpis je stabilan (provereno) + +Bila je opravdana sumnja: server potpiše `licenseJSON`, pa ga *ponovo* +serijalizuje u odgovor (`json.Unmarshal` → `ActivateResponse`). Da se bajtovi +razlikuju, potpis ne bi važio na uređaju. +**Ne razlikuju se:** oba puta se marshal-uje isti tip `model.LicenseData`, a Go +`encoding/json` je determinističan (polja u redosledu deklaracije, isto +escape-ovanje); `limits`/`features` su `json.RawMessage` pa prolaze doslovno +(kompaktirani isto oba puta). Potpis će se poklopiti. + +### DVA NESLAGANJA POLJA (ne blokiraju aktivaciju, kvare samo prikaz) + +Firmware (`licenca.cpp`) čita `edition` i `customer`, a server ih **ne šalje +u tom obliku**: + +| FW traži | Server šalje | Posledica | +|---|---|---| +| `edition` (string) | **nema ga** (ima `license_type`) | polje ostaje prazno u UI | +| `customer` (string) | `customer` je **objekat** `{"name","email"}` | `pure::jsonStr` traži `"` a nailazi na `{` → vrati `false`, polje prazno | +| `customer_name` (rezerva) | nema ga | rezerva ne pomaže | + +Aktivacija, potpis, fingerprint i rok rade normalno — samo „Izdanje" i +„Korisnik" u SPA ostaju prazni. Popravka je jednostavna, ali je **odluka** +gde: ili FW da čita `license_type` i `customer.name` (jedna-dve linije), ili +server da doda `edition`/`customer_name` u `LicenseData`. **Preporuka: menjati +FW**, jer server već opslužuje žive klijente (ESIR/ARV/LIGHT_TICKET) i njegov +odgovor se ne dira bez potrebe. ### Odgovor P3 (2026-07-25)