Fişe Ek Alanlar — receiptExtras ✓ Canlı

Kapanış gate'i (table.close / packet.close) allow yanıtında receiptExtras dizisi dönerse, öğeler kapanış yazılmadan ÖNCE adisyon dokümanına işlenir ve fişe basılabilir hale gelir. Karar ile veri aynı yanıtta gelir — belgeyi bağlamak için ikinci bir API çağrısı gerekmez. Tipik kullanım: Almanya KassenSichV / TSE mali bloğu, e-arşiv belge numarası, sadakat/kampanya satırı.

← Hook'lar (Akış Kontrolü) · table.close · packet.close

Bunu yazdırma köprüsüyle karıştırma. receiptExtras uygulamanın kendi hesap fişine alan ekler ve kullanıcı fiş bastığında çalışır — eklenti ekranının açık olması gerekmez. print ise kendi başına duran bir belge bastırır (kupon, çek, rapor) ve yalnız eklenti sayfan panelde açıkken çalışır.
Yeni scope yok, yeni uç yok, manifest alanı yok — sahiplik mevcut capability scope'unuzdan türetilir. Bu bir satışa yazma ucu değildir (§ Katman 1).

Sözleşme

Gate isteği değişmedi (bkz. Action Hook referansı). Yanıta yalnız receiptExtras eklendi:

allow + receiptExtras
{
  "decision": "allow",
  "receiptExtras": [
    { "key": "tse.qr",               "type": "qr",   "value": "V0;…" },
    { "key": "tse.txNumber",         "type": "text", "label": "Beleg-Nr",   "value": "366" },
    { "key": "tse.signatureCounter", "type": "text", "label": "Sig-Zähler", "value": "774" },
    { "key": "tse.timeStart",        "type": "text", "label": "TSE-Start",  "value": "1785438065" },
    { "key": "tse.status",           "type": "text", "value": "signed" }
  ]
}

{ "data": { … } } sarmalı da kabul edilir (mevcut hook davranışı).

Öğe şeması

AlanTipZorunluKural
keystringevet≤64; a-zA-Z0-9_- segmentleri, nokta ile ayrılır (tse.qr). Namespace sahipliğine tabidir.
type'text' | 'qr'hayırBilinmeyen değer öğeyi DÜŞÜRMEZ — güvenli default text uygulanır.
valuestringevetDAİMA metin, 1–2000 karakter. Sayı göndermeyin, string gönderin.
labelstringhayır≤64 — fişte değerin başına basılacak etiket (Beleg-Nr).
verifiedbooleanplatform üretirSahiplenilmiş namespace + yetki doğrulandıysa platform ekler. Eklenti gönderemez (gönderilirse yok sayılır).

type değerleri:

  • text — Metin — fişe düz satır olarak basılır (label varsa başına eklenir)
  • qr — QR — fişe QR kodu olarak basılır (ör. TSE doğrulama verisi)
value neden hep metin? Bu veri fişe basılan gösterim verisidir, hesaplama verisi değil. Beleg-Nr, Sig-Zähler ve epoch değerleri metin taşındığı için int32/precision taşması diye bir sorun doğmaz — TSE'den ne geldiyse fişe o basılır. Sayı göndermeyin, string gönderin.

Katman 1 — belgeye yazılan tek anahtar

Gate yanıtındaki diğer alanlar (decision, message, level, display) protokol alanlarıdır: akışı yönetir ve kasiyere bildirim gösterir. Adisyon dokümanına yazılan tek alan receiptExtras'tır.

Tutar, ödeme ve satır alanları bu yola hiç giremez. Platformun yazdığı alan adı sabittir; verdiğiniz key yalnız dizi öğesinin içinde yaşar — doküman kökünde alan açamaz. {"key":"total"} göndermek adisyon tutarını değiştirmez, yalnızca total adlı bir gösterim öğesi üretir (ve verified işareti almaz).

