Webhook'lar
Ödemenin son durumunu tarayıcıya güvenmeden, imzalı sunucu-sunucu bildirimleriyle öğrenin.
Kurulum
Panel → Geliştirici → Webhook'lar → Uç nokta ekle. Https adresinizi, dinlenecek olayları ve modu (test/canlı) seçin. Oluşturmada bir imza sırrı gösterilir; yalnızca bir kez görüntülenir ve gerektiğinde döndürülebilir (rotate). Panelden Test olayı gönder ile uç noktanızı deneyebilir, teslimat geçmişini görüp başarısız teslimatı yeniden gönderebilirsiniz.
Güvenlik gereği webhook adresleri özel/yerel ağ IP'lerine (10.x, 192.168.x, 127.x, link-local …) çözümleniyorsa gönderim yapılmaz (SSRF koruması).
Olaylar
| type | Ne zaman |
|---|---|
payment.requires_action | 3D doğrulaması bekleniyor. |
payment.authorized | Ön provizyon alındı. |
payment.captured | Ödeme tahsil edildi (satış ya da ön provizyon kapama). |
payment.failed | Ödeme reddedildi ya da başarısız oldu. |
payment.cancelled | Ödeme iptal edildi. |
payment.refunded | Kısmi ya da tam iade yapıldı. |
webhook.test | Panelden gönderilen deneme olayı. |
İstek gövdesi ve başlıklar
{
"id": "evt_01J...",
"type": "payment.captured",
"mode": "live",
"createdAt": "2026-03-02T09:14:23Z",
"data": { "id": "pay_01J...", "object": "payment", "status": "captured", "amount": 15990, "...": "..." }
}| Başlık | İçerik |
|---|---|
X-Pgw-Event | Olay türü. |
X-Pgw-Delivery | Teslimat numarası (yeniden denemelerde aynı kalır). |
X-Pgw-Signature | t=<unix>,v1=<hex> |
İmza doğrulama
İmza: HMAC-SHA256(secret, "<t>.<ham gövde>") değerinin küçük harfli hex çıktısıdır. Gövdeyi ayrıştırmadan, gelen ham haliyle imzalayın; zaman damgasını da kontrol ederek (ör. ±5 dakika) tekrar saldırılarını engelleyin ve karşılaştırmayı sabit zamanlı yapın.
import crypto from "node:crypto";
// rawBody: Buffer/string — JSON.parse ETMEDEN, gelen ham gövde
export function verify(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected), b = Buffer.from(parts.v1 ?? "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Yanıtınız ve yeniden deneme
- İsteği 2xx ile yanıtlayın; başka her yanıt ya da 10 saniyeyi aşan zaman aşımı başarısız sayılır.
- Başarısız teslimatlar artan aralıklarla yeniden denenir: 30 sn, 2 dk, 10 dk, 30 dk, 2 sa, 6 sa, 12 sa, 24 sa. Sonuncudan sonra teslimat
failedolarak işaretlenir; panelden elle yeniden gönderilebilir. - Aynı olay birden fazla kez gelebilir. İşlemi
id(olay) ya da ödemeid+statusile idempotent yapın. - Olayların sırası garanti edilmez; durumu kesinleştirmek gerekirse ödemeyi
GET /v1/payments/{id}ile okuyun.