Fiziksel POS
Ortak Kurallar

Ortak Kurallar

Bu sayfadaki kurallar Fiziksel POS yönetim API'sinin bütün uçlarında geçerlidir. Uç sayfaları bu kurallara bağlantı verir.

Yanıt zarfı

Başarılı yanıtta veri data altındadır. Bu API'de tek kayıt dönen her uç kaydı varlık adıyla sarar; ilişkili veriler kardeş alanlarda durur:

Uç türüdata biçimiÖrnek
Tek kayıt okuma, güncelleme, durum değişikliği{ <varlık> } ve varsa ilişkili alanlar{ "store": { … } }, { "terminal": { … }, "acquirers": { … } }
Oluşturma{ <varlık>, replayed }{ "register": { … }, "replayed": false }
Liste{ items, pageInfo }{ "items": [ … ], "pageInfo": { … } }
Katalog listesiDizi[ { … }, { … } ]
{
  "status": "success",
  "code": "SUCCESS",
  "data": {
    "store": {
      "id": "00000000-0000-0000-0000-000000000101",
      "code": "IST-KADIKOY",
      "name": "Kadıköy Mağazası",
      "storeStatus": "ACTIVE",
      "rowVersion": 1
    },
    "replayed": false
  },
  "statusCode": 201,
  "requestId": "00000000-0000-0000-0000-000000000000"
}

Örnekte mağaza nesnesi kısaltılmıştır; yanıt bütün alanları içerir.

  • Oluşturma uçları yeni kayıtta 201, aynı Idempotency-Key ile yapılan tekrarda 200 döner. → Idempotency-Key
  • Liste uçlarında pageInfo zarfın üst düzeyinde de bulunur; iki kopya aynıdır.
  • Örneklerde kısalık için gösterilmeyen zarf alanları (message, timestamp …) da yanıtta bulunur. Zarfın bütün alanları ve hata zarfı: Hata Yönetimi.
  • Hata durumunda message'a göre değil code'a göre dallanın. Bu ürünün kodları: Hata Kodları.
  • physical_pos.* hatalarında yanıt docsUrl alanı taşır; bağlantı, Hata Kodları sayfasında o kodun satırına gider.
  • Hata ayrıntısı details içindedir: tek alanlı hatalarda details.field ve details.reason; birden çok alanı kapsayan doğrulama hatasında (physical_pos.validation.invalid) details.errors dizisi, her öğe { field, errors: [<sebep>] }. Gönderdiğiniz değer details'te yankılanmaz.

Alan kuralları

KuralAyrıntı
KimliklerBütün kimlikler (id, storeId, terminalId …) UUID'dir. Yoldaki biçim dışı kimlik, olmayan kayıtla aynı 404 cevabını alır.
ZamanlarISO 8601, UTC (2026-01-01T07:00:00.000Z).
Boş değerİsteğe bağlı bir alanın değeri yoksa alan yanıtta null olarak bulunur.
Durum alanlarıDurum alanları varlığa göre adlandırılır: storeStatus, registerStatus, terminalStatus, acquirerStatus, lifecycleStatus.
Bilinmeyen alanTanımlanmamış gövde alanı ya da sorgu parametresi her uçta 400 VALIDATION_ERROR döner.
OrtamSorguda ya da gövdede environment gönderilirse istek 400 physical_pos.input.environment_invalid alır (details.reason: not_allowed). Organizasyon ve ortam yalnız anahtarınızdan çözülür. → Ortamlar
Boş güncellemeDeğişecek alan içermeyen PATCH isteği 400 physical_pos.input.empty_patch alır. Değerleri mevcutla aynı olan PATCH hata değildir: 200 döner, kayıt değişmez, rowVersion artmaz ve değişiklik geçmişine olay yazılmaz.
Liste süzgeçleriGeçersiz süzgeç değeri (küme dışı durum, biçim dışı kimlik ya da kod, kurala uymayan ya da 64 karakteri aşan q) 400 physical_pos.input.filter_invalid alır; details.field süzgecin adını verir. Değer kümeleri büyük/küçük harfe duyarlıdır.

Sayfalama

Liste uçları cursor tabanlı sayfalama kullanır:

ParametreAçıklama
limitSayfa başına kayıt, 1–100. Varsayılan 25. Aralık dışı değer 400 VALIDATION_ERROR döner.
cursorÖnceki yanıttaki pageInfo.nextCursor. İlk sayfada gönderilmez.
includeTotalKayıt listelerinde (mağazalar, kasalar, terminaller, banka satırları, cihazlar). true ise pageInfo.totalCount doldurulur. Geçmiş listeleri (…/audit, …/assignments, …/links) bu parametreyi almaz.
"pageInfo": { "hasMore": true, "nextCursor": "eyJ0IjoiMjAyNi0…", "totalCount": null }
  • Sıralama her listede en yeni kayıt önce (createdAt azalan, eşitlikte id azalan) ve değiştirilemez.
  • pageInfo.hasMore true ise sonraki sayfayı cursor=<nextCursor> ile isteyin; son sayfada nextCursor null'dır.
  • totalCount, includeTotal=true göndermediğiniz sürece null'dır. Toplam sayı hesaplaması maliyetlidir; ilerlemek için hasMore yeterlidir.
  • İmleci kendiniz üretmeyin ve değiştirmeyin. Bozuk imleç 400 physical_pos.cursor.invalid döner; ilk sayfadan yeniden başlayın.
  • Süzgeçleri değiştirdiğinizde imleci sıfırlayın.

Katalog uçları sayfalanmaz; bütün liste tek yanıtta dizi olarak döner.

Idempotency-Key

Kayıt oluşturan uçlar, ağ hatasından sonra güvenle tekrar deneyebilmeniz için Idempotency-Key başlığı ister:

Uçİşlem
POST /storesMağaza oluşturma
POST /stores/{storeId}/registersKasa oluşturma
POST /terminalsMantıksal terminal oluşturma
POST /terminals/{terminalId}/acquirersBanka satırı ekleme
POST /terminals/{terminalId}/device-link/closeCihaz bağını çözme
POST /devicesCihaz kaydetme
POST /devices/{deviceId}/assignment/closeCihazı depoya alma
POST /devices/{deviceId}/retireCihazı emekliye ayırma

Biçim: 8–200 karakter, boşluksuz yazdırılabilir ASCII. Değer kırpılmaz ve düzeltilmez. Başlık yoksa 400 physical_pos.idempotency.key_required, biçim dışıysa 400 physical_pos.idempotency.key_invalid.

Davranış:

DurumSonuç
Yeni anahtarİşlem yapılır; oluşturma uçlarında 201, replayed: false.
Aynı anahtar, aynı uç, aynı gövdeİşlem tekrarlanmaz; ilk işlemin kaydı güncel hâliyle döner, 200, replayed: true.
Aynı anahtar, farklı gövde409 physical_pos.idempotency_mismatch. Yeni kayıt oluşmaz.
Aynı anahtar, başka bir işlem (ör. önce mağaza, sonra terminal oluşturma)409 physical_pos.idempotency_mismatch.
  • Gövdeler, alan kuralları uygulandıktan sonraki hâlleriyle karşılaştırılır; alanların sırası fark etmez.
  • Kasa oluşturma ve banka satırı ekleme gibi bir kayda bağlı işlemlerde yol parametresi de karşılaştırmaya girer: aynı anahtarı başka bir mağaza için kullanmak 409 alır.
  • Bir anahtar, anahtarınızın ortamında yalnız bir işlem için kullanılabilir ve süresiz saklanır. Kullanılmış bir anahtarı yeni bir işlem için yeniden kullanmayın.
  • Test ve canlı anahtarların değerleri birbirinden ayrı tutulur.

Her yeni işlem için yeni bir UUID kullanın ve anahtarı işlemle birlikte saklayın. Zaman aşımı ya da bağlantı kopması sonrası aynı anahtar ve aynı gövdeyle yeniden deneyin; işlem ilk denemede tamamlandıysa replayed: true ile aynı kaydı alırsınız.

Diğer yazma uçları (PATCH, arşivleme, yeniden açma, terminal kapatma, cihaz atama, cihaz değiştirme) Idempotency-Key kullanmaz; bu uçlarda başlık dikkate alınmaz. Bu uçları rowVersion korur: işlem ilk denemede tamamlandıysa tekrar 409 physical_pos.version_conflict alır (terminal kapatmada 409 physical_pos.terminal.closed). Bu durumda kaydı okuyun ve değişikliğin uygulanıp uygulanmadığını kontrol edin.

Eşzamanlılık: rowVersion

Değiştirilebilen her kayıt (mağaza, kasa, mantıksal terminal, banka satırı) bir rowVersion tam sayısı taşır. Kaydı değiştiren isteklerde son okuduğunuz rowVersion değerini gövdede gönderirsiniz:

{ "rowVersion": 3, "name": "Kadıköy Mağazası (Yeni)" }
  • rowVersion zorunludur; 1 ile 2147483647 arasında bir JSON tam sayısıdır. Dize ("3") gönderirseniz istek 400 VALIDATION_ERROR alır.
  • Kayıt siz okuduktan sonra başka bir istekle değiştiyse istek 409 physical_pos.version_conflict alır ve hiçbir değişiklik yapılmaz. Kaydı yeniden okuyun, değişikliğinizi güncel hâline uygulayın ve yeni rowVersion ile gönderin.
  • Başarılı her değişiklikten sonra yanıttaki güncel rowVersion değerini kullanın.
  • Banka satırı güncellemesi satırın kendi rowVersion değerini, cihaz bağı işlemleri terminalin rowVersion değerini, cihaz işlemleri cihazın rowVersion değerini ister.

Kod kuralları

Mağaza, kasa ve terminal kodları kanonik biçime çevrilerek saklanır:

AlanKuralÖrnek
code (mağaza, kasa, terminal)Baştaki ve sondaki boşluk kırpılır, büyük harfe çevrilir. Yalnız A-Z, 0-9, ., _, -; harf ya da rakamla başlar; en çok 64 karakter.ist-kadikoy → IST-KADIKOY
vendorCodeBüyük harfe çevrilir. Harfle başlar, yalnız A-Z ve 0-9, 2–32 karakter. Katalogdaki bir üreticinin kodu olmalıdır.pavo → PAVO
externalRef (mağaza)Kendi sisteminizdeki kimlik. Boşluksuz yazdırılabilir ASCII, en çok 128 karakter. Kırpılmaz, büyük/küçük harf korunur.store_00042
  • ASCII dışı karakter içeren kod reddedilir, dönüştürülmez: KADIKÖY kabul edilmez, KADIKOY yazın.
  • Biçim dışı kod 400 physical_pos.input.code_invalid, biçim dışı üretici kodu 400 physical_pos.input.vendor_code_invalid, biçim dışı dış referans 400 physical_pos.input.ref_invalid alır.
  • Kodlar oluşturulduktan sonra değişmez.

Metin kuralları

Serbest metin alanlarına (name, displayName, city, district, addressLine, label, arama parametresi q) şu kurallar uygulanır:

  1. Metin Unicode NFC biçimine getirilir, baştaki ve sondaki boşluk kırpılır.
  2. Kırpıldıktan sonra boş kalan metin reddedilir.
  3. Şu karakterler reddedilir: kontrol karakterleri, sıfır genişlikli ve görünmez karakterler, yazı yönünü değiştiren karakterler, normal boşluk dışındaki boşluk karakterleri (bölünemez boşluk vb.), varyasyon seçicileri ve etiket karakterleri.
  4. Uzunluk karakter (Unicode kod noktası) sayısıyla ölçülür; sınırlar her alanın tablosunda yazar.

Kurala uymayan metin düzeltilmez, istek 400 physical_pos.input.text_invalid alır. details.field alan adını, details.reason sebebi verir. Liste aramasındaki (q) aynı ihlal 400 physical_pos.input.filter_invalid alır.

details.reasonAnlamı
not_stringDeğer metin değil.
emptyKırpıldıktan sonra boş.
malformed_unicodeGeçersiz Unicode dizisi.
forbidden_characterYasak karakter içeriyor.
too_longAlanın sınırını aşıyor.

Türkçe karakterler ve emoji gibi görünür karakterler serbest metinde kullanılabilir.

Saat dilimi

Mağazanın timezone alanı bir IANA saat dilimi adıdır (ör. Europe/Istanbul). Gönderilmezse Europe/Istanbul kullanılır.

  • Kabul edilen adlar IANA saat dilimi veritabanının (tzdb) bölge adları ve UTC'dir. Ad, büyük/küçük harfiyle birebir yazılmalıdır.
  • Eski ya da yeniden adlandırılmış adlar güncel adlarına çevrilerek saklanır; yanıtta her zaman güncel ad döner:
GönderilenSaklanan
Europe/KievEurope/Kyiv
Asia/CalcuttaAsia/Kolkata
Asia/IstanbulEurope/Istanbul
Etc/UTC, Universal, ZuluUTC
  • Tanınmayan adlar ile tek bir bölgeye karşılık gelmeyen eski adlar ve kısaltmalar (EET, HST, Turkey gibi) reddedilir: 400 physical_pos.validation.invalid, details.errors[0] = { "field": "timezone", "errors": ["iana_unknown"] }.

Gerekçe kodları

Arşivleme, yeniden açma, terminal kapatma, cihaz bağı ve cihaz işlemleri bir reasonCode ister. Kodlar sabit listelerdendir, büyük harfle ve birebir yazılır; listede olmayan ya da işleme uymayan kod 400 physical_pos.input.reason_code_invalid alır.

İşlemKabul edilen kodlar
Mağaza arşivlemeSTORE_CLOSED, TEMPORARILY_CLOSED, RELOCATED, MERGED, CREATED_IN_ERROR, DATA_CORRECTION, OTHER
Kasa arşivlemeSTORE_ARCHIVED, REGISTER_REMOVED, REGISTER_REPLACED, STORE_CLOSED, TEMPORARILY_CLOSED, CREATED_IN_ERROR, DATA_CORRECTION, OTHER
Mağaza ya da kasa yeniden açmaREOPENED, ARCHIVED_IN_ERROR, DATA_CORRECTION, OTHER
Terminal kapatmaMERCHANT_REQUEST, CONTRACT_ENDED, BANK_TERMINATED, CREATED_IN_ERROR, DUPLICATE_RECORD, MIGRATED, OTHER
Terminale ilk cihazı bağlamaINITIAL_SETUP, RELOCATION, CORRECTION, OTHER
Bağlı cihazı değiştirmeDEVICE_REPLACEMENT, DEVICE_FAULT, DEVICE_LOST, DEVICE_STOLEN, RELOCATION, CORRECTION, OTHER
Cihaz bağını çözmeDEVICE_REPLACEMENT, DEVICE_FAULT, DEVICE_LOST, DEVICE_STOLEN, RELOCATION, CORRECTION, OTHER
Cihaz atama, depoya alma, emeklilikCihazlar sayfasındaki tablolar
  • Mağaza arşivlendiğinde aktif kasaları aynı işlemde arşivlenir; bu kasaların değişiklik geçmişine STORE_ARCHIVED kodu yazılır, mağazanın gerekçesi changes.storeReason alanında durur.
  • OTHER yalnız listede karşılığı olmayan durumlar içindir.

Oran sınırı

Yazma uçlarında (POST ve PATCH), organizasyonunuz ve API anahtarınız başına 60 saniyelik bir pencere uygulanır. Bir uç ailesinin bütün yazmaları aynı sayacı paylaşır; okumalar sınırlanmaz:

Uç grubu60 saniyede en çok
Mağaza ve kasa yazmaları (oluşturma, güncelleme, arşivleme, yeniden açma)60 istek
Terminal ailesi yazmaları (terminal, banka satırı, cihaz bağı)30 istek
Cihaz yazmaları (kaydetme, güncelleme, atama, depoya alma, emeklilik)30 istek

Sınır aşılırsa istek 429 RATE_LIMITED alır; Retry-After başlığı ve details.retryAfterSeconds beklemeniz gereken saniyeyi verir. Platform geneli sınırlar: Hız Sınırları.

Değişiklik geçmişi

Mağaza, kasa ve terminalde yapılan her değişiklik, işlemle aynı anda değişiklik geçmişine yazılır. Geçmişi GET …/audit uçlarından sayfalı okursunuz.

AlanAçıklama
idOlayın kimliği.
actionOlayın türü, physical_pos.audit.<varlık>_<eylem> biçiminde (ör. physical_pos.audit.store_archived).
actorTypeİşlemi yapan: USER (panel kullanıcısı), API_KEY (API anahtarı) ya da platform tarafı işlemlerde OPERATOR / SYSTEM.
actorIdPanel kullanıcısında auth-user:<kullanıcı kimliği>, API anahtarında kid:<anahtar kimliği>.
reasonCodeİşlemin gerekçe kodu; gerekçe istemeyen işlemlerde null.
changesDeğişikliğin özeti. Yalnız tanımlı alanları ve basit değerleri taşır; ad ve adres gibi serbest metinlerin değeri yazılmaz, yalnız değiştiği bilgisi yazılır.
createdAtOlay zamanı.

Mağaza, kasa ve cihaz olaylarında ayrıca requestId (değişikliği yapan isteğin kimliği), terminal olaylarında entityType, entityId ve environment bulunur.

Yeniden deneme

  • 4xx hatalar yeniden denemeyle düzelmez; isteği düzeltin.
  • 503 physical_pos.service.busy (details.retryable: true) kaydın o an başka bir işlemle meşgul olduğunu söyler; kısa bir beklemeden sonra aynı istekle yeniden deneyin.
  • Diğer 5xx hatalarda geri çekilerek yeniden deneyin. Oluşturma isteklerinde aynı Idempotency-Key'i kullanın.