İmzaları doğrulama
Her teslimat bir Ciqra-Signature header’ı taşır. İmzası doğrulanmayan her isteği reddedin —
endpoint’iniz herkese açıktır ve ona herkes POST atabilir.
Header
Bölüm başlığı “Header”Ciqra-Signature: t=1758535200,v1=530e6f8f…4596| Parça | Anlamı |
|---|---|
t |
CIQRA’nın teslimatı imzaladığı Unix zamanı (saniye). |
v1 |
HMAC-SHA256(key = signing secret, message = t + "." + raw body) değerinin küçük harfli hex gösterimi. |
Anahtar, kurulumunuzun imzalama secret’ının (ciqra_ss_…) UTF-8 baytlarıdır — önek dahil dizenin tamamı.
Mesaj ise zaman damgası, düz bir . ve istek gövdesinin alındığı haliyle ham (raw) baytlarıdır.
Ayrıştırmadan önce doğrulayın: ayrıştırılmış JSON’u yeniden serileştirmek baytları değiştirir ve imza tutmaz.
Adımlar
Bölüm başlığı “Adımlar”- Header’ı
,ile, her parçayı da ilk=ile bölün;tvev1’i alın. tsaatinizden 5 dakikadan fazla uzaksa reddedin — bu, yakalanmış teslimatların yeniden oynatılmasını (replay) engeller.hex(HMAC-SHA256(secret, t + "." + rawBody))değerini hesaplayın.v1ile sabit zamanlı (constant time) karşılaştırın.
import { createHmac, timingSafeEqual } from "node:crypto";
// Verify a CIQRA webhook. rawBody: the request body exactly as received (string), before any JSON parsing.export function verifyCiqra(rawBody, header, secret, toleranceSec = 300, nowSec = Date.now() / 1000) { const parts = {}; for (const part of String(header).split(",")) { const eq = part.indexOf("="); if (eq > 0) parts[part.slice(0, eq).trim()] = part.slice(eq + 1).trim(); }
const t = Number(parts.t); if (!Number.isInteger(t) || Math.abs(nowSec - t) > toleranceSec) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`, "utf8").digest("hex"); const given = parts.v1 ?? ""; return given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected));}import hashlibimport hmacimport time
def verify_ciqra(raw_body: bytes, header: str, secret: str, tolerance: int = 300, now: float | None = None) -> bool: """Verify a CIQRA webhook. raw_body: the request body bytes exactly as received.""" parts = {} for part in header.split(","): key, sep, value = part.partition("=") if sep: parts[key.strip()] = value.strip() try: t = int(parts["t"]) except (KeyError, ValueError): return False if abs((time.time() if now is None else now) - t) > tolerance: return False expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, parts.get("v1", ""))using System.Security.Cryptography;using System.Text;
static bool VerifyCiqra(string rawBody, string header, string secret, TimeSpan tolerance){ string? t = null, v1 = null; foreach (var part in header.Split(',', StringSplitOptions.TrimEntries)) { var eq = part.IndexOf('='); if (eq <= 0) continue; if (part[..eq] == "t") t = part[(eq + 1)..]; else if (part[..eq] == "v1") v1 = part[(eq + 1)..]; } if (!long.TryParse(t, out var unix) || v1 is null) return false;
var signedAt = DateTimeOffset.FromUnixTimeSeconds(unix); if ((DateTimeOffset.UtcNow - signedAt).Duration() > tolerance) return false;
var mac = HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes($"{unix}.{rawBody}")); return CryptographicOperations.FixedTimeEquals( Encoding.ASCII.GetBytes(Convert.ToHexStringLower(mac)), Encoding.ASCII.GetBytes(v1));}Yukarıdaki Node.js ve Python kodu, bu sitenin test takımının aşağıdaki test vektörüne karşı çalıştırdığı dosyanın ta kendisidir.
Test vektörü
Bölüm başlığı “Test vektörü”Gerçek bir mağazaya bağlanmadan önce uygulamanızı bununla kontrol edin.
| Girdi | Değer |
|---|---|
| secret | ciqra_ss_test |
| ham gövde | {"orderId":"0f8e","status":"Paid"} |
t |
1758535200 |
| imzalanan mesaj | 1758535200.{"orderId":"0f8e","status":"Paid"} |
| header | t=1758535200,v1=530e6f8fc05a8e47dd1befcbdf45fa89b16b4491362f5e7c0427b44965bb4596 |
Bu değer platformun kendi içindeki bir testle sabitlenmiştir; dolayısıyla CIQRA’nın gerçekte gönderdiğinden sapamaz. Test ederken sabit bir “şimdi” değeri vermeyi unutmayın, yoksa 5 dakikalık pencere 2025 tarihli zaman damgasını reddeder.
Diğer teslimat header’ları
Bölüm başlığı “Diğer teslimat header’ları”| Header | Değer |
|---|---|
Ciqra-Event |
Olay türü, ör. order.paid. |
Ciqra-Event-Id |
Olayın kararlı id’si — tekilleştirme için bunu kullanın. |
Ciqra-Delivery |
Bu teslimat deneme serisinin id’si. |
Ciqra-Webhook-Id |
Abonelik id’si. |
Ciqra-Event-Age-Seconds |
Olayın gerçekleşmesinden bu yana geçen saniye; yeniden denemelerden sonra büyür. |
User-Agent |
CIQRA-Webhooks/1.0 |
Bu header’lar imzalı değildir. Yönlendirme için kullanın; veri için yalnızca imzalı gövdeye güvenin.