Olaylar
Webhook'lar
Bir hakem yanıt verdiğinde ya da bir davetin süresi dolduğunda Scholvet adresinizi çağırır; sürekli sorgulamanız gerekmez.
Kurulum
Konsoldaki dergi sayfasında POST isteği kabul eden bir HTTPS adresi girin. Konsol, her isteği imzalayan bir webhook gizli anahtarı gösterir. Adres yoksa olay gönderilmez; davetleri sorgulayarak izleyin. Alan adı yalnızca herkese açık IP adreslerine çözümlenmelidir; Scholvet bunu kaydederken ve her teslimatta yeniden denetler.
Olaylar
| type | Anlamı |
|---|---|
invitation.accepted | Hakem kabul etti. data.reviewer kimliğini taşır; hakemi sisteminizde atayın. |
invitation.declined | Hakem reddetti ya da davet beklerken hesabını sildi (declineReason other). |
invitation.expired | respondBy'a kadar yanıt gelmedi. |
Kendi yaptığınız değişiklikler, yani geri çekme ve tamamlama onayı, size geri gönderilmez.
İstek
| Başlık | Anlamı |
|---|---|
X-Scholvet-Event | Olay türü; gövdedeki type ile aynı. |
X-Scholvet-Event-Id | Olayın tekil kimliği; gövdedeki id ile aynı. Tekrar eden teslimleri ayıklamak için kullanın. |
X-Scholvet-Timestamp | İsteğin imzalandığı Unix zamanı (saniye). |
X-Scholvet-Signature | Webhook anahtarınızla, timestamp + "." + ham gövde üzerinden hesaplanan küçük harfli hex HMAC-SHA256. |
POST /scholvet/webhook HTTP/1.1
Host: journal.example.org
Content-Type: application/json; charset=utf-8
User-Agent: Scholvet/1.0
X-Scholvet-Event: invitation.accepted
X-Scholvet-Event-Id: 3f2b8d0c-71a4-4c55-9a2e-0d6f1b7c9e41
X-Scholvet-Timestamp: 1793189653
X-Scholvet-Signature: <hex hmac_sha256(secret, timestamp + "." + body)>
{
"id": "3f2b8d0c-71a4-4c55-9a2e-0d6f1b7c9e41",
"type": "invitation.accepted",
"createdAt": "2026-10-29T07:02:46Z",
"data": { "invitationId": "6d8da67a-...", "status": "accepted", "reviewer": { "...": "..." } }
}
Gövde { id, type, createdAt, data } biçimindedir; data davet nesnesidir.
İmzayı doğrulama
- Ham istek gövdesini JSON'a çevirmeden önce bayt olarak okuyun. Yeniden serileştirilmiş JSON eşleşmez.
- X-Scholvet-Timestamp saatinizden beş dakikadan fazla sapıyorsa isteği reddedin.
- Webhook anahtarınızla timestamp + "." + gövde üzerinden HMAC-SHA256 hesaplayın ve X-Scholvet-Signature ile sabit sürede karşılaştırın.
- Ancak bundan sonra gövdeyi çözümleyip işleyin.
using System.Security.Cryptography;
using System.Text;
static bool IsValidScholvetSignature(string secret, string timestamp, string rawBody, string signature)
{
if (!long.TryParse(timestamp, out var seconds) ||
Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - seconds) > 300) return false;
var expected = Convert.ToHexStringLower(HMACSHA256.HashData(
Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes(timestamp + "." + rawBody)));
return CryptographicOperations.FixedTimeEquals(
Encoding.ASCII.GetBytes(expected), Encoding.ASCII.GetBytes(signature.Trim().ToLowerInvariant()));
}
const crypto = require("crypto");
function isValidScholvetSignature(secret, timestamp, rawBody, signature) {
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = crypto.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`, "utf8")
.digest("hex");
const given = String(signature).trim().toLowerCase();
return given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(given));
}
function isValidScholvetSignature(string $secret, string $timestamp, string $rawBody, string $signature): bool
{
if (abs(time() - (int)$timestamp) > 300) {
return false;
}
$expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
return hash_equals($expected, strtolower(trim($signature)));
}
import hashlib, hmac, time
def is_valid_scholvet_signature(secret: str, timestamp: str, raw_body: bytes, signature: str) -> bool:
if abs(time.time() - int(timestamp)) > 300:
return False
expected = hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature.strip().lower())
Test vektörü
Doğrulayıcınız tam olarak bu değerleri kabul etmelidir. Gövde, gösterilen bayt dizisinin aynısıdır; sonunda satır sonu yoktur.
- secret
whsec_test_example- X-Scholvet-Timestamp
1793189653- body
{"id":"3f2b8d0c-71a4-4c55-9a2e-0d6f1b7c9e41","type":"invitation.accepted"}- X-Scholvet-Signature
01a26b6c6c8d0377814a5cf39dc828caaedffb08bb47cc6050df73bb280d2dae
Yanıt ve yeniden denemeler
- 10 saniye içinde herhangi bir 2xx durumuyla yanıt verin. İş uzun sürecekse yanıttan sonra yapın.
- Diğer her yanıt ya da zaman aşımı 1 dakika, 5 dakika, 30 dakika, 2 saat, 6 saat ve 24 saat sonra yeniden denenir. Ardından olaydan vazgeçilir. Yönlendirmeler (3xx) izlenmez ve başarısız sayılır.
- Teslim en az bir kezdir. Aynı olay iki kez gelebilir; olay kimliğini kullanarak işleminizi tekrarlanabilir yapın.
- Olaylar sırasız gelebilir. Şüphede kalırsanız davetin güncel durumunu sorgulayın.