Paket Detayı — GET /plugin-api/packets/get ✓ Canlı

Action ve iframe etkileşimleri yalnızca packetId taşır; paketin DOLU order'ını (ürünler, müşteri, adres, toplam ve ödeme kırılımı payments[]) bu uçtan çekersiniz. Standart desen: etkileşim bir id taşır, veri API'den okunur.

← API Uçları · ortak kurallar (base, auth, hata zarfı) orada.

İstek

GET {RESTOMENUM_BASE}/plugin-api/packets/get?packetId=<id>
Authorization: Bearer <apiKey>     // kurulumdaki (OAuth exchange) install API key
  • Base ({RESTOMENUM_BASE}): ortama göre — Sandbox https://sandbox.plugins.restomenum.app, Production https://plugins.restomenum.app (tüm liste: API Uçları).
  • Auth: Authorization: Bearer <apiKey> — kurulumda OAuth token exchange'te aldığın apiKey.
  • Scope: orders:read zorunlu (yoksa plugin.scope.denied).

Yanıt

data, webhook packet.created ile aynı kanonik alan ailesini taşır (envelope olmadan); alanların tam açıklaması o referansta. Canlı API yanıtında iki incelik var (aşağıda).

Gerçek 200 örneği (canlı)
{ "success": true, "data": {
  "packetId": "1780950756501",
  "docNo": 9,
  "entegrasyon": "packet",            // sipariş kanalı — STRING kod (packet | yemeksepeti | getir | trendyol …)
  "total": 27.8, "paid": 0, "totalDiscount": 0, "paymentNote": "nakit",
  "orders": [
    { "id": "1780950755436-de37", "title": "Cortado", "quantity": 1,
      // seçilen seçenekler — NESNE dizisi { id, title, price } (eskiden düz ad dizisi).
      // price katalogtan gelir; ücretliyse lineTotalDecimal'a platformca dahildir (elle ekleme).
      "options": [ { "id": "1693060234511", "title": "Az şekerli", "price": 0 } ],
      "extraDecimal": 0, "discountDecimal": 0, "note": "", "lineTotalDecimal": 13.9 }
  ],
  "payments": [],                     // ödeme kırılımı — bu pakette tahsilat yok (paid: 0). Satır şeması aşağıda.
  "customer": {                       // ⚠️ customers:read + consent ile DOLU gelir; yoksa PII alanları kırpılır
    "id": "123456", "name": "Ahmet Bayrak", "phone": "123456", "call": "123456",
    "address": "Adres", "addressDescription": null, "region": null
  }
} }
Tip inceliği: entegrasyon canlı API'de bir string kanal kodudur ("packet", "yemeksepeti" …) — obje değil. orderCode, isScheduled, scheduledDate, note ve orders[].product opsiyoneldir: yalnız ilgili durumda (entegrasyon / ileri tarihli sipariş / not) gelir, manuel pakette yanıtta yer almaz. Eksikliğe dayanıklı parse et.
Durumsal ek alanlar (entegrasyon / scheduled)
// entegrasyon siparişi + scheduled örnek (ek alanlar):
{
  "orderCode": "A12",                 // platform sipariş kodu (yemeksepeti/getir/trendyol)
  "isScheduled": true, "scheduledDate": 1780999999000,   // ileri tarihli teslim (epoch ms)
  "note": "Zili çalma"                // müşteri notu (varsa)
  // orders[].product: { title, … }   // bazı kanallarda tam ürün objesi de eklenir
}
Satır kimliği, korelasyon alanı ve satış tipi
{
  "type": "takeaway",                 // SATIŞ düzeyi tüketim biçimi — null = "bilinmiyor"
  "orders": [{
    "id": "kiosk-9f2c",
    "lineId": "kiosk-9f2c",           // = id (yazma/okuma simetrisi); eski satırlarda null
    "metadata": [                     // DİZİ; hiç yazılmadıysa null
      { "key": "tseRef", "value": "TSE-9911", "by": "hellokiosk" },   // by = YAZAR (platform damgalar)
      { "key": "loyalty", "value": "L-2", "by": "sadakat" }           // başka eklentinin öğesi — kendininkini by ile ayıkla
    ]
  }],
  "payments": [{
    "lineId": "pay-7c31",             // KALICI satır kimliği (yoksa null) — methodId ile KARIŞTIRMA
    "methodId": "m-cash",             // ödeme YÖNTEMİ id'si
    "metadata": [ { "key": "tseTx", "value": "77", "by": "fiskaly" } ]   // yoksa null
  }]
}
Yeni alanlar. Satır düzeyinde lineId + metadata, satış düzeyinde type döner. Üçü de eski kayıtlarda null'dır ("bilinmiyor" — kimlik/biçim uydurulmaz); payments[].id'ye fallback yapılmaz. Aynı alanlar tables/get, packet.created ve kapanış gate'inde de gelir — hepsi aynı modelden üretilir. Alanları göndermeyen eklentiler etkilenmez.

