PBS · Partner

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"
  }
}

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
}
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