Paket hesabı kapatılmadan önce sorulan before-action gate. table.close gibi TENANT-GENEL çalışır: SAHİPLİK ŞARTI YOKTUR — bu hook’u kaydeden her aktif eklenti, restoranın TÜM paket kapanışlarında (kendi oluşturmadığı paketler dahil) sorgulanır ve herhangi bir eklentinin deny’ı kapanışı engeller. allow/deny dönersiniz; deny → hesap kapanmaz, message personele gösterilir. transition YOKTUR. (Sahiplik kuralı yalnız packet.status.update için geçerlidir.)
| Hook key | packet.close |
| Scope | hooks:packet.close (consent-gated güçlü yetki) |
| Sahiplik | YOK — tenant-genel (table.close gibi); tüm paket kapanışlarında sorulur |
| Manifest | hooks: [{ event, ui, timeoutMs, failMode, includeData }] |
| Hedef | manifest actionUrl (yoksa webhookUrl) |
| Method | POST (senkron) |
| Rate limit | hook kovası (varsayılan 60/dk per-install) |
{
"event": "packet.close",
"ui": { "kind": "none" }, // gate genelde UI'sız karar; form/iframe da desteklenir
"timeoutMs": 5000, // cevap bütçesi [1s,10s]; aşımda failMode uygulanır
"failMode": "open", // VARSAYILAN: open (timeout/hata → kapanışa izin ver + uyar)
"includeData": true // gövdeye paketin kanonik verisi gömülür (packets/get şekli)
}Varsayılan open: endpoint’iniz cevap veremezse hesap kapatma engellenmez (degraded uyarısıyla devam). "closed" seçebilirsiniz ama ciddi sorumluluk: sunucunuz düştüğünde restoranın o paketlerdeki hesap kapatması bloklanır.
enforce bu hook’ta DESTEKLENMEZ (yok sayılır): gate çekirdek işlemin (hesap kapatma) içinde çalışır, ayrı sert doğrulayıcı yok. deny sunucuda kapanışı engeller; bu yeterli garantidir.
{
"type": "hook",
"event": "packet.close",
"stage": "before",
"tenantId": "<tenantId>",
"target": { "type": "packet", "id": "<packetId>" },
"data": {
"packetId": "<packetId>", "docNo": 12,
"customer": { "id": "c1", "name": "...", "phone": "..." },
"orders": [ { "id": "...", "title": "...", "quantity": 1, "options": [], "lineTotal": 130 } ],
"entegrasyon": "packet", "total": 130, "paid": 0, "totalDiscount": 0
},
"formData": { "<sizin-field-keyiniz>": "<personel-degeri>" }, // DİNAMİK: formunuzdaki key'ler; yalnız ui.kind form/iframe ise
"actor": { "userId": "<uid>", "role": "staff" },
"timeoutMs": 5000,
"occurredAt": 1718000000000,
"hookId": "hk_<uuid>"
}| Alan | Açıklama |
|---|---|
| target.id | Paket id’si. Veriyi packets/get?packetId= ile de çekebilirsiniz. (transition YOKTUR — bu hook kapanış öncesidir.) |
| data | Yalnız includeData:true ise. packets/get ile birebir kanonik şekil; customer yalnız customers:read + PII consent ile, data yalnız orders:read ile. |
| formData | Yalnız ui.kind form/iframe ise gelir. İçeriği DİNAMİKTİR: anahtarlar SİZİN formunuzda tanımladığınız field key’ler, değerler personelin girdiği değerlerdir (her eklentiye özel; sabit şema yok). ui.kind none ise hiç gelmez. |
| actor | Kapatmayı başlatan kullanıcı {userId, role} (manager/staff) — imzalı gövdede, güvenilir. |
| hookId | Bu çağrının id’si (loglama/dedup). |
{ "decision": "allow", "message": "Hesap kapatılabilir." }{ "decision": "deny", "message": "Önce kurye teslimini onaylayın." }| message | Anlam |
|---|---|
| plugin.hook.targetNotFound | Paket bulunamadı. |
| plugin.hook.notRegistered | Manifest’inizde bu hook yok. |
| plugin.scope.denied | hooks:packet.close onaylı değil. |
| plugin.hook.inactive | Kurulum pasif (kill-switch / billing / connect). |