Adisyon tutarını/kalemlerini gerçekten değiştirmeniz gerekiyorsa tables/update-orders · packets/update-orders ve update-payments uçlarını kullanın — onlar recompute + paid ≤ total doğrulamasından geçer.

Katman 2 — key namespace sahipliği (GÜVENLİK)

Bazı namespace'ler sahiplenilmiştir; onları yalnız o alanın yetkilisi yazabilir:

NamespaceGereken yetkiAnlamı
tse.*hook scope'u + capability:fiscal.de:provide + fiscal.de bağlı sağlayıcısıMali blok (KassenSichV / TSE)
diğer (loyalty.*, kampanya…)hook scope'u yeterliSerbest gösterim öğesi
Neden: aksi halde herhangi bir eklenti tse.qr döndürüp fişe sahte mali blok bastırabilirdi. Yetkisiz eklentinin sahiplenilmiş key'i sessizce düşürülür (dizide yer almaz) — hata dönmez, bu yüzden yetkinizi kurulumda doğrulayın.
Gate yolunda scope YETMEZ — binding de aranır. Kapanış gate'i tenant-genel sorulur; tenant'ta iki fiskal sağlayıcı kuruluysa scope tek başına ikisine de tse.* yazdırırdı. Artık eklentinin o tenant'ta fiscal.de'nin bağlı sağlayıcısı olduğu da doğrulanır; değilse öğe sessizce düşer. Tenant'ta tek aday varsa davranış değişmez.
Yetki nasıl alınır: manifest'te provides: [{ capability: "fiscal.de" }] beyan edersiniz (portal editöründe "Eklentiler-Arası Yetenekler" kartı); türetilmiş capability:fiscal.de:provide scope'u otomatik eklenir — elle scope seçmezsiniz. Aynı scope, fişi imzalamak için platformdan gelen type:"capability" isteğini almanızı da sağlar: Mali Fişleme (fiscal.de). E-fatura/e-arşiv belgesi ayrı bir yetenektir (invoice.issue) — mali cihaz imzasıyla karıştırmayın; invoice.issue sağlayıcısı tse.* yazamaz.
İki yol, aynı blok: receiptExtras yalnız kapanış gate'inde değil, fiscal.de capability yanıtında da dönebilir (fişi kendi basan kiosk eklentisi POS kapanış gate'ini tetiklemez). Her iki yolda da platform öğeleri satış belgesine yazar → aynı blok packets/get ile okunup yeniden basılabilir (yeni imza YOK). Sınırlar, namespace sahipliği ve verified kuralı ikisinde de aynıdır.

verified işareti

Sahiplenilmiş namespace'te ve yetki doğrulandıysa platform öğeye verified: true ekler:

{ "key":"tse.txNumber", "type":"text", "label":"Beleg-Nr", "value":"366", "verified":true }
  • Sahiplenilmemiş/bilinmeyen key'ler bu işareti asla almaz — onlara mali anlam yüklenemez.
  • Fiş şablonu mali bloğu yalnız verified öğelere basar.
  • Eklenti verified'ı kendisi gönderemez; platform üretir (gönderilirse yok sayılır).

Yazım kuralları

  • Yalnız allow'da yazılır. deny dönerseniz hiçbir şey yazılmaz — ödeme eşlenemediği için deny döndüyseniz fiş de kesilmemiştir, o key'lerin belgeye girmemesi gerekir. pending'de karar henüz yoktur.
  • Değiştirilemezlik: bir key bir kez yazıldıysa üzerine yazılmaz. Aynı key'i tekrar göndermek sessizce yok sayılır (platformda frozen olarak loglanır). Düzeltme gerekiyorsa fiscal doğru yol iptal/düzeltme belgesidir — yeni bir key ile gönderin.
  • Doküman yoksa yazılmaz: adisyon o arada kapanmış/silinmişse yazım atlanır, gate kararınız etkilenmez.
  • Fail-safe: yazım hatası gate kararını bozmaz. allow dediyseniz kapanış devam eder.