Satır seçenekleri (options[])

Bir sipariş satırının seçilmiş seçenekleri tipli nesne dizisidir. Aynı şekil tüm okuma yüzeylerinde geçerlidir: packets/get, tables/get, packet.* / table.* olayları (created/updated/closed/deleted/reopened/closed_deleted), packet.cancelled, customer.order_added, hook includeData gövdesi, callback servisi ve iptal listesi cancels[].

"options": [
  { "id": "1693060234600", "title": "Ekstra Peynir", "price": 15 }
]

// id    → katalogdaki seçenek kimliği (products/get → options[].choices[].id)
// title → seçenek adı
// price → katalogtan okunan birim fiyat; YAZARKEN BEYAN EDİLEMEZ, lineTotalDecimal'a platform dahil eder
//
// Yazarken üç biçimden biriyle beyan edersin: { id } | { title } | "ad" (eski biçim).
// Okurken her zaman üç alan birden döner.
Kırıcı değişiklik: bu alan eskiden düz ad dizisiydi (["Ekstra Peynir"]). Yalnız ada ihtiyacın varsa options.map(o => o.title) yaz. price satır toplamına platformca dahildir — lineTotalDecimal üzerine ayrıca ekleme.
Yazarken seçeneği { id }, { title } ya da düz string ile beyan edersin; fiyat beyan edilemez (şema reddeder, katalogtan okunur). Ayrıntı: packets/create.

quantity / extra / discount — tip garantisi

Bu üç alan dokümandan ham geçiyordu (lineTotalDecimal ve vatRate normalize edilirken): sözleşme tip garantisi vermiyordu ve alan eksikse payload'dan sessizce düşüyordu — "alan gelmedi" ile "değer yok" ayırt edilemiyordu. ⏳ Artık her zaman sonlu bir sayı gelir.

GirdiextraDecimal / discountDecimalquantity
"10" · "10,50"ayrıştırılır → 10 · 10.5 (virgül ondalık ayırıcı)
"" · çözülemeyen01
alan yok01
açık 000 (korunur)
quantity neden 1'e düşüyor? Aynı satırın lineTotalDecimal'ı eksik adedi 1 kabul ederek hesaplanıyor; 0 deseydik gövde kendi kendini yalanlardı ({ quantity: 0, lineTotalDecimal: 100 }). Yuvarlama yapılmaz — quantity para değildir, kilo bazlı satır ikiden fazla ondalık taşıyabilir.
Number("") → NaN dalı artık hiç doğmuyor. Ölçüm: dört üretim tenant'ında 1196 satır tarandı, tamamı zaten sayıydı — bu bir veri düzeltmesi değil, sözleşme garantisidir. (Sandbox'ta yayında, production'a çıkmadı.)

Tutarlar (currency / amountExponent / amounts)

Satır tutarı iki biçimde gelir: lineTotalDecimal (ondalık, değişmedi) ve amounts (tamsayı minor unit bileşen kırılımı). Para birimi belgede bir kez durur.

Tutar alanları
{
  "currency": "TRY",                  // ISO-4217 — çözülemezse alan HİÇ gelmez
  "amountExponent": 2,                // YALNIZ amounts.* için (TRY → kuruş)
  "total": 27.8, "paid": 0,           // ← ONDALIK, değişmedi (üssü BUNLARA uygulama)
  "orders": [{
    "lineTotalDecimal": 13.9,                // ← ONDALIK
    "vatRate": 10,
    "amounts": {                      // ← TAMSAYI minor unit (kuruş)
      "base": 1390, "options": 0, "extra": 0, "discount": 0, "timer": 0,
      "gross": 1390, "net": 1264, "vat": 126
    }
  }]
}
amountExponent yalnız amounts.* içindir. total, paid, lineTotalDecimal, options[].price, payments[].amount ondalıktır — üssü onlara uygularsan 100 kat sapma alırsın. Garantiler, karışık KDV kovaları (perVat) ve geri düşüş kuralları: Tutarlar (amounts).

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.

PII (customer)

customer alanları webhook ile aynı kuralla kırpılır: customers:read yoksa PII alanları (name, phone, address, email, tckn, vergino) silinir; id vb. kalır.

Hatalar

DurumYanıt
Paket yok{ success:false, message:"plugin.packets.notFound" }
Scope yok{ success:false, message:"plugin.scope.denied" }

Kullanım

// action-hook (/api/action) ya da iframe (/api/send) içinde: etkileşim yalnız packetId taşır → dolu paketi çek.
const id = envlp.target.id;          // packet.created ile aynı packetId (target.type: packet)
const r = await fetch(`${RESTOMENUM_BASE}/plugin-api/packets/get?packetId=${id}`, {
  headers: { Authorization: `Bearer ${apiKey}` },   // kurulumdaki install API key
});
const { success, data, message } = await r.json();
if (!success) throw new Error(message);   // plugin.packets.notFound | plugin.scope.denied

// data.orders / data.customer / data.total … → kurye/hedef sisteme ilet