Paket Oluştur — POST /plugin-api/packets/create ✓ Canlı

Eklentinin Restomenum'da yeni paket/delivery siparişi oluşturduğu yazma ucudur. Okuma uçlarından farklı: POST, JSON gövde alır ve orders:write scope'u ister. Sepet ürün id'leriyle gönderilir; fiyatlar Restomenum ürün kaydından alınır (gönderdiğiniz fiyat yok sayılır). idempotencyKey ile retry'da çift sipariş oluşmaz.

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

Genel

Method / yolPOST /plugin-api/packets/create
Authinstall API key — Authorization: Bearer serverId.pluginId.secret
Scopeorders:write
Rate limitAyrı write kovası (varsayılan 20/dk; okuma kovasından sıkı) — Limitler
Content-Typeapplication/json

İstek

POST {RESTOMENUM_BASE}/plugin-api/packets/create
Authorization: Bearer <apiKey>     // install API key: serverId.pluginId.secret
Content-Type: application/json
  • Base: Sandbox https://sandbox.plugins.restomenum.app, Production https://plugins.restomenum.app (API Uçları).
  • Auth: Authorization: Bearer <apiKey>token exchange'teki install API key.
İstek gövdesi
{
  "customer": {                      // zorunlu
    "id": "cust-123",                // zorunlu
    "name": "Ahmet Bayrak",          // zorunlu
    "address": "Örnek Mah. ...",     // zorunlu
    "region": "Kadıköy",             // ops
    "addressDescription": "3. kat",  // ops
    "phone": "5xx...",               // ops
    "call": "5xx..."                 // ops
  },
  "cart": [                          // zorunlu, 1..200 kalem
    {
      "product": "3b5d-f6d0",        // ürün id (products/list id'si) — zorunlu
      "quantity": 2,                 // zorunlu (0..9999)
      "options": ["çilek", "bal"],   // ops — opsiyon ADLARI (string)
      "discount": 0,                 // ops (>= 0)
      "note": "az pişmiş"            // ops
    }
  ],
  "paymentNote": "nakit",            // zorunlu
  "payments": [                      // ops (<= 20). price >= 0; price > 0 olanlar saklanır
    { "id": "pm-1", "title": "Nakit", "price": 130, "isDiscount": false }
  ],
  "note": "Kapıda zil çalma",        // ops
  "status": "Approved",              // ops: 'none' | 'Approved' (default 'Approved')
  "restaurantDelivery": true,        // ops (default true)
  "idempotencyKey": "order-9af2...", // ops AMA ÖNERİLİR (retry'da çift sipariş engeller)
  "callbackUrl": "https://..."       // ops (https, ≤500) — SAHİPLİK damgası + status hedefi; webhookUrl ile AYNI domain ŞART
}

Gövde alanları

AlanTipZorunluAçıklama
customerobjectevetid, name, address zorunlu; region, addressDescription, phone, call opsiyonel.
cart[]arrayevet1..200 kalem. Her kalem: product (ürün id, zorunlu), quantity (0..9999, zorunlu), options (opsiyon adları — string dizisi), discount (≥0), note.
paymentNotestringevetÖdeme notu (ör. "nakit").
payments[]arrayhayır≤20. { id, title, price, isDiscount? }. price ≥ 0; yalnız price > 0 saklanır. Normal satır: id tenant'ın gerçek yöntemi olmalı (payment-methods/list'ten al); title yok sayılır. isDiscount:true: doğrulamadan muaf (serbest id), title zorunlu. Aşağı bkz.
notestringhayırSipariş notu (ör. "Kapıda zil çalma").
statusstringhayır'none' | 'Approved'varsayılan 'Approved'.
restaurantDeliverybooleanhayırRestoranın kendi kuryesiyle teslim — varsayılan true.
idempotencyKeystringönerilirKendi benzersiz sipariş anahtarın. Aynı anahtarla retry → aynı paket, çift sipariş yazmaz (24sa pencere).
callbackUrlstring (https)hayırSahiplik damgası + status-bildirim hedefi. Kurallar: https, ≤500 karakter, manifest webhookUrl'ünüzle aynı registered domain, private IP'ye çözünmemeli (ihlalde 400). Aşağı bkz.

Yanıt

// başarı
{ "success": true, "data": { "packetId": "a1b2c3-uuid" } }

