Skip to Content
GuidesWebhooks

Webhook’lar

Solidus entegrasyonunuzda olaylar gerçekleştiğinde gerçek zamanlı bildirimler alın. Webhook’lar, bir doğrulama tamamlandığında, bir kimlik bilgisi düzenlendiğinde veya başka olaylar meydana geldiğinde uç noktanıza HTTP POST istekleri teslim eder.

Temel URL

https://verify.solidus.network/v1

API anahtarınızla istekleri kimlik doğrulayın:

Authorization: Bearer YOUR_API_KEY

1. Bir Webhook Uç Noktası Oluşturun

Webhook olaylarını almak için bir URL kaydedin.

const response = await fetch('https://verify.solidus.network/v1/webhooks', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_API_KEY', }, body: JSON.stringify({ url: 'https://myapp.com/webhooks/solidus', events: [ 'verification.completed', 'verification.failed', 'verification.expired', 'credential.issued', 'credential.revoked', ], description: 'Production webhook for KYC events', }), }) const webhook = await response.json()

Yanıt:

{ "id": "wh_3nK8mR2pQ5", "url": "https://myapp.com/webhooks/solidus", "events": [ "verification.completed", "verification.failed", "verification.expired", "credential.issued", "credential.revoked" ], "description": "Production webhook for KYC events", "secret": "whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6", "status": "active", "createdAt": "2026-05-07T12:00:00Z" }

secret değerini kaydedin. Webhook imzalarını doğrulamak için onu kullanacaksınız. Sır, yalnızca oluşturma sırasında bir kez döndürülür.

2. Desteklenen Olaylar

EventTrigger
verification.completedKYC doğrulaması geçti, kimlik bilgisi düzenlendi
verification.failedKYC doğrulaması başarısız oldu (belge sorunları, yüz uyuşmazlığı)
verification.expiredDoğrulama oturumu tamamlanmadan önce sona erdi
credential.issuedYeni bir Doğrulanabilir Kimlik Bilgisi düzenlendi
credential.revokedMevcut bir kimlik bilgisi iptal edildi

3. Webhook Yük (Payload) Biçimi

Her webhook teslimatı, aşağıdaki yapıya sahip bir JSON yükü gönderir:

{ "id": "evt_8nR3kL5mQ2xY", "type": "verification.completed", "createdAt": "2026-05-07T12:05:30Z", "data": { "verificationId": "ver_2xK9mP4qR7nL", "status": "completed", "outcome": "pass", "subjectDid": "did:solidus:testnet:7Hk3mRtQZv...", "level": 1, "credentialId": "urn:uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890" } }

Olay Türüne Göre Yük

verification.completed

{ "id": "evt_8nR3kL5mQ2xY", "type": "verification.completed", "createdAt": "2026-05-07T12:05:30Z", "data": { "verificationId": "ver_2xK9mP4qR7nL", "status": "completed", "outcome": "pass", "subjectDid": "did:solidus:testnet:7Hk3mRtQZv...", "level": 1, "credentialId": "urn:uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890" } }

verification.failed

{ "id": "evt_9pQ4rM6sT3wZ", "type": "verification.failed", "createdAt": "2026-05-07T12:05:30Z", "data": { "verificationId": "ver_2xK9mP4qR7nL", "status": "failed", "outcome": "fail", "subjectDid": "did:solidus:testnet:7Hk3mRtQZv...", "reason": "face_mismatch", "message": "The selfie does not match the photo on the identity document." } }

credential.issued

{ "id": "evt_5kN2jH8bF7mR", "type": "credential.issued", "createdAt": "2026-05-07T12:05:30Z", "data": { "credentialId": "urn:uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890", "type": ["VerifiableCredential", "KYCCredential"], "issuer": "did:solidus:testnet:verify-service", "subjectDid": "did:solidus:testnet:7Hk3mRtQZv...", "issuanceDate": "2026-05-07T12:05:30Z", "expirationDate": "2027-05-07T12:05:30Z", "txHash": "0x7a3b9c2d1e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b" } }

credential.revoked

{ "id": "evt_4mL9gK3cE6nP", "type": "credential.revoked", "createdAt": "2026-05-07T14:20:00Z", "data": { "credentialId": "urn:uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890", "subjectDid": "did:solidus:testnet:7Hk3mRtQZv...", "revokedBy": "did:solidus:testnet:verify-service", "reason": "Information no longer accurate", "txHash": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b" } }

4. İmza Doğrulama

Her webhook isteği, X-Solidus-Signature başlığında bir imza içerir. İsteğin Solidus’tan geldiğini doğrulamak için her zaman bu imzayı kontrol edin.

İmza, webhook sırrınızı anahtar olarak kullanan, ham istek gövdesinin bir HMAC-SHA256 hex özetidir.

import crypto from 'crypto' function verifyWebhookSignature( payload: string, signature: string, secret: string ): boolean { const expected = crypto .createHmac('sha256', secret) .update(payload) .digest('hex') // Use timing-safe comparison to prevent timing attacks return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ) }

İstek ayrıca bir Unix zaman damgasıyla bir X-Solidus-Timestamp başlığı içerir. Yeniden oynatma saldırılarını önlemek için zaman damgası 5 dakikadan daha eski olan istekleri reddedin.

5. Tam Express Webhook İşleyicisi

Solidus webhook’larını alan, doğrulayan ve işleyen üretime hazır bir Express işleyicisi.

import express from 'express' import crypto from 'crypto' const app = express() const WEBHOOK_SECRET = process.env.SOLIDUS_WEBHOOK_SECRET! // Use raw body for signature verification app.post( '/webhooks/solidus', express.raw({ type: 'application/json' }), async (req, res) => { const signature = req.headers['x-solidus-signature'] as string const timestamp = req.headers['x-solidus-timestamp'] as string const rawBody = req.body.toString() // 1. Verify the timestamp is recent (within 5 minutes) const eventTime = parseInt(timestamp, 10) * 1000 const now = Date.now() if (Math.abs(now - eventTime) > 5 * 60 * 1000) { res.status(400).json({ error: 'Timestamp too old' }) return } // 2. Verify the signature const expected = crypto .createHmac('sha256', WEBHOOK_SECRET) .update(rawBody) .digest('hex') const isValid = crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ) if (!isValid) { res.status(401).json({ error: 'Invalid signature' }) return } // 3. Parse the event const event = JSON.parse(rawBody) // 4. Respond immediately with 200 to acknowledge receipt res.status(200).json({ received: true }) // 5. Process the event asynchronously try { switch (event.type) { case 'verification.completed': await handleVerificationCompleted(event.data) break case 'verification.failed': await handleVerificationFailed(event.data) break case 'verification.expired': await handleVerificationExpired(event.data) break case 'credential.issued': await handleCredentialIssued(event.data) break case 'credential.revoked': await handleCredentialRevoked(event.data) break default: console.log('Unknown event type:', event.type) } } catch (err) { console.error('Error processing webhook:', err) } } ) async function handleVerificationCompleted(data: any) { // Update user record in your database await db.users.update({ where: { did: data.subjectDid }, data: { kycStatus: 'verified', kycLevel: data.level, credentialId: data.credentialId, verifiedAt: new Date(), }, }) } async function handleVerificationFailed(data: any) { await db.users.update({ where: { did: data.subjectDid }, data: { kycStatus: 'failed', kycFailureReason: data.reason, }, }) } async function handleVerificationExpired(data: any) { await db.users.update({ where: { did: data.subjectDid }, data: { kycStatus: 'expired' }, }) } async function handleCredentialIssued(data: any) { await db.credentials.create({ data: { credentialId: data.credentialId, subjectDid: data.subjectDid, type: data.type, issuer: data.issuer, issuedAt: new Date(data.issuanceDate), expiresAt: new Date(data.expirationDate), txHash: data.txHash, }, }) } async function handleCredentialRevoked(data: any) { await db.credentials.update({ where: { credentialId: data.credentialId }, data: { revokedAt: new Date(), revokedBy: data.revokedBy, revocationReason: data.reason, }, }) } app.listen(3001)

6. Teslimatı İşleme

Webhook teslimatlarını güvenilir şekilde işlemek için şu uygulamaları izleyin:

Hızlı yanıt verin. Mümkün olduğunca hızlı bir 200 durum kodu döndürün. Yanıt verdikten sonra olayı eşzamansız olarak işleyin. Uç noktanız çok uzun sürerse (30 saniyeden fazla), teslimat başarısız olarak işaretlenir.

Yinelenenleri işleyin. Webhook teslimatları yeniden denenebilir, bu yüzden aynı olayı birden fazla kez alabilirsiniz. Olayları tekilleştirmek için id alanını kullanın.

// Deduplicate using the event ID const alreadyProcessed = await db.processedEvents.findUnique({ where: { eventId: event.id }, }) if (alreadyProcessed) { res.status(200).json({ received: true, duplicate: true }) return } // Mark as processed before handling await db.processedEvents.create({ data: { eventId: event.id, processedAt: new Date() }, })

HTTPS kullanın. Webhook uç noktaları üretimde HTTPS kullanmalıdır. HTTP uç noktalarına yalnızca geliştirme sırasında localhost için izin verilir.

7. Yeniden Deneme Politikası

Uç noktanız 2xx olmayan bir durum kodu döndürürse veya 30 saniye içinde yanıt vermezse, Solidus teslimatı üstel geri çekilme (exponential backoff) ile yeniden dener:

AttemptDelay after failure
1. yeniden deneme1 dakika
2. yeniden deneme5 dakika
3. yeniden deneme30 dakika
4. yeniden deneme2 saat
5. yeniden deneme12 saat

5 başarısız yeniden denemeden sonra, teslimat kalıcı olarak başarısız işaretlenir. Kontrol panelinde veya API aracılığıyla başarısız teslimatları görüntüleyebilirsiniz.

8. Teslimat Günlükleri

Bir webhook uç noktası için teslimat geçmişini inceleyin.

const response = await fetch( 'https://verify.solidus.network/v1/webhooks/wh_3nK8mR2pQ5/deliveries', { headers: { 'Authorization': 'Bearer YOUR_API_KEY' }, } ) const deliveries = await response.json()

Yanıt:

{ "data": [ { "id": "del_7rT2nK9mP4", "eventId": "evt_8nR3kL5mQ2xY", "eventType": "verification.completed", "status": "delivered", "statusCode": 200, "attemptNumber": 1, "requestTimestamp": "2026-05-07T12:05:31Z", "responseTimestamp": "2026-05-07T12:05:31Z", "duration": 145 }, { "id": "del_4mL9gK3cE6", "eventId": "evt_9pQ4rM6sT3wZ", "eventType": "verification.failed", "status": "failed", "statusCode": 500, "attemptNumber": 1, "nextRetryAt": "2026-05-07T12:11:30Z", "requestTimestamp": "2026-05-07T12:05:30Z", "responseTimestamp": "2026-05-07T12:05:32Z", "duration": 2045 } ], "pagination": { "total": 2, "page": 1, "perPage": 25 } }

9. Webhook’ları Test Etme

Doğru yapılandırıldığını doğrulamak için uç noktanıza bir test olayı gönderin.

const response = await fetch( 'https://verify.solidus.network/v1/webhooks/test', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_API_KEY', }, body: JSON.stringify({ webhookId: 'wh_3nK8mR2pQ5', eventType: 'verification.completed', }), } ) const result = await response.json()

Yanıt:

{ "id": "del_test_9kM2nR5pQ3", "status": "delivered", "statusCode": 200, "duration": 230, "request": { "url": "https://myapp.com/webhooks/solidus", "headers": { "Content-Type": "application/json", "X-Solidus-Signature": "a1b2c3d4...", "X-Solidus-Timestamp": "1746619530" } } }

Test olaylarının, gerçek olaylardan ayırt edebilmeniz için evt_test_ önekli bir id’si vardır.

10. Webhook’ları Yönetme

Webhook’ları Listele

curl -H "Authorization: Bearer YOUR_API_KEY" \ https://verify.solidus.network/v1/webhooks

Bir Webhook’u Güncelle

curl -X PATCH https://verify.solidus.network/v1/webhooks/wh_3nK8mR2pQ5 \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "events": ["verification.completed", "credential.issued"], "status": "active" }'

Bir Webhook’u Sil

curl -X DELETE https://verify.solidus.network/v1/webhooks/wh_3nK8mR2pQ5 \ -H "Authorization: Bearer YOUR_API_KEY"

Sırrı Döndür (Rotate)

Webhook sırrınız ele geçirilirse, onu döndürün. Eski sır anında geçersiz hale gelir.

curl -X POST \ https://verify.solidus.network/v1/webhooks/wh_3nK8mR2pQ5/rotate-secret \ -H "Authorization: Bearer YOUR_API_KEY"

Güvenlik Kontrol Listesi

  • Olayları işlemeden önce her zaman X-Solidus-Signature başlığını doğrulayın
  • Zaman damgaları 5 dakikadan daha eski olan istekleri reddedin
  • Üretimde HTTPS uç noktaları kullanın
  • Webhook sırrını güvenli bir şekilde saklayın (ortam değişkeni, sır yöneticisi)
  • Olay id alanını kullanarak olayları tekilleştirin
  • 200 döndürdükten sonra olayları eşzamansız olarak işleyin
  • Kalıcı başarısızlıklar için teslimat günlüklerini izleyin

Sonraki Adımlar

Last updated on