Personel devam webhook entegrasyonu

Başarılı giriş-çıkış kayıtları için HTTPS webhook, HMAC imza doğrulama ve yeniden deneme kuralları.

1. Yapılandırma (`metadata.pbs`)

Webhook yalnızca tenant bazında etkinleştirilir. Ankara Yazılım operasyon ekibi saas.tenants.metadata.pbs alanına URL ve gizli anahtarı yazar. Self-servis panelden düzenleme yoktur; talep için Kurumsal Panel → Firma → Entegrasyonlar sayfasındaki süreci izleyin.

  • webhookUrl (veya legacy pbs_webhook_url) — HTTPS POST uç noktanız
  • webhookSecret (veya pbs_webhook_secret) — HMAC-SHA256 paylaşımlı sır
{
  "pbs": {
    "webhookUrl": "https://partner.example.com/hooks/pbs",
    "webhookSecret": "generate-a-long-random-secret"
  }
}

Örnek değer: webhookUrl ve webhookSecret kendi değerlerinizle değiştirilir.

2. Olay ve örnek gövde

Her başarılı punch (mobil QR, personel QR, kiosk) sonrası sunucu asenkron POST gönderir. Punch API yanıtı webhook teslimini beklemez.

Olay adı: pbs.attendance.recorded

{
  "event": "pbs.attendance.recorded",
  "tenantId": "demo-firma",
  "userId": "usr_abc123",
  "siteId": "550e8400-e29b-41d4-a716-446655440000",
  "eventType": "in",
  "eventId": "660e8400-e29b-41d4-a716-446655440001",
  "recordedAt": "2026-06-11T10:00:00.000Z",
  "distanceM": 8.1
}

Örnek değer: tenantId, userId, siteId, eventId, recordedAt ve distanceM alanlarındaki değerler gerçek bir kaydı göstermez.

AlanAçıklama
eventSabit: pbs.attendance.recorded
tenantIdFirma kodu (X-Project-ID ile aynı tenant anahtarı)
userIdPersonel kullanıcı UUID
siteIdİşyeri UUID
eventTypein veya out
eventIdDevam kaydı UUID (idempotent anahtar olarak kullanın)
recordedAtISO 8601 UTC zaman damgası
distanceMGeofence mesafesi (metre) veya null

3. `x-pbs-signature` doğrulama

webhookSecret tanımlıysa istek başlığında imza gelir. Gövdeyi ham bayt/string olarak HMAC-SHA256 ile hashleyin; JSON parse edip tekrar stringify etmeyin (imza bozulur).

  • Header: x-pbs-signature: sha256=<hex>
  • Content-Type: application/json
  • User-Agent: ankarayazilim-pbs-webhook/1
import { createHmac, timingSafeEqual } from 'node:crypto';

/** Raw POST body string — do not re-serialize parsed JSON. */
export function verifyPbsWebhookSignature(rawBody, secret, signatureHeader) {
  if (!secret || !signatureHeader) return false;
  const expected = createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex');
  const received = String(signatureHeader).replace(/^sha256=/i, '').trim();
  if (expected.length !== received.length) return false;
  return timingSafeEqual(Buffer.from(expected, 'utf8'), Buffer.from(received, 'utf8'));
}

// Express example:
// app.post('/hooks/pbs', express.raw({ type: 'application/json' }), (req, res) => {
//   const ok = verifyPbsWebhookSignature(
//     req.body.toString('utf8'),
//     process.env.PBS_WEBHOOK_SECRET,
//     req.headers['x-pbs-signature'],
//   );
//   if (!ok) return res.status(401).send('invalid signature');
//   const payload = JSON.parse(req.body.toString('utf8'));
//   if (payload.event !== 'pbs.attendance.recorded') return res.status(400).end();
//   res.status(200).json({ ok: true });
// });

Başarılı işleme için 2xx HTTP yanıtı dönün. İmza hatasında 401, bilinmeyen olay için 400 uygundur.

4. Yeniden deneme ve dead-letter

Geçici hatalarda Ankara Yazılım sunucusu otomatik yeniden dener. Kalıcı 4xx yanıtlarında (429 hariç) tekrar deneme yapılmaz.

ParametreDeğer
Deneme sayısı4 (ilk gönderim + 3 retry)
İstek zaman aşımı8 saniye
Backoff500 ms → 1 s → 2 s → 4 s (üst sınır 8 s)
Yeniden denenen HTTP429, 5xx, ağ/timeout hataları
Yeniden denenmeyen HTTP401, 403, 404, diğer 4xx
TükeninceAudit: webhook.dead_letter — PBS panelinde kırmızı uyarı

Partner uç noktanız idempotent olmalıdır: aynı eventId tekrar gelirse güvenle yok sayın veya upsert yapın.

Teknik sözleşme: CONTRACTS/pbs-attendance.md