Przejdź do głównej zawartości

Zdarzenia i webhooki

const ws = new WebSocket(`wss://api.przymierz.online/v1/events?token=${token}`);
ws.onmessage = (e) => {
const event = JSON.parse(e.data);
// {"type": "tryon.succeeded" | "tryon.failed" | "tryon.refused", "tryon_id": "...", "status": "...", "error_code": null}
if (event.tryon_id === pendingId) showResult(); // GET /v1/tryons/{id} da result_url
};

Token przekazujesz w adresie, bo przeglądarki nie wysyłają nagłówka Authorization przy WebSocket. Gdy połączenie nie działa, odpytuj GET /v1/tryons/{id} co 2 sekundy.

Adres ustawiasz w panelu (Deweloper → Webhook). Wysyłamy POST z JSON.

type Kiedy
tryon.succeeded Przymiarka gotowa
tryon.failed Nie udała się, przyczyna w error_code (lista)
tryon.refused Model odmówił wykonania
tryon.rated Klientka oceniła wynik kciukiem
ping Test z panelu (Wyślij test)

Koniec przymiarki:

{
"type": "tryon.succeeded",
"tryon_id": "6d572ac0-...",
"status": "succeeded",
"error_code": null,
"external_user_id": "klientka-42"
}

Gdy klientka oceni wynik kciukiem, wysyłamy tryon.rated:

{
"type": "tryon.rated",
"tryon_id": "6d572ac0-...",
"rating": "down",
"reasons": ["length"],
"external_user_id": "klientka-42"
}

Powody: length, color, body_changed, not_me, other. Ponowna ocena tej samej przymiarki wysyła zdarzenie jeszcze raz.

Twój serwer powinien odpowiedzieć kodem 2xx w ciągu 10 sekund. Każdą próbę widać w panelu, a nieudaną możesz wysłać ponownie. Zdarzenie ping ma tylko pola type i sent_at.

Nagłówek X-Przymierz-Signature: t=<znacznik czasu>,v1=<podpis>. Podpis to HMAC-SHA256 z sekretem webhooka liczony z "{t}.{treść}". Odrzucaj zdarzenia starsze niż 5 minut.

import hashlib, hmac, time
def verify(secret: str, header: str, body: bytes) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
import crypto from "node:crypto";
export function verify(secret, header, rawBody) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.`).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

Liczy się surowa treść zapytania (bajty), nie JSON po ponownym zapisie.