T-SOFT/Storefront API/Ürünler

Ürünler

Yetki:storefront:read — herkese açık, token isteğe bağlı. İndirimli fiyat (discountPrice) için storefront:pricing gerekir. Token almak için Kimlik Doğrulama, hata gövdelerinin biçimi için Genel Bakış.

Listeleme

GET /products
curl -s 'https://<domain>/api/v3/storefront/2026-01/products?page[number]=1&page[size]=20' \
  -H 'Accept: application/vnd.api+json' \
  -H 'Authorization: Bearer <TOKEN>'

Yanıt zarfı — kayıtlar data dizisinde, sayfa bilgisi meta.page içinde, gezinme bağlantıları links altında döner:

{
  "meta": {
    "page": {
      "currentPage": 1,
      "from": 1,
      "to": 20,
      "perPage": 20,
      "lastPage": 8,
      "total": 156
    }
  },
  "jsonapi": { "version": "1.0" },
  "links": {
    "first": "https://<domain>/api/v3/storefront/2026-01/products?page[number]=1&page[size]=20",
    "next":  "https://<domain>/api/v3/storefront/2026-01/products?page[number]=2&page[size]=20",
    "last":  "https://<domain>/api/v3/storefront/2026-01/products?page[number]=8&page[size]=20"
  },
  "data": [ /* ürün kayıtları */ ]
}

page[size] verilmezse sayfa boyutu 20'dir. Sonsuz kaydırmada bir sonraki sayfayı links.next üzerinden iste; bu alan son sayfada yer almaz — bitişi böyle tespit et (currentPage === lastPage karşılaştırması da aynı sonucu verir). Toplam kayıt sayısı meta.page.total, o anki dilim from–to aralığıdır.

Örnek bir ürün kaydı (data dizisinin bir elemanı) — tam alan seti:

{
  "type": "products",
  "id": "42",
  "attributes": {
    "name": "Kırmızı Koltuk",
    "slug": "kirmizi-koltuk",
    "seoTitle": "Kırmızı Koltuk | Mağaza",
    "seoDescription": "…",
    "brand": "Nike",
    "brandId": 80,
    "categoryId": 335,
    "ratingAverage": 4.4,
    "commentCount": 3,
    "price":         { "amount": "200.00", "currencyCode": "TRY", "decimalPlaces": 2 },
    "vatIncluded":   true,
    "vatRate":       20,
    "isNew":         true,
    "discountPrice": { "amount": "149.90", "currencyCode": "TRY", "decimalPlaces": 2 },
    "isFeatured": true,
    "isBestseller": false,
    "isOpportunity": true,
    "opportunityEndsAt": 1784286835,
    "availableForSale": true,
    "stock": 12,
    "hasVariants": false,
    "image": "https://<domain>/Data/EcommerceCatalog/…/urun-adi.jpg"
  }
}

Alanlar

AlanTipKural
namestringÜrün adı. Her zaman döner
slugstringSEO bağlantı parçası. Her zaman döner
seoTitlestringSayfa başlığı; tanımlı değilse boş gelebilir
seoDescriptionstringMeta açıklama; tanımlı değilse boş gelebilir
brandstringMarka adı. Markasız üründe boş gelir — marka bağlantısı için brandId kullan
brandIdinteger | nullMarkasız üründe null (0 değil)
categoryIdintegerÜrünün ana kategorisi (tüm kategorileri değil)
ratingAveragenumber0–5, tek ondalık. Yorumu yoksa 0
commentCountintegerYalnızca onaylı yorum sayısı
priceMoneyListe fiyatı. Her zaman döner
discountPriceMoneyYalnız storefront:pricing yetkisi ve aktif indirim varken döner; yoksa alan yanıtta yer almaz
isFeaturedbooleanVitrin/öne çıkan işareti
isBestsellerbooleanÇok satan işareti
isOpportunitybooleanFırsat penceresi şu anda aktif mi (karar sunucuda)
opportunityEndsAtintegerEpoch saniye. Yalnız isOpportunity: true iken döner; yoksa alan yanıtta yer almaz (null değil)
availableForSalebooleanSepete eklenebilir mi. Her zaman döner
stockintegerHam stok adedi. Yalnız storefront:read scope'lu token'da döner; yoksa alan yanıtta yer almaz
hasVariantsbooleantrue ise fiyat alanları varsayılan varyantı gösterir; seçenekler için ?include=variants
imagestringAna görselin tam URL'si. Görsel yoksa boş string (null değil)
symbolsarrayÜründe aktif olan simgeler (rozet/etiket). Aktif simge yoksa boş dizi
attributesarrayÜrünün özellik (filtre) değerleri. Detay isteğinde her zaman döner; listede yalnız fields[products]=attributes ile istenirse döner
orderNoteEnabledbooleanÜrüne sipariş notu yazılabilir mi. Yalnız detay isteğinde döner