Aynı adisyona birden fazla eklenti yazarsa

Kapanış gate'i tenant-genel fan-out yapar (packet.close tüm kayıtlı eklentilere paralel sorulur), bu yüzden eşzamanlı yazım normaldir:

DurumSonuç
Farklı key'ler (TSE eklentisi tse.*, sadakat eklentisi loyalty.*)İkisi de yazılır, dizi birleşir
Aynı keyİlk yazan kazanır; ikincinin değeri yok sayılır (değiştirilemezlik)

Yani başka bir eklentinin varlığı sizin öğelerinizi ezmez; siz de onunkini ezemezsiniz. 10 eşzamanlı yazımda kayıp gözlenmedi.

Sınırlar

SınırDeğer
Dizi öğe sayısı30 (adisyon başına toplam)
key uzunluğu64
value uzunluğu2000
label uzunluğu64
typetext | qr
tse.qr için özel not: TSE QR verisi ölçümde 341–363 karakter. Sınır bilinçli olarak yüksek tutuldu; 255 karakterlik bir alan bu veriyi taşırdı ve kırpılan QR doğrulanamaz hale gelirdi. Platform uzun değeri kırpmaz — reddeder: bozuk QR üretmektense öğeyi düşürmeyi tercih eder.

Geliştiricinin bilmesi gerekenler

  1. Geri okuma ucu yok. receiptExtras şu an tables/get / packets/get yanıtlarında dönmez. Yazdığınız belge verisini kendi tarafınızda saklayın. Yeniden basım/denetim sorgusu bu mekanizmanın dışındadır: POS bilgisiyle (kasa, belge no, iş günü) sizden çekilir — platform tarafında ek izin gerekmez.
  2. Yalnız kapanış gate'lerinde anlamlıdır (table.close, packet.close). Kapanış olmayan bir hook'ta gönderirseniz fişle ilişkilendirilecek bir belge yoktur.
  3. Fişe basma frontend'in işidir. Platform veriyi güvenli saklar; şablon tse.qr varsa QR basar, tse.status uyarı durumundaysa uyarı gösterir. Öğe sırası fişteki sırayı garanti etmez — label göndererek anlamı öğenin kendisinde taşıyın.
  4. Timeout bütçesi gate isteğindeki timeoutMs'tir (manifest'ten; table.close varsayılanı 10 sn, paket gate'lerinde 5 sn). Mali belge kesimi bu süreye sığmıyorsa failMode politikanızı gözden geçirin; süre aşımında yanıtınız değerlendirilmez.
  5. Kapatma yetkisi yok — allow dönmek kapanışa izin vermektir, kapanışı sizin yapmanız değil.

Örnek — TSE mali bloğu

Gate isteği → allow + receiptExtras → deny
// Gate isteği (platform → eklenti)
{ "type":"hook", "event":"table.close", "target":{ "type":"table", "id":"masa-5" }, "timeoutMs":5000 }

// Yanıt (eklenti → platform) — belge kesildi, fişe basılacak alanlar döndü
{ "decision":"allow",
  "receiptExtras":[
    { "key":"tse.qr",               "type":"qr",   "value":"V0;KassenSN;Kassenbeleg-V1;…" },
    { "key":"tse.txNumber",         "type":"text", "label":"Beleg-Nr",   "value":"366" },
    { "key":"tse.signatureCounter", "type":"text", "label":"Sig-Zähler", "value":"774" },
    { "key":"tse.timeStart",        "type":"text", "label":"TSE-Start",  "value":"1785438065" },
    { "key":"tse.status",           "type":"text", "value":"signed" }
  ] }

// Ödeme eşlenemedi → belge KESİLMEDİ → hiçbir key yazılmamalı
{ "decision":"deny", "message":"Ödeme TSE ile eşlenemedi" }