☰ Meniu · Webhook-uri
Webhook-uri
Primește un POST HTTPS când se întâmplă ceva: un bon aplicat oriunde, o recompensă deblocată, o comandă plasată la masă.
Evenimentele acoperă tot ce se face prin API la acest local, de integrarea ta și de oricare alta (altă casă, un chioșc, un conector de programări). Abonează-te doar la evenimentele pe care le folosești.
Adaugă un endpoint
Din Dezvoltatori → Webhook-uri sau prin API. URL-ul trebuie să fie HTTPS public. Secretul de semnare este întors o singură dată, la crearea endpointului.
curl https://loyaltyfy.io/api/v1/webhook_endpoints \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://pos.example.com/loyaltyfy/webhooks",
"events": ["transaction.completed", "order.created"]
}'{
"object": "webhook_endpoint",
"id": "01c2a784-4bcf-43b9-8ac9-ddbad7a25341",
"url": "https://pos.example.com/loyaltyfy/webhooks",
"description": null,
"events": ["transaction.completed", "order.created"],
"status": "enabled",
"disabled_reason": null,
"livemode": false,
"created_at": "2026-10-06T18:17:32.381+00:00",
"secret": "whsec_UvjHvXfPk80ITYl_xPeAtskI0WjR8-PS"
}Ce primești
POST /loyaltyfy/webhooks HTTP/1.1
Content-Type: application/json
User-Agent: Loyaltyfy-Webhooks/1.0
Loyaltyfy-Signature: t=1791310488,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd{
"id": "evt_5a1f0c9e2b7d4e3f81a0c6d2",
"object": "event",
"type": "order.created",
"created": 1791310488,
"livemode": true,
"data": {
"object": {
"object": "order",
"id": "0c69a81c-0fbc-4333-b26c-789566b9bea4",
"number": 10,
"status": "new",
"table": "7",
...
}
}
}data.object este același obiect pe care îl întoarce API-ul (o tranzacție, un membru, o comandă), așa cum era în momentul evenimentului. Răspunde cu orice 2xx în 10 secunde. Fă procesarea lentă după ce ai răspuns.
Verifică semnătura
Loyaltyfy-Signature conține un timestamp t și o semnătură v1: HMAC-SHA256 calculat peste t, un punct și corpul brut al cererii, cu secretul endpointului tău drept cheie, în hex. Calculeaz-o peste exact octeții primiți, înainte de orice parsare JSON, compară în timp constant și respinge timestamp-urile mai vechi de cinci minute.
import crypto from "node:crypto";
// rawBody: the request body exactly as received (a string or Buffer, not parsed JSON)
export function verifyLoyaltyfy(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 ?? "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}import hmac, hashlib, time
def verify_loyaltyfy(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t = int(parts.get("t", "0"))
if abs(time.time() - t) > tolerance:
return False
signed = f"{t}.".encode() + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))Reîncercări
Dacă endpointul tău nu răspunde cu 2xx la timp, reîncercăm după 1 minut, 5 minute, 30 de minute, 2 ore, 6 ore, 12 ore și 24 de ore. După 20 de livrări eșuate la rând, endpointul este dezactivat, iar localul vede motivul în panou; îl reactivezi cu PATCH /v1/webhook_endpoints/{id} și enabled: true.
Din cauza reîncercărilor, un eveniment poate sosi de mai multe ori, iar evenimentele pot sosi în altă ordine. Folosește id-ul evenimentului ca să sari peste duplicate și timestamp-urile obiectului ca să decizi ce este mai nou.
Testează un endpoint
curl -X POST https://loyaltyfy.io/api/v1/webhook_endpoints/01c2a784-4bcf-43b9-8ac9-ddbad7a25341/test \
-H "Authorization: Bearer sk_test_..."
# { "object": "webhook_test", "delivered": true, "status_code": 200, "error": null }Lista completă de evenimente este în referința Evenimente.