Koşullu alanlarda (discountPrice, opportunityEndsAt, stock) null kontrolü değil varlık kontrolü yap — alan yanıtta hiç yer almaz.

symbols semantiği

Mağazanın tanımladığı ürün simgeleri (ör. "Ücretsiz Kargo", "Yeni Sezon"). Liste ve detay yüzeyinde aynı şekilde döner.

"symbols": [
  { "id": 1, "label": "Ücretsiz Kargo", "icon": "https://<domain>/free-shipping.svg" },
  { "id": 4, "label": "Yeni Sezon", "icon": "" }
]
AlanTipKural
idintegerSimge numarası (1–20). Mağaza ayarlarındaki simge sırasıdır
labelstringSimgenin metni. Tanımsızsa boş string
iconstringSimge görselinin adresi. Tanımsızsa boş string
  • Yalnız üründe aktif olan simgeler döner; pasif olanlar listeye girmez.
  • Mağazanın hiç tanımlamadığı (etiketi de görseli de boş) bir simge, üründe aktif olsa bile dönmez — gösterilecek içeriği yoktur.
  • Kullanılabilir simge sayısı mağazanın lisansına bağlıdır; id değerleri bu yüzden aralıklı olabilir (ör. 1, 4, 7).
  • Sıra, simge numarasına göre artan biçimdedir.

attributes semantiği

Ürünün özellik değerleri — ürün detayındaki "Tüm özellikler" bölümünü besler. Tekil ürün isteğinde her zaman döner.

Liste yanıtında alan varsayılan olarak yer almaz; her kayıt için ayrıca okunduğundan listeyi ağırlaştırmasın diye açıkça istenmesi gerekir:

curl "https://<domain>/api/v3/storefront/2026-01/products?fields[products]=name,attributes" \
  -H "Accept: application/vnd.api+json" \
  -H "Authorization: Bearer <access_token>"

fields[products] listesine attributes yazıldığında alan liste kayıtlarında da döner; yazılmazsa hiç görünmez.

