packet.created ✓ Canlı

Restomenum'da yeni bir paket sipariş oluşunca eklentinin webhookUrl'ine imzalı POST edilir. Bu, paket/kurye eklentilerinin temel event'idir. Aşağıdaki şema canlı (authoritative) payload'ı tanımlar.

← Event Kataloğu

HTTP & imza

POST {webhookUrl}
Content-Type: application/json
X-Restomenum-Signature: t=<unixSec>,v1=<HMAC_SHA256(webhookSecret, "<t>.<rawBody>")>
  • İmzayı ham gövde üzerinden doğrula ("<t>.<rawBody>"), ±5 dk replay toleransı. webhookSecret kurulumda (OAuth exchange) verilir — bkz. imza şeması ve token exchange.
  • 2xx dön → işlendi sayılır. 2xx dışı / timeout → Restomenum retry eder.
  • Aynı id tekrar gelebilir (retry) → idempotency için id'yi dedup et.

Envelope (tüm event'lerde ortak)

AlanTipZorunluAçıklama
idstring✓Idempotency anahtarı. Aynı id tekrar gelebilir → dedup et.
typestring✓packet.created
versionstring✓Envelope şema versiyonu ("1").
environment"sandbox" | "production"✓Teslimin ortamı (test mağazası = "sandbox") — imzalı gövdede; header kopyasını değil bunu kullan.
tenantIdstring✓Restoran (tenant) id.
occurredAtnumber✓Oluşma zamanı (unix ms).
dataobject✓Event'e özel gövde (aşağıda).

data alanları

AlanTipZorunluAçıklama
packetIdstring✓Paket id (statü callback'lerinde kullanılır).
docNonumber✓Günlük sipariş no.
orderCodestring–Sipariş kodu (örn. "A12").
totalnumber✓Paket toplamı.
paidnumber✓Ödenen tutar.
totalDiscountnumber–İndirim toplamı (türetilmiş).
paymentNotestring–Ödeme notu (örn. "Kapıda nakit").
isScheduledboolean✓İleri tarihli sipariş mi?
scheduledDatenumber | null–İleri tarihliyse zaman, değilse null.
notestring–Müşteri/sipariş notu.
entegrasyonstring–Sipariş kanalı — string kod: packet (manuel/telefon), yemeksepeti, getir, trendyol …
orders[]array✓Sipariş satırları (id, title, quantity, options[], extra, discount, note, lineTotalDecimal, product). title = ürün adı, kökten direkt oku; options = nesne dizisi { id, title, price } (eskiden düz ad dizisi); price katalogtan gelir, lineTotalDecimal'a platformca dahildir; product tam ürün objesi de gelir. lineTotalDecimal'ı Restomenum hesaplar.
payments[]array✓Ödeme kırılımı ({ methodId, title, amount, cash, isDiscount }) — packets/get ile birebir aynı satır. Tahsilat yoksa boş; paid bu satırların toplamıdır (aşağı bkz).
customerobject–⚠️ PII alanları yalnız customers:read + consent ile DOLU gelir (aşağı bkz).
callbackUrlsobject–⚠️ Yalnız packets:status ile EKLENİR (aşağı bkz).

Tam örnek payload

customers:read + packets:status DAHİL (en dolu hali)
{
  "id": "evt_9f2a7c1b",
  "type": "packet.created",
  "version": "1",
  "environment": "sandbox",
  "tenantId": "tnt_123",
  "occurredAt": 1730000000000,
  "data": {
    "packetId": "1780633662954",
    "docNo": 42,
    "orderCode": "A12",
    "total": 145,
    "paid": 0,
    "totalDiscount": 0,
    "paymentNote": "Kapıda nakit",
    "isScheduled": false,
    "scheduledDate": null,
    "note": "Zili çalma",
    "entegrasyon": "packet",
    "orders": [
      {
        "id": "ord_1",
        "title": "Lahmacun",
        "quantity": 2,
        "options": [
          {
            "id": "1693060234801",
            "title": "Acılı",
            "price": 0
          },
          {
            "id": "1693060234802",
            "title": "Bol soğan",
            "price": 0
          }
        ],
        "extraDecimal": 0,
        "discountDecimal": 0,
        "note": "",
        "lineTotalDecimal": 60,
        "product": {
          "title": "Lahmacun"
        }
      }
    ],
    "payments": [],
    "customer": {
      "id": "cust_9",
      "name": "Ahmet Yılmaz",
      "phone": "05xxxxxxxxx",
      "address": "Atatürk Cad. No:5",
      "addressDescription": "2. kat",
      "region": "Kadıköy",
      "call": "05xxxxxxxxx"
    },
    "callbackUrls": {
      "pickup": "https://plugins.restomenum.app/plugin-api/packets/{tenantId}/{pluginId}/{packetId}/pickup?token=…",
      "delivered": "https://plugins.restomenum.app/plugin-api/packets/{tenantId}/{pluginId}/{packetId}/delivered?token=…",
      "cancel": "https://plugins.restomenum.app/plugin-api/packets/{tenantId}/{pluginId}/{packetId}/cancel?token=…"
    }
  }
}

payments[] — ödeme kırılımı

payments[], paketin ödeme satırlarını taşır ve orders:read ile gelir. Aynı satır şekli packets/get, tables/get ve packet.created yüzeylerinde birebir aynıdır. İç alanlar (created = işlemi yapan personel, log, description) gönderilmez.

Satır şeması
// payments[] satırı — allowlist (packets/get · tables/get · packet.created BİREBİR AYNI)
{
  "methodId":   string | null,   // tenant'ın ödeme yöntemi id'si; tanımlı yönteme bağlı değilse null
  "title":      string,          // yöntemin başlığı (tenant tanımı) — gösterim için
  "amount":     number,          // satır tutarı
  "cash":       boolean,         // yöntem NAKİT mi → CASH / NON_CASH ayrımı YALNIZ buradan okunur
  "cashDeclared": boolean,       // cash BEYAN mı, yoksa platformun güvenli varsayımı mı
  "isDiscount": boolean          // true → indirim satırı (tahsilat DEĞİL), satır listede KALIR
}
AlanTipZorunluAçıklama
methodIdstring | null✓Tenant'ın ödeme yöntemi id'si — geçerli id'ler payment-methods/list'ten gelir. Satır tanımlı bir yönteme bağlı değilse null (satırın kendi uuid'si sızdırılmaz).
titlestring✓Yöntemin tenant tanımındaki başlığı (örn. "nakit"). Gösterim içindir.
amountnumber✓Satır tutarı. Tüm satırların toplamı paid'e eşittir (indirim satırları dahil).
cashboolean✓Yöntem nakit mi. Mali CASH/NON_CASH ayrımının tek doğru kaynağı budur; tenant tanımında işaretli değilse false gelir.
cashDeclaredboolean✓cash bir beyan mı, yoksa platformun güvenli varsayımı mı? false → yöntem kaydında nakitlik alanı hiç doğmamış. Mali damgalamada bu ayrım kritiktir — bkz. aşağıdaki uyarı.
isDiscountboolean✓true → satır bir indirimdir, tahsilat değildir. Satır listeden düşürülmez; totalDiscount bu satırlardan türetilir.
Nakit ayrımını cash bayrağından oku — methodId veya title METNİNDEN çıkarma. Id'ler tenant'a özeldir ("29-cash" gibi bir id nakit olmayabilir, "kart" adlı bir yöntem nakit işaretli olabilir). Ayrıntı için payment-methods/list.
cashDeclared:false ise cash bir varsayımdır. Yöntem kaydında nakitlik alanı hiç doğmamıştır (genelde kiracı açılışında oluşan varsayılan yöntemler). Almanya'da ödeme tipi (Bar/Unbar) imzalanan fişin güvence altına alınan verisidir (ZAHLART_TYP): cashDeclared:false görürsen varsayıma dayalı sınıflandırmayı damgalama, kiracıdan beyan iste.
Aynı alan payment-methods/list kataloğunda da var — satırda ayrıca bulunmasının sebebi join'in çalışmaması: ölçümde ödeme satırlarının %39'u kiracı kataloğunda karşılığı olmayan bir methodId taşıyor (teslimat entegrasyonlarının ürettiği sözde-yöntemler). O satırlarda join edecek hedef YOKTUR → nakitlik bilgisini satırdan oku.
Satır kimliği ve korelasyon alanı
// Satır kimliği + korelasyon alanı — aynı satırda döner
{
  "lineId":   string | null,     // KALICI satır kimliği; eski/panel satırlarında null (id'ye FALLBACK YOK)
  "metadata": [ { key, value, by } ] | null   // opak korelasyon DİZİSİ; by = yazar (platform damgalar)
}
lineId ≠ methodId. lineId satırın kalıcı kimliğidir; methodId ödeme yönteminin id'sidir. Panel/yerli akışta veya bu sürümden önce yazılmış satırlarda lineId ve metadata null döner ve yazmadaki payments[].id'ye fallback yapılmaz — o alan yöntem id'si de olabildiği için iki farklı satırı aynı kimlikle gösterirdi. Kimlik uydurmak yerine "bilinmiyor" denir. Kurallar: Satır kimliği & metadata.
⏳ Yeni: cancelPayments[] — iptal edilen tahsilatlar. payments[] ile birebir aynı şekil (yeni bir tip öğrenmene gerek yok). Satır void'i (cancels) yayınlanırken ödeme void'inin yayınlanmaması doğrudan bir asimetriydi: iptal edilmiş bir tahsilat tüketiciye hiç görünmüyor, defterdeki "ödenmiş" tutar POS'takinden sapıyordu. ⚠️ paid aktif ödemelerin toplamıdır — bu liste ona dahil değildir. (Sandbox'ta canlı, production'a dağıtılmadı.)
Toplam tutarlılığı: indirim satırları da listede kaldığı için sum(amount) === paid korunur. Mali toplamda isDiscount:true satırlarını tahsilat gibi sayma; ama listeden atarsan toplam paid'i tutmaz.

Scope'a göre değişen alanlar

ScopeEtki
events:subscribe + packet.createdWebhook'u almak için zorunlu (manifest events[] + scope).
customers:read yoksacustomer objesindeki PII alanları silinir (name, phone, address, email, tckn, vergino). id / region / call / addressDescription kalır.
packets:status yoksacallbackUrls hiç eklenmez.
PII alanlarını yalnız belirttiğin amaçla işle. Yeni bir sürümde customers:read eklemek kurulumda re-consent tetikler. Bkz. customers:read.

Statü callback (packets:status)

Paket durumunu Restomenum'a bildirmek için callbackUrls'ten ilgili URL'e POST at (token query string'te). Bu URL'leri sen üretmezsin — platform üretip payload'a ekler, sen uygun anda çağırırsın (örn. aksiyon butonu / actionUrl çalışınca).

# Statü bildirimi — token query string'te, gövde boş/serbest
POST https://plugins.restomenum.app/plugin-api/packets/{tenantId}/{pluginId}/{packetId}/pickup?token=…
POST …/{packetId}/delivered?token=…
POST …/{packetId}/cancel?token=…
# pickup → yolda · delivered → teslim edildi · cancel → iptal (terminal — sonrası değişmez)
pickup     → yolda
delivered  → teslim edildi
cancel     → iptal
(terminal durum sonrası değişmez)