Loyetta

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.

  1. Loyetta destek ekibiyle iletişime geçin ve hangi kanal(lar)ı almak istediğinizi belirtin — SMS, push veya her ikisi.
  2. İ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.
  3. 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ÖrnekAçıklama
Content-Typeapplication/jsonGövde her zaman JSON'dur.
X-Loyetta-Signaturet=1719300000,v1=ab34…Zaman damgası + bir veya daha fazla HMAC-SHA256 imzası. Bkz. Doğrulama.
X-Loyetta-Eventsms.sendOlay türü (sms.send veya push.send).
X-Loyetta-Delivery01HZX9...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 fazla v1 değ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ı

  1. Ham istek gövdesini okuyun (herhangi bir JSON ayrıştırmasından önce).
  2. X-Loyetta-Signature başlığını t ve v1 değerleri listesine ayrıştırın.
  3. t, geçerli zamanınızdan 5 dakikadan (300 saniye) fazla uzaktaysa isteği reddedin — bu, tekrar saldırılarını (replay) azaltır.
  4. HMAC-SHA256("<t>.<ham-govde>", secret) hesaplayın.
  5. Hesapladığınız imzayı her v1 değ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:

DenemeBu denemeden önce beklenen süre
1hemen
210 saniye
360 saniye
45 dakika
515 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-Signature başlığı birden fazla v1= değeri içerir.
  • Doğrulamanız (herhangi bir v1 eş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-Delivery kullanarak işleyicinizi idempotent yapın.

Hızlı başvuru

MetotPOST
İçerik türüapplication/json
Olaylarsms.send, push.send
İmza başlığıX-Loyetta-Signature: t=<unix>,v1=<hex>[,v1=<hex>]
İmzalanan dize<zaman-damgasi>.<ham-govde>
AlgoritmaHMAC-SHA256 (hex)
Tekrar (replay) toleransı300 saniye
Başarı yanıtıherhangi bir 2xx
Zaman aşımı15 saniye
Yeniden denemeler5 deneme (geri çekilme 10s, 60s, 5d, 15d)
Uç nokta kurulumuLoyetta'ya sağlanır; bizim tarafımızdan yapılandırılır