Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.payven.com.tr/llms.txt

Use this file to discover all available pages before exploring further.

Webhook endpoint’iniz internete açıktır — tarafınıza gelen isteklerin gerçekten Payven’den geldiğini kanıtlamak için HMAC-SHA256 imzasını doğrulamak şarttır. Aksi halde sahte istekler gönderilebilir.

İmza üretimi

Payven, her webhook isteğinde imzayı şu şekilde hesaplar:
imza = HMAC_SHA256(
  secret,
  timestamp + "." + httpBody
)

X-Payven-Signature: sha256=<hex(imza)>
X-Payven-Timestamp: <unix-timestamp>
BileşenAçıklama
secretWebhook abonelik oluştururken aldığınız whsec_... değeri
timestampİsteğin gönderildiği Unix epoch saniyesi
httpBodyİstek gövdesinin tam binary içeriği (parse edilmemiş ham string)

Doğrulama adımları

1

Header'ları okuyun

X-Payven-Signature ve X-Payven-Timestamp başlıklarını alın.
2

Replay saldırılarına karşı timestamp'i kontrol edin

now - timestamp > 5 dk ise reddedin (eski isteğin tekrarı olabilir).
3

İmzayı yeniden hesaplayın

HMAC_SHA256(secret, timestamp + "." + body) hesaplayın.
4

Sabit zamanlı karşılaştırma

Hesapladığınız imzayla X-Payven-Signature değerini timing-safe karşılaştırın.
5

Geçerse işleyin

Aynı zamanda 5 saniyenin altında 2xx döndürün; ağır iş kuyruğa atılsın.

Kod örnekleri

using System.Security.Cryptography;
using System.Text;

public class PayvenWebhookValidator
{
    private const int ToleranceSeconds = 300;

    public bool Verify(string secret, string timestamp, string body, string signatureHeader)
    {
        // 1. Replay koruması
        var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
        if (Math.Abs(now - long.Parse(timestamp)) > ToleranceSeconds)
            return false;

        // 2. İmza hesapla
        var payload = $"{timestamp}.{body}";
        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
        var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
        var expected = "sha256=" + Convert.ToHexString(hash).ToLowerInvariant();

        // 3. Sabit zamanlı karşılaştırma
        return CryptographicOperations.FixedTimeEquals(
            Encoding.UTF8.GetBytes(expected),
            Encoding.UTF8.GetBytes(signatureHeader));
    }
}

[HttpPost("/webhooks/payven")]
public async Task<IActionResult> Receive()
{
    using var reader = new StreamReader(Request.Body);
    var body = await reader.ReadToEndAsync();

    var timestamp = Request.Headers["X-Payven-Timestamp"].ToString();
    var signature = Request.Headers["X-Payven-Signature"].ToString();

    if (!_validator.Verify(_secret, timestamp, body, signature))
        return Unauthorized();

    var evt = JsonSerializer.Deserialize<WebhookEvent>(body);
    await _queue.EnqueueAsync(evt); // ağır iş asenkron
    return Ok();
}

Yaygın hatalar

Body’yi mutlaka raw bytes olarak okuyun. Express’te express.json() middleware’i body’yi parse edip yeniden serialize ettiği için imza bozulur. express.raw() kullanın.ASP.NET’te otomatik model binding aynı sorunu yaratır — [FromBody] yerine Request.Body stream’i okuyun.
Sunucu saatleri arasında küçük sapmalar (NTP gecikmesi) ve ağ gecikmesi olabilir. 5 dakika makul bir tolerans. Daha sıkı (örn. 1 dk) yapabilirsiniz; daha gevşek (örn. 1 saat) yapmayın — replay riski artar.
Normal == karşılaştırması karakter karakter yapılır ve eşleşmeyen ilk karakterde durur. Saldırgan, doğru karakteri bulduğunda yanıt süresinin biraz uzadığını ölçerek imzayı brute-force keşfedebilir. Timing-safe karşılaştırma her zaman aynı sürede çalışır.
Her aboneliğin kendi secret’ı vardır. Hangi abonelikten geldiğini ayırt etmek için X-Payven-Webhook-Id başlığına bakın ve eşleşen secret’ı kullanın.

Secret rotasyonu

Sızıntı şüphesinde:
curl -X POST https://vpos.payven.com.tr/api/v1/webhooks/wh_8e3f5c12/rotate-secret \
  -H "X-API-Key: $KEY" -H "X-API-Secret: $SECRET" -H "X-Merchant-Id: $MERCHANT"
Yeni secret döner. Eski secret anında geçersiz olur — sıfır kesintiyle rotasyon için iki ayrı abonelik kullanın.