Dokümantasyon / Webhooklar
Webhooklar
Olaylar, imza doğrulama ve tekrar deneme
Webhook, AloYemek'te bir olay olduğunda firmanızın belirlediği HTTPS adresine gönderilen POST isteğidir. Webhook adresinizi başvuru formunda verirsiniz; sonradan değiştirmek için bize yazın.
Olaylar
| Olay | Ne zaman? | data |
|---|---|---|
order.created | Restorana yeni sipariş geldi. | Sipariş nesnesi + event |
order.status_changed | Sipariş durumu değişti (kabul, hazır, yolda, teslim, tamamlandı). Değişiklik restoran panelinden, AloYemek'ten veya sizin API çağrınızdan gelebilir. | Sipariş nesnesi + event |
order.cancelled | Sipariş reddedildi, müşteri tarafından iptal edildi veya teslim edilemedi. | Sipariş nesnesi + event |
connection.created | Bir restoran firmanıza bağlandı. | restaurantName, scopes |
connection.key_rotated | Restoran anahtarını yeniledi; eski anahtar ve token'lar geçersiz. | restaurantName, scopes |
connection.revoked | Restoran bağlantıyı kaldırdı. | restaurantName, scopes |
ping | AloYemek panelinden gönderilen test bildirimi. | message |
İstek
POST /aloyemek/webhook HTTP/1.1 Content-Type: application/json; charset=utf-8 User-Agent: AloYemek-Webhook/1.0 X-AloYemek-Event: order.created X-AloYemek-Delivery: evt_3 X-AloYemek-Timestamp: 1789653499 X-AloYemek-Integrator: 3f0c1d7a-… X-AloYemek-Signature: sha256=5b1e0c…
{
"id": "evt_3",
"event": "order.created",
"createdAt": "2026-09-17T17:18:30+03:00",
"restaurantId": "0b484214-4740-407b-b3ec-d3987e6f1bb4",
"data": {
"id": "ad136e9a-9e62-43a5-a739-246ad09ab3dc",
"number": 10018,
"status": "new",
"statusGroup": "new",
"availableActions": ["accept", "reject", "ready"],
"type": "delivery",
"customer": { "name": "Ayşe Yılmaz", "phone": "+905551112233" },
"items": [ … ],
"totals": { … },
"event": { "status": "new", "changedBy": "" }
}
}Sipariş olaylarında data, sipariş detayı ile aynı nesnedir. data.event.status olayın gerçekleştiği andaki durumu, data.status gönderim anındaki güncel durumu gösterir. data.event.changedBy değişikliği kimin yaptığını belirtir; kendi API çağrılarınız API: Firma adınız olarak görünür.
İmza doğrulama
Her isteği işlemeden önce doğrulayın. İmza, X-AloYemek-Timestamp değeri, bir nokta ve ham istek gövdesinin webhook imza anahtarınızla HMAC-SHA256 özetidir. Tekrar saldırılarına karşı 5 dakikadan eski zaman damgalarını reddedin.
$govde = file_get_contents('php://input');
$zaman = $_SERVER['HTTP_X_ALOYEMEK_TIMESTAMP'] ?? '';
$imza = $_SERVER['HTTP_X_ALOYEMEK_SIGNATURE'] ?? '';
$beklenen = 'sha256=' . hash_hmac('sha256', $zaman . '.' . $govde, getenv('ALOYEMEK_WEBHOOK_SECRET'));
if (!hash_equals($beklenen, $imza) || abs(time() - (int)$zaman) > 300) {
http_response_code(401);
exit;
}
$olay = json_decode($govde, true);
// Olayı kuyruğa alın, hemen 200 dönün
http_response_code(200);import crypto from 'node:crypto';
import express from 'express';
const app = express();
app.post('/aloyemek/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const zaman = req.get('X-AloYemek-Timestamp');
const imza = req.get('X-AloYemek-Signature') || '';
const beklenen = 'sha256=' + crypto
.createHmac('sha256', process.env.ALOYEMEK_WEBHOOK_SECRET)
.update(zaman + '.' + req.body)
.digest('hex');
const gecerli = imza.length === beklenen.length &&
crypto.timingSafeEqual(Buffer.from(imza), Buffer.from(beklenen));
if (!gecerli || Math.abs(Date.now() / 1000 - Number(zaman)) > 300) return res.sendStatus(401);
const olay = JSON.parse(req.body);
res.sendStatus(200);
});import hmac, hashlib, os, time
from flask import Flask, request, abort
app = Flask(__name__)
@app.post('/aloyemek/webhook')
def webhook():
govde = request.get_data()
zaman = request.headers.get('X-AloYemek-Timestamp', '')
imza = request.headers.get('X-AloYemek-Signature', '')
beklenen = 'sha256=' + hmac.new(os.environ['ALOYEMEK_WEBHOOK_SECRET'].encode(),
zaman.encode() + b'.' + govde, hashlib.sha256).hexdigest()
if not hmac.compare_digest(beklenen, imza) or abs(time.time() - int(zaman or 0)) > 300:
abort(401)
olay = request.get_json()
return '', 200[HttpPost("aloyemek/webhook")]
public async Task<IActionResult> Webhook()
{
using var okuyucu = new StreamReader(Request.Body);
var govde = await okuyucu.ReadToEndAsync();
var zaman = Request.Headers["X-AloYemek-Timestamp"].ToString();
var imza = Request.Headers["X-AloYemek-Signature"].ToString();
var anahtar = Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("ALOYEMEK_WEBHOOK_SECRET"));
using var hmac = new HMACSHA256(anahtar);
var ozet = hmac.ComputeHash(Encoding.UTF8.GetBytes(zaman + "." + govde));
var beklenen = "sha256=" + Convert.ToHexString(ozet).ToLowerInvariant();
if (!CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(beklenen), Encoding.UTF8.GetBytes(imza))
|| Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - long.Parse(zaman)) > 300)
return Unauthorized();
return Ok();
}Yanıt ve tekrar deneme
- 10 saniye içinde 2xx dönün. Ağır işleri (yazdırma, veritabanı) kuyruğa alıp hemen yanıt verin.
- 2xx dışındaki her yanıt veya zaman aşımı başarısız sayılır ve şu aralıklarla tekrar denenir: 1 dk, 5 dk, 15 dk, 30 dk, 1 sa, 3 sa, 6 sa (toplam 8 deneme).
- Yönlendirmeler (3xx) izlenmez; adresinizin doğrudan 2xx döndüğünden emin olun.
- Aynı olay birden fazla gelebilir.
id(ör.evt_3) alanını saklayıp tekrar gelenleri yok sayın. - Olayların sırası garanti değildir; sipariş durumunu
createdAtvedata.updatedAtile karşılaştırarak güncelleyin veya şüphedeGET /orders/{id}ile doğrulayın.
Webhook gönderimleri her dakika çalışan bir görevle yapılır; yeni sipariş genellikle 5–10 saniye içinde adresinize ulaşır.
Sorunuz mu var? destek@aloyemek.com