"attributes": [
  { "id": 12, "label": "Renk", "values": [
    { "id": 340, "label": "Kırmızı" },
    { "id": 341, "label": "Mavi" }
  ] },
  { "id": 18, "label": "Ağırlık", "values": [
    { "id": null, "label": "2.5" }
  ] }
]
AlanTipKural
idintegerÖzellik grubunun id'si. filter[attr] ile filtrelerken kullanılan grup id'si ile aynıdır
labelstringÖzellik grubunun adı (ör. Renk, Beden)
valuesarrayÜrünün o gruptaki değerleri
values[].idinteger | nullSeçenek id'si. Sayısal özelliklerde null
values[].labelstringGösterilecek değer
  • Çoklu seçimli gruplarda birden çok değer dönebilir; tekli seçimde tek değer döner.
  • Sayısal gruplarda (ör. ağırlık, hacim) values[].id null'dur ve label sayının kendisidir.
  • Mağazanın pasife aldığı özellik grupları dönmez.
  • Değeri artık geçerli bir seçeneğe karşılık gelmeyen grup dönmez; gösterilecek içeriği yoktur.
  • Gruplar mağaza ayarındaki sıraya göre döner.
  • Yalnızca yayında olan ürünler döner. Gizli ürünler listede yer almaz; tekil istekte de 404 verir.
  • availableForSale (boolean) herkese açıktır; stock (integer) ise ham stok adedidir ve yalnızca storefront:read scope'lu token'da döner — bkz. stock semantiği.
  • brandId (integer | null) markanın Markalar kaynağındaki id'sidir. Markası olmayan üründe null döner (0 değil). Marka sayfası akışı: üründeki brandId ile GET /brands/{brandId} marka detayını, GET /products?filter[brandId]={brandId} markanın ürün listesini verir.
  • ratingAverage (number) onaylı yorumlardan hesaplanan puan ortalamasıdır; 0-5 ölçeğinde, 1 ondalık hassasiyetle döner (tam değerler ondalıksız serileşir: 4). Hiç yorumu olmayan üründe 0 döner; yıldız bileşeninin boş durumunu commentCount === 0 üzerinden çizin.
  • commentCount (integer) yalnızca onaylı yorumları sayar ve ?include=comments ile dönen kayıt kümesiyle aynı sayıdadır — bkz. Yorumlar.
  • image her zaman tam URL'dir. Görsel yoksa boş string gelir (null değil).
  • price ürünün gösterilecek fiyatıdır. Ürünün KDV gösterim ayarı açıksa KDV dahil döner; bu durumda vatIncluded true olur ve vatRate uygulanan oranı verir. Ayar kapalıysa fiyat KDV hariçtir ve vatIncluded false döner. Fiyatın üzerine istemci tarafında ayrıca KDV eklenmez.
  • vatIncluded (boolean) price ve discountPrice değerlerine KDV'nin dahil olup olmadığını söyler. vatRate (number) ürünün KDV oranıdır (örn. 20) ve ayar kapalıyken de dolu gelir.
  • isNew (boolean) ürünün "Yeni" olarak işaretlenip işaretlenmediğidir; rozet gösterimi bu alandan çizilir.
  • discountPrice yalnızca storefront:pricing scope'lu token'da ve aktif bir indirim varken yer alır — bkz. Money.
  • isOpportunity (boolean) ürünün şu anda aktif bir fırsat penceresinde olup olmadığını söyler. Karar sunucudadır: fırsat işareti ve başlangıç/bitiş tarihleri sunucu saatiyle değerlendirilir — istemci tarafında tarih karşılaştırması yapmayın.
  • opportunityEndsAt (integer, epoch saniye) fırsatın bitiş zamanıdır ve yalnızca isOpportunity: true iken döner; diğer durumlarda alan yanıtta hiç yer almaz (null değil — varlık kontrolü yapın). Geri sayım bileşenini bu değerden çizin; süre dolduğunda sonraki yanıtta hem isOpportunity false olur hem de alan kaybolur.

Tek ürün

GET /products/{id}
curl -s 'https://<domain>/api/v3/storefront/2026-01/products/42' \
  -H 'Accept: application/vnd.api+json' \
  -H 'Authorization: Bearer <TOKEN>'

Yanıt yapısı listeleme ile aynıdır; data tekil bir obje olarak döner. Gizli veya var olmayan ürün → 404.

Filtreler

