Webhook'lar: Genel Bakış
Loyetta, giden webhook'ları kendi uç noktalarınıza iletebilir; böylece yerleşik bir sağlayıcı kullanmak yerine müşteri iletişimini kendiniz yönetebilirsiniz. Bunu, SMS veya push bildirimlerini kendi altyapınız üzerinden göndermek istediğinizde kullanın — örneğin ek dahili kontroller yapmak, kendi gönderim hızı/onay kurallarınızı uygulamak ya da hâlihazırda anlaşmalı olduğunuz operatör ve sağlayıcılar üzerinden yönlendirmek için.
Loyetta sadakat mantığını yürütür (kime, ne gönderileceği); gönderimi siz yaparsınız. Şu anda iki kanal için kullanılabilir:
- SMS Gönderimi — Loyetta size hedef kullanıcı kimliklerini ve mesaj metnini gönderir; telefon numaralarını kendi tarafınızda çözümleyip SMS'i siz iletirsiniz.
- Push Gönderimi — Loyetta size hedef kullanıcı kimliklerini ve bildirim içeriğini gönderir; push'u kendi altyapınız üzerinden iletirsiniz.
Her webhook, gerçekten Loyetta'dan geldiğini doğrulayabilmeniz için paylaşılan bir gizli anahtarla imzalanır.
Bu, Loyetta'nın entegrasyon modeliyle uyumlu, sunucudan sunucuya bir özelliktir. Uç noktanız Loyetta sunucularından trafik alır — imza gizli anahtarınızı veya webhook işleyicinizi asla istemci uygulamalarına açmayın.
Kurulum
Webhook uç noktaları, panelinizden değil, Loyetta ekibi tarafından yapılandırılır.
- Loyetta destek ekibiyle iletişime geçin ve hangi kanal(lar)ı almak istediğinizi belirtin — SMS, push veya her ikisi.
- İstekleri almak istediğiniz her kanal için HTTPS URL'sini sağlayın. Her ikisi için aynı URL'yi veya kanal başına ayrı bir URL kullanabilirsiniz.
- URL'lerinizi kaydeder ve size imza gizli anahtarınızı veririz.
Yapılandırma tamamlandığında, Loyetta ilgili SMS / push trafiğini otomatik olarak uç noktalarınıza yönlendirir.
İmza gizli anahtarı
Şuna benzer tek bir paylaşılan gizli anahtar alırsınız:
loy_whsec_8f2c1a9b4e7d6c0a3f5e2d1c8b7a6f9e4d3c2b1a0f9e8d7c
- Aynı gizli anahtar, tüm kanallarda size gönderdiğimiz her webhook'u imzalamak için kullanılır.
- Ona bir parola gibi davranın: şifrelenmiş bir gizli anahtar / ortam değişkeni olarak saklayın, asla sürüm kontrolüne eklemeyin, asla istemci tarafında açığa çıkarmayın.
- Gizli anahtar döndürülebilir (bkz. Gizli anahtar döndürme). Döndürme sırasında her isteği hem eski hem yeni anahtarla imzalarız; böylece geçiş yaparken hiçbir şey bozulmaz.
İstek formatı
Her webhook, JSON gövdeli bir HTTP POST isteğidir. Gövde yapısı kanala göre değişir — olay başına yükler için SMS Gönderimi ve Push Gönderimi sayfalarına bakın.
Başlıklar
| Başlık | Örnek | Açıklama |
|---|---|---|
Content-Type | application/json | Gövde her zaman JSON'dur. |
X-Loyetta-Signature | t=1719300000,v1=ab34… | Zaman damgası + bir veya daha fazla HMAC-SHA256 imzası. Bkz. Doğrulama. |
X-Loyetta-Event | sms.send | Olay türü (sms.send veya push.send). |
X-Loyetta-Delivery | 01HZX9... | Bu gönderim denemesi için benzersiz bir kimlik. Günlük kaydı ve idempotency için kullanışlıdır. |
İmza başlığı formatı
t=<unix-zaman-damgasi>,v1=<hex-imza>[,v1=<hex-imza>]
t— isteğin imzalandığı Unix zaman damgası (saniye).v1— bir HMAC-SHA256 imzası. Gizli anahtar döndürme sırasında birden fazlav1değeri olabilir; herhangi biri eşleşirse istek geçerlidir.
Doğrulama
Bir webhook'a göre işlem yapmadan önce her zaman imzayı doğrulayın. Bu, isteğin Loyetta'dan geldiğini ve değiştirilmediğini kanıtlar. SMS ve Push işleyicilerinin ikisi de bu aynı doğrulama adımına dayanır.
İmza, şu dize üzerinden bir HMAC-SHA256'dır:
<zaman-damgasi>.<ham-istek-govdesi>
burada <zaman-damgasi>, X-Loyetta-Signature başlığındaki t değeridir ve <ham-istek-govdesi>, istek gövdesinin tam baytlarıdır.
Ayrıştırılmış JSON'u yeniden seri hale getirmeyin — imzayı aldığınız ham gövde üzerinden hesaplayın, aksi halde baytlar eşleşmez.
Doğrulama adımları
- Ham istek gövdesini okuyun (herhangi bir JSON ayrıştırmasından önce).
X-Loyetta-Signaturebaşlığınıtvev1değerleri listesine ayrıştırın.t, geçerli zamanınızdan 5 dakikadan (300 saniye) fazla uzaktaysa isteği reddedin — bu, tekrar saldırılarını (replay) azaltır.HMAC-SHA256("<t>.<ham-govde>", secret)hesaplayın.- Hesapladığınız imzayı her
v1değeriyle sabit zamanlı bir karşılaştırma kullanarak kıyaslayın. Herhangi biri eşleşirse istek gerçektir.
Örnek
Önce ham istek gövdesini (JSON ayrıştırmasından önce) okuyun, ardından kullandığınız teknolojiye uygun kod parçasıyla doğrulayın. Node örneği, değiştirilmemiş gövde baytlarına erişmek için express.raw() kullanır — kendi framework'ünüzde eşdeğerini yapın.
const crypto = require('crypto');
const express = require('express');
const app = express();
const SECRET = process.env.LOYETTA_WEBHOOK_SECRET;
const TOLERANCE_SECONDS = 300;
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(
header.split(',').map((p) => p.split('='))
);
// Döndürme sırasında `v1` birden fazla kez görünebilir:
const signatures = header
.split(',')
.filter((p) => p.startsWith('v1='))
.map((p) => p.slice(3));
const timestamp = parseInt(parts.t, 10);
if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) {
return false;
}
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
return signatures.some((sig) => {
const a = Buffer.from(expected);
const b = Buffer.from(sig);
return a.length === b.length && crypto.timingSafeEqual(a, b);
});
}
app.post('/webhooks/loyetta', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body.toString('utf8');
const header = req.get('X-Loyetta-Signature') || '';
if (!verify(rawBody, header, SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = req.get('X-Loyetta-Event');
const payload = JSON.parse(rawBody);
// Hızlıca yanıt verin (2xx), ardından asenkron işleyin.
res.status(200).send('ok');
if (event === 'sms.send') {
// Bkz. "SMS Gönderimi"
} else if (event === 'push.send') {
// Bkz. "Push Gönderimi"
}
});Yanıt verme
- Başarılı alındığını onaylamak için herhangi bir 2xx durumu (örn.
200 OK) döndürün. - 2xx olmayan herhangi bir yanıt (veya bağlantı hatası/zaman aşımı) başarısızlık olarak değerlendirilir ve yeniden denenir — bkz. Yeniden denemeler.
- Hızlı yanıt verin. Önce webhook'u onaylayın, ardından asıl SMS/push gönderimini asenkron olarak (kuyruk/worker) yapın. İstek 15 saniye sonra zaman aşımına uğrar.
- Yanıt gövdesi Loyetta tarafında hata ayıklama için yakalanır (kısaltılmış), ancak belirli bir şey içermesi gerekmez.
Yeniden denemeler ve gönderim
Uç noktanız bir 2xx yanıtı döndürmezse, Loyetta üstel geri çekilme (exponential backoff) ile yeniden dener:
| Deneme | Bu denemeden önce beklenen süre |
|---|---|
| 1 | hemen |
| 2 | 10 saniye |
| 3 | 60 saniye |
| 4 | 5 dakika |
| 5 | 15 dakika |
5 denemeden sonra gönderim başarısız olarak işaretlenir ve daha fazla denenmez.
Yeniden denemeler mümkün olduğundan, aynı mantıksal webhook birden fazla kez iletilebilir — işleyicinizi idempotent olacak şekilde tasarlayın.
Idempotency
Tekilleştirmek için X-Loyetta-Delivery başlığını (gönderim başına benzersiz bir kimlik) kullanın. İşlenmiş gönderim kimliklerini kaydedin ve zaten işlediklerinizi atlayın. Bu, uç noktanız aslında başarılı olduğu ancak yanıt bize zamanında ulaşmadığı için bir yeniden deneme yapıldığında sizi korur.
Gizli anahtar döndürme
İmza gizli anahtarı döndürüldüğünde:
- Bir geçiş penceresi boyunca Loyetta her webhook'u hem yeni hem eski gizli anahtarla imzalar —
X-Loyetta-Signaturebaşlığı birden fazlav1=değeri içerir. - Doğrulamanız (herhangi bir
v1eşleşirse isteği kabul eden), saklanan gizli anahtarınızı güncellemiş olun ya da olmayın çalışmaya devam eder. - Bu pencere boyunca saklanan gizli anahtarınızı yeni değerle güncelleyin. Herkes geçiş yaptığında, eski gizli anahtar emekliye ayrılır ve yalnızca yenisi gönderilir.
Yukarıdaki örneklerde gösterildiği gibi doğrulamanız tüm v1 değerleri üzerinde döndüğü sürece, istek sırasında herhangi bir işlem gerekmez.
Güvenlik kontrol listesi
- ✅ İşlemeden önce her istekte imzayı doğrulayın.
- ✅ HMAC'i, yeniden seri hale getirilmiş JSON üzerinden değil, ham gövde baytları üzerinden hesaplayın.
- ✅ Zaman damgası 5 dakikalık toleransın dışında olan istekleri reddedin.
- ✅ Sabit zamanlı bir karşılaştırma kullanın (
timingSafeEqual/hash_equals). - ✅ Uç noktayı yalnızca HTTPS üzerinden sunun.
- ✅ Gizli anahtarı şifrelenmiş bir gizli anahtar / ortam değişkeni olarak saklayın.
- ✅
X-Loyetta-Deliverykullanarak işleyicinizi idempotent yapın.
Hızlı başvuru
| Metot | POST |
| İçerik türü | application/json |
| Olaylar | sms.send, push.send |
| İmza başlığı | X-Loyetta-Signature: t=<unix>,v1=<hex>[,v1=<hex>] |
| İmzalanan dize | <zaman-damgasi>.<ham-govde> |
| Algoritma | HMAC-SHA256 (hex) |
| Tekrar (replay) toleransı | 300 saniye |
| Başarı yanıtı | herhangi bir 2xx |
| Zaman aşımı | 15 saniye |
| Yeniden denemeler | 5 deneme (geri çekilme 10s, 60s, 5d, 15d) |
| Uç nokta kurulumu | Loyetta'ya sağlanır; bizim tarafımızdan yapılandırılır |