// hata
{ "success": false, "message": "<açıklama>" }

data.packetId oluşan paketin id'sidir → tam detayı packets/get?packetId= ile çekebilirsin.

messageAnlam
joi doğrulama mesajıGeçersiz gövde (eksik/yanlış alan).
Product not found: <id>cart'taki bir ürün mağazada yok (TÜM ürünler var olmalı).
unknown_payment_methodNormal ödeme satırının id'si tenant'ın tanımlı yöntemi değil (400). Önce payment-methods/list.
no_payment_methods_configuredTenant'ta hiç ödeme yöntemi tanımlı değil (400; id yanlışlığından ayrı kod).
Paid amount is greater than total…payments toplamı sipariş tutarını aşıyor.
Server not foundGeçersiz tenant.
Duplicate request already in progressAynı idempotencyKey ile eşzamanlı 2. istek.
plugin.scope.deniedorders:write onaylı değil.
callbackUrl must be under the same domain…callbackUrl manifest webhookUrl'ünüzle aynı registered domain'de değil.
callbackUrl rejected: <sebep>callbackUrl güvenlik kontrolünden geçemedi (private IP / DNS hatası vb.).

Önemli kurallar

Fiyatlar OTORİTER. Satır fiyatı Restomenum ürün kaydından alınır; gövdede fiyat göndermezsiniz / gönderseniz de yok sayılır. Yalnız product (id) + quantity + options (ad) + discount verirsiniz. (Güvenlik: client fiyat iddiası kabul edilmez.) payments[].price ödeme tutarıdır, ürün fiyatı değil.
Ödeme yöntemi doğrulaması (önce payment-methods/list): normal ödeme satırlarının id'si tenant'ın gerçek bir yöntemi olmalı — keyfi id → unknown_payment_method. title yöntem kaydından türetilir (yok sayılır). İndirim satırları (isDiscount:true) muaftır ve title gerektirir.
  • Idempotency: idempotencyKey gönderin. Ağ retry / tekrar tesliminde aynı paketi döndürür, çift sipariş yazmaz (24sa pencere).
  • Tüm cart ürünleri var olmalı: biri bile eksikse istek reddedilir — yarım sipariş yazılmaz (Product not found).
  • options = opsiyon adları (string dizisi); fiyatlarını backend belirler.
  • Oluşan paket entegrasyon: "packet" ile yazılır; packet.created event'i abone eklentilere düşer.

Status (durum) yönetimi — ayrı mekanizma

Bu uç paketi oluşturur. Sonradan status'ü sürmek (Approved → OnDelivery → Delivered) için bu ucu DEĞİL, packets:status scope'u + status callback'lerini kullanın: packet.created event'inde size imzalı callbackUrl'ler (pickup / delivered / cancel) verilir; teslim/iptal durumlarını onları çağırarak setlersiniz (Getir/Yemeksepeti deseni — platform kendi siparişinin teslim durumunu sürer).

Gövdedeki callbackUrl = sahiplik damgası. Paketi sizin oluşturduğunuzu işaretler ve ileride statü bildirimlerinin hedefi olur. Sahiplik damgası packet.status.update gate'ini açar: restoran sizin oluşturduğunuz paketin statüsünü değiştirmeden önce size sorulur (allow/deny). (packet.close ise tenant-geneldir, sahiplik/callbackUrl gerektirmez.) Kurallar: https, ≤500 karakter, manifest webhookUrl'ünüzle aynı registered domain, private IP'ye çözünmemeli (ihlalde 400).
Bildirim gönderimi henüz aktif değil (yakında: packet.status_changed) —callbackUrl şimdilik saklanır + sahiplik/hook'u açar, ama statü değişiminde henüz çağrı yapılmaz. Status'ü siz setlemek istiyorsanız packets:status + statusCallback yolunu kullanın (yukarıda).

Örnek (curl)

curl -X POST https://plugins.restomenum.app/plugin-api/packets/create \
  -H "Authorization: Bearer <serverId>.<pluginId>.<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": { "id": "c1", "name": "Test", "address": "Adres" },
    "cart": [ { "product": "3b5d-f6d0", "quantity": 1 } ],
    "paymentNote": "nakit",
    "idempotencyKey": "order-9af2"
  }'