FiltreÖrnekDavranış
filter[id][]filter[id][]=42&filter[id][]=43Belirli id'lerle sınırla
filter[categoryId]filter[categoryId]=335 veya filter[categoryId]=335,340Kategori id'si (attributes.categoryId ile aynı değer uzayı) — alt kategoriler dahil, virgülle birden fazla kategori
filter[brand]filter[brand]=NikeMarka adı (string)
filter[brandId]filter[brandId]=80 veya filter[brandId]=80,81Marka id'si — virgülle birden fazla marka
filter[priceMin] / [priceMax]filter[priceMin]=40&filter[priceMax]=60Ham liste fiyatı aralığı
filter[availableForSale]filter[availableForSale]=trueSatılabilirlik
filter[hasDiscount]filter[hasDiscount]=trueİndirimdekiler (boolean). Filtrenin kendisi yetki gerektirmez; discountPrice alanı storefront:pricing ister. Yani storefront:pricing yetkili token'da true listesindeki her üründe discountPrice döner, false listesinde hiçbirinde dönmez; bu yetki olmadan discountPrice hiçbir üründe dönmez ama filtre yine indirimli ürünleri süzer. Süzme, mağazanın ürünü indirimli olarak gösterme işaretine bakar — vitrinde indirimli görünen küme ile bu filtrenin döndürdüğü küme aynıdır
filter[isOpportunity]filter[isOpportunity]=trueFırsat ürünleri (boolean). attributes.isOpportunity ile aynı değer uzayı: true yalnızca aktif fırsat penceresindeki ürünleri döner. Fırsat ekranı = filter[isOpportunity]=true + istenen sort
filter[modelId]filter[modelId]=12 veya filter[modelId]=12,13Model id'si — virgülle birden fazla model
filter[variantType1Id]filter[variantType1Id]=24,26Birinci varyant ekseni (genelde renk); seçenek id'leri virgülle
filter[variantType2Id]filter[variantType2Id]=31İkinci varyant ekseni (genelde beden); seçenek id'leri virgülle
filter[isNew]filter[isNew]=trueYeni ürünler (boolean). attributes.isNew ile aynı değer uzayı
filter[attr]filter[attr][1][]=101Ürün özelliği seçimi — grup id'si ve seçenek id'leri
filter[q]filter[q]=koltukÜrün adında kısmi arama (case-insensitive)

Filtreler birleştirilebilir; birden fazla filtre AND mantığıyla çalışır:

products?filter[categoryId]=335&filter[availableForSale]=true&filter[priceMax]=500&sort=-newest
filter[priceMin] / filter[priceMax]ham liste fiyatı üzerinden çalışır — üye grubuna özel fiyatlar bu aralık hesabına girmez. UI'da "liste fiyatına göre filtrele" olarak konumla.
filter[categoryId] kategori ağacında aşağı iner: verilen kategoriye veya onun herhangi bir alt kategorisine bağlı tüm ürünler döner. filter[categoryId]=335, hem doğrudan 335'teki hem de 335'in alt dallarındaki ürünleri getirir — alt kategori id'lerini ayrıca toplamanıza gerek yoktur. Virgülle birden fazla kategori verilebilir (filter[categoryId]=335,340 → iki ağacın birleşimi). Sayısal olmayan değerler yok sayılır; ürünün attributes.categoryId alanı yine yalnızca ana kategorisini gösterir.
İki marka filtresi var: filter[brand] marka adıyla eşleşir (attributes.brand ile aynı string, yazım farkına duyarlı), filter[brandId] ise marka id'siyle — Markalar kaynağının döndüğü id değerini doğrudan kullanabilir, virgülle birden fazla marka geçebilirsin. Sağlam eşleşme için filter[brandId] tercih edilir.
filter[isOpportunity] ile filter[hasDiscount]bağımsız kümelerdir: fırsat ürünü indirimli olmak zorunda değildir, indirimli ürün fırsat olmak zorunda değildir. İkisi birlikte gönderilirse kesişim döner.

Sıralama

sort= parametresi. Alan adının başına - koymak azalan sırayı verir. sort verilmezse kayıtlar id sırasıyla döner; sayfalar arasında tutarlı bir sıra istiyorsan açık bir sort gönder.

SortAnlamı
name / -nameÜrün adı (alfabetik)
price / -priceÜrünün gösterilen fiyatı — üye grubuna özel fiyat veya indirim uygulanmışsa sıralama da onun üzerinden yapılır
-newestEn yeni eklenen
-bestsellerÇok satılan sırası
-ratePuan — sıralanan değer yanıtta attributes.ratingAverage alanında döner
-viewCountEn çok görüntülenen
-recommendationCountEn çok tavsiye edilen
-commentCountEn çok yorumlanan
-favoriteCountEn çok favoriye eklenen
randomRastgele sıra
positionMağazanın elle verdiği sıra
sort=price ile filter[priceMin] / filter[priceMax]aynı fiyatı kullanmaz: sıralama gösterilen (indirimli veya gruba özel) fiyata, aralık filtresi ham liste fiyatına bakar. Fiyat aralığı seçip fiyata göre sıralayan bir ekranda bu fark görünür olabilir.
sort=name dizilişi veritabanının karşılaştırma kurallarına göredir; Türkçe'ye özel alfabetik sıra garanti edilmez (ç c-bloğunda dizilir, ı/i ayrışmaz). Kesin Türkçe sıra gerekiyorsa istemci kendi tarafında sıralamalıdır.
sort=discountPricedesteklenmez — fiyat alanları obje taşıdığı için doğrudan sıralanamaz. Eski koddan kaldıysa temizle.

Include'lar

İlişkili kayıtlar ?include= ile aynı yanıtta (included[] dizisinde) istenir; hem listelemede hem tek üründe çalışır, virgülle birden fazla ilişki verilebilir:

products/42?include=images,variants
products?filter[categoryId]=335&include=images
İlişkiNe dönerNotlar
imagesÜrün galerisi (product-images): url, mediumUrl, smallUrl, sortOrderÜrün kartındaki görsel kaydırma için listelemede de kullanılabilir; sortOrder galeri sırasıdır
variantsVaryantlar (variants): productId, sku, barcode, options, inStock, pricediscountPrice burada da storefront:pricing yetkisine bağlıdır
commentsOnaylı yorumlar (product-comments) — bkz. YorumlarTamamı döner, ayrıca sayfalanmaz
relatedProductsİlişkili/alternatif ürünler (yine products tipinde)Ayrı bir adet alanı yoktur; sayı gerekiyorsa dönen listenin uzunluğunu sayın

Varyantlar included[] içinde şu biçimde döner — options bir {name, value} dizisidir, varyant seçiciyi bu diziden kur:

{
  "type": "variants",
  "id": "803",
  "attributes": {
    "productId": 773,
    "sku": "",
    "barcode": "",
    "options": [
      { "name": "Renk", "value": "Mavi" },
      { "name": "Beden", "value": "M" }
    ],
    "inStock": true
  }
}

Varyantta satılabilirlik alanı inStock'tur; üründeki availableForSale ile aynı soruyu yanıtlar, adlandırma farkı kaynak tipinden gelir. sku ve barcode tanımlı değilse boş string döner.

Include derinliği 1'dir — include=relatedProducts.images gibi zincirleme yol desteklenmez.

availableForSale semantiği

availableForSale: true şu koşulda döner:

Mağaza backorder'a (eksi stoğa satış) açık veya güncel stok > 0

Yani bu alan "sepete eklenebilir mi?" sinyalidir; "depoda kaç adet var?" değil. Buton ve rozet mantığını doğrudan bu boolean'a bağla. availableForSale herkese açıktır — storefront:read scope'u olmayan anonim istekte de döner.

Ham stok adedi (stock) ayrı bir alandır ve yetkiye bağlıdır — bkz. stock semantiği.

stock semantiği

stock ürünün ham stok adedidir (kaç adet kaldı) ve yetkiye bağlıdır: yalnızca storefront:read scope'lu bir token ile yapılan isteklerde döner. Token gönderilmeyen (anonim) veya bu scope'u taşımayan isteklerde alan yanıtta hiç yer almaz (null değil — varlık kontrolü yapın).

{ "stock": 12, "availableForSale": true }
  • stock ve availableForSale farklı sorulara yanıt verir: availableForSale "sepete eklenebilir mi?" (herkese açık boolean, backorder ayarını da hesaba katar), stock ise "depoda kaç adet var?" (yetkili ham sayı). Vitrin butonunu availableForSale'e, "son N ürün" gibi adet gösterimlerini stock'a bağlayın.
  • Anonim vitrinde stock alanı gelmeyeceği için, bu değere bağlı bileşenleri yalnızca scope'lu token ile çalışan istemciler çizmelidir.