- Site Adı: FShop E-ticaret
- Site Adresi: https://fyazilim.com/fshop/
- Açıklama: Türkiye'nin açık kaynak ücretsiz ve kolay geliştirilebilir e ticaret yazılımı
- Açıldığı Tarih: Temmuz 2026
FShop Açık Kaynak Ücretsiz E-Ticaret Yazılımı
Tüm Eklentiler
Bu portal, FriSay (FShop) e-ticaret platformunu entegre etmek, Web API ile ürün/sipariş senkronize etmek, vitrin AJAX uçlarını kullanmak ve modül / tema geliştirmek için dokümantasyon sağlar.
Nereden başlamalı?
- Hızlı Başlangıç — uçtan uca partner entegrasyon akışı
- API Kuralları — sayfalama, hatalar, rate limit
- Kimlik Doğrulama — API anahtarı ve izinler
- Endpoint sayfaları — ürünler, katalog, siparişler
- Modül / Tema — platformu genişletme
Nasıl kullanılır?
- FriSay yönetim panelinde API bölümüne gidin.
- Web API özelliğini etkinleştirin ve bir anahtar oluşturun (izinleri iş ortağına göre ayarlayın).
- Her istekte anahtarı
X-API-Keybaşlığıyla gönderin. - PHP ve cURL örnekleri için ilgili endpoint sayfalarını takip edin.
Temel URL
| Ortam | URL |
|---|---|
| Canlı | https://yoursite.com/api/v1/ |
| Yerel | http://localhost/fshop/api/v1/ |
Bu portalda neler var?
| Bölüm | İçerik |
|---|---|
| Başlangıç | Hızlı başlangıç, kurallar, kimlik doğrulama |
| Ürünler / Katalog / Siparişler | Partner Web API (/api/v1) |
| Vitrin ve Operasyon | Tema AJAX API, cron / döviz / pazaryeri |
| Geliştiriciler | Modül, ödeme modülü, hook, sabitler, tema |
Genel Bilgiler
- Canlı ortamda HTTPS tercih edin.
- İstek / yanıt formatı JSON (
UTF-8) şeklindedir. - POST/PATCH isteklerinde
Content-Type: application/jsongönderin. - API sürümü: v1
- Ürün sayfası 1 tabanlı; sipariş sayfası 0 tabanlıdır — ayrıntılar için API Kuralları.
HTTP Durum Kodları
| Kod | Anlam |
|---|---|
200 | Başarılı |
201 | Oluşturuldu (ör. yeni ürün / görsel) |
400 | Geçersiz istek |
403 | API kapalı veya geçersiz anahtar |
404 | Kayıt bulunamadı |
422 | Doğrulama hatası |
429 | Rate limit aşıldı |
503 | API anahtarı yapılandırılmamış |
Test Sitesi
https://fyazilim.com/fshop/admin
User: admin@fyazilim.com
Pass: admin123
API Key: Admin → API
Bu rehber, iş ortağının FriSay Web API ile uçtan uca entegrasyonunu adım adım özetler: API anahtarı, katalog, ürün, sipariş, kargo ve fatura.
https://fyazilim.com/fshop/admin — kullanıcı admin@fyazilim.com / admin123. API anahtarını Yönetim → API bölümünden alın. Ayrıntılar için Giriş sayfasına bakın.Adım adım
- Yönetim panelinde Web API özelliğini etkinleştirin ve uygun kapsamlarla (ürün / sipariş okuma-yazma vb.) bir anahtar oluşturun. Bkz. Kimlik Doğrulama.
- Kategori ve marka listelerini çekin; ürün oluştururken ID veya ad kullanabilirsiniz. Bkz. Kategorileri Listele, Markaları Listele.
- Ürün oluşturun (ad, kategori, marka, fiyat zorunlu), ardından görsel yükleyin. Bkz. Ürün Ekle, Görsel Yükle, Ürünleri Listele.
- Siparişleri periyodik olarak sorgulayın (
page0 tabanlıdır); durum ve tarih filtrelerini kullanın. Bkz. Siparişleri Listele. - Kargoya verirken durumu güncelleyin ve kargo firması + takip numarası gönderin. Bkz. Sipariş Durumunu Güncelle.
- Sevkiyattan sonra herkese açık fatura URL'sini siparişe ekleyin. Bkz. Sipariş Faturası.
Önerilen akış
| Sıra | İşlem | Endpoint / sayfa |
|---|---|---|
| 1 | API anahtarı + kapsamlar | authentication |
| 2 | Kategori / marka keşfi | GET /categories, GET /brands |
| 3 | Ürün oluştur + görsel | add-product → POST /products/{id}/image |
| 4 | Siparişleri tara (poll) | list-orders (page=0) |
| 5 | Durum + kargo takibi | update-order-status |
| 6 | Fatura URL'si | order-invoice |
İlgili sayfalar
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
function apiRequest($method, $url, $apiKey, $data = null)
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
// 1) List categories (0-based page)
$categories = apiRequest('GET', $baseUrl . '/categories?page=0&size=50&active=1', $apiKey);
// 2) Create a simple product
$created = apiRequest('POST', $baseUrl . '/products', $apiKey, [
'name' => 'API Demo Product',
'category' => 'Test Category',
'brand' => 'Test Brand',
'price' => 149.90,
'stock' => 20,
'active' => 1,
'stock_code' => 'DEMO-001',
]);
$productId = (int) ($created['response']['data']['id'] ?? 0);
// 3) List recent picking orders (0-based page)
$orders = apiRequest(
'GET',
$baseUrl . '/orders?page=0&size=20&status=2&date_from=2026-06-01',
$apiKey
);
print_r([
'categories_http' => $categories['http_code'],
'product_id' => $productId,
'orders_http' => $orders['http_code'],
]);# 1) Smoke test — categories
curl -s -H "X-API-Key: YOUR_API_KEY" -H "Accept: application/json" \
"https://example.com/api/v1/categories?page=0&size=20"
# 2) Create product
curl -s -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"name": "API Demo Product",
"category": "Test Category",
"brand": "Test Brand",
"price": 149.90,
"stock": 20,
"active": 1,
"stock_code": "DEMO-001"
}' \
"https://example.com/api/v1/products"
# 3) List picking orders (page is 0-based)
curl -s -H "X-API-Key: YOUR_API_KEY" -H "Accept: application/json" \
"https://example.com/api/v1/orders?page=0&size=20&status=2&date_from=2026-06-01"
# 4) Mark shipped + cargo (replace 15 with order id)
curl -s -X PATCH \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status":3,"cargoCompany":"Yurtiçi Kargo","trackingNumber":"YT123456789"}' \
"https://example.com/api/v1/orders/15"
# 5) Attach invoice URL
curl -s -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/fatura.pdf","name":"Invoice"}' \
"https://example.com/api/v1/orders/15/invoice" FriSay Web API v1 için ortak kurallar: URL, JSON zarfı, hata biçimi, HTTP kodları, hız limiti, sayfalama ve alan takma adları.
Temel URL ve içerik tipi
- Canlı:
https://yoursite.com/api/v1/ - Yerel örnek:
http://localhost/fshop/api/v1/ - İstek / yanıt: UTF-8 JSON
- POST / PUT / PATCH gövdelerinde
Content-Type: application/jsongönderin - Kimlik doğrulama:
X-API-Key(önerilen), Bearer veya?api_key=— bkz. Kimlik Doğrulama
Başarı zarfı
Çoğu yanıt şu yapıya yakındır:
{
"success": true,
"data": { },
"content": [ ],
"message": "",
"meta": { }
}Sipariş listesi Trendyol benzeri alanlar da döndürebilir (totalElements, content[]). Ürün oluşturma gibi işlemlerde HTTP durumu 201 olabilir.
Hata gövdesi
{
"success": false,
"message": "Açıklayıcı hata metni"
}HTTP durum kodları
| Kod | Anlam |
|---|---|
200 | Başarılı |
201 | Oluşturuldu (ürün, görsel vb.) |
400 | Geçersiz istek |
403 | API kapalı veya geçersiz / yetkisiz anahtar |
404 | Kayıt bulunamadı |
405 | Yöntem desteklenmiyor |
422 | Doğrulama hatası |
429 | Hız limiti aşıldı |
503 | API anahtarı yapılandırılmamış / servis kullanılamıyor |
Hız limiti
- 300 başarılı istek / 15 dakika / IP
- Başarısız kimlik doğrulama kilidi: yaklaşık 30 başarısız deneme / 15 dakika
429 aldığınızda geri çekilip yeniden deneyin; anahtarı agresifçe denemeyin.
Sayfalama
- Ürünler (
GET /products): sayfa numarası 1 tabanlıdır (ilk sayfapage=1). - Siparişler, kategoriler, markalar: sayfa numarası 0 tabanlıdır (ilk sayfa
page=0).
_method geçersiz kılma
Bazı istemciler veya proxy'ler PUT / PATCH / DELETE gönderemez. Gerektiğinde POST gövdesinde veya form alanında _method=PUT, _method=PATCH veya _method=DELETE kullanarak gerçek HTTP yöntemini belirtebilirsiniz.
Alan takma adları
Özellikle kargo alanlarında hem camelCase hem snake_case kabul edilir:
cargoCompany/cargo_companytrackingNumber/tracking_number
Sipariş tarih filtrelerinde date_from / date_to veya milisaniye cinsinden startDate / endDate kullanılabilir.
Webhook yok
İlgili sayfalar
{
"success": true,
"data": [],
"meta": {
"total": 5,
"page": 1,
"limit": 10,
"pages": 1
}
}{
"success": false,
"message": "Geçersiz API anahtarı"
} https://example.com/api/v1/Her Web API isteğinde API anahtarınız bulunmalıdır. Anahtarları Yönetim → API bölümünden oluşturun.
Desteklenen Yöntemler (birini kullanın)
| Yöntem | Örnek |
|---|---|
| Başlık (önerilen) | X-API-Key: YOUR_API_KEY |
| Bearer token | Authorization: Bearer YOUR_API_KEY |
| Query string | ?api_key=YOUR_API_KEY |
İzin matrisi
| İzin anahtarı | Açıklama | Endpoint örnekleri |
|---|---|---|
orders.read | Siparişleri oku / çek | GET /orders, GET /orders/{id} |
orders.write | Sipariş güncelle / fatura | PATCH /orders/{id}, invoice |
products.read | Ürünleri oku | GET /products, GET /products/{id} |
products.create | Ürün ekle | POST /products |
products.update | Ürün / stok / görsel | PATCH, /quick, /image |
products.delete | Ürün sil | DELETE /products/{id} |
categories.read | Kategorileri oku | GET /categories |
brands.read | Markaları oku | GET /brands |
Önerilen anahtar senaryoları
- ERP / stok:
products.read+products.update(+ isteğe bağlı create) - Pazaryeri / kargo:
orders.read+orders.write - Salt okunur rapor: yalnızca
*.readizinleri
Rate limit
Başarılı istekler IP başına yaklaşık 300 / 15 dakika ile sınırlıdır. Başarısız kimlik doğrulama denemeleri ayrı olarak kilitlenebilir. Aşımda 429 dönülebilir — ayrıntılar için API Kuralları.
Sık karşılaşılan hatalar
| Durum | Olası neden |
|---|---|
403 Web API kapalı | Admin → API'de Web API kapalı |
403 Geçersiz anahtar / yetki | Yanlış key veya eksik permission |
503 | Hiç API anahtarı tanımlı değil |
/api/cart.php vb.) API Key kullanmaz; müşteri oturumu + CSRF kullanır. Bkz. Vitrin AJAX API. Cron işleri SHOP_TOKEN kullanır — Cron.<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY'; // Admin → API
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
// Connection check — categories
$result = apiRequest('GET', $baseUrl . '/categories', $apiKey);
print_r($result);curl -s -H "X-API-Key: YOUR_API_KEY" \
-H "Accept: application/json" \
"https://example.com/api/v1/categories"{
"totalElements": 4,
"content": [
{
"id": 3,
"name": "Vitamin & Takviye",
"slug": "vitamin-takviye",
"parentId": 1,
"parentName": "Sağlık",
"active": true
}
]
} https://example.com/api/v1/productsSayfalanmış ürün listesini döndürür. Sayfa numaralandırması 1 tabanlıdır (siparişlerden farklı olarak).
Query Parametreleri
| Parametre | Tip | Zorunlu | Açıklama |
|---|---|---|---|
page | integer | Hayır | Sayfa numarası (varsayılan: 1) |
limit | integer | Hayır | Sayfa başına kayıt (varsayılan: 30, en fazla: 100) |
q | string | Hayır | Ad, stok kodu veya barkod ile arama |
category | integer | Hayır | Kategori ID'sine göre filtrele |
brand | integer | Hayır | Marka ID'sine göre filtrele |
active | integer | Hayır | 1 aktif, 0 pasif, -1 tümü (varsayılan) |
lang | string | Hayır | Dil kodu (ör. tr, en) |
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
$result = apiRequest(
'GET',
$baseUrl . '/products?page=1&limit=20&q=vitamin&active=1',
$apiKey
);
print_r($result);curl -s -H "X-API-Key: YOUR_API_KEY" \
-H "Accept: application/json" \
"https://example.com/api/v1/products?page=1&limit=20&q=vitamin&active=1"{
"success": true,
"lang": "tr",
"data": [
{
"id": 5,
"name": "Multivitamin Complex 60 Tablet",
"slug": "multivitamin-complex-60",
"url": "https://example.com/multivitamin-complex-60-5",
"price": 329,
"old_price": 399,
"stock": 200,
"in_stock": true,
"active": 1,
"barcode": "8690000000059",
"stock_code": "DEMO-MULTI60",
"id_category": 3,
"category_name": "Vitamin & Takviye",
"id_brand": 3,
"brand_name": "FShop Demo",
"image_url": "https://example.com/img/products/5.jpg",
"has_variations": false
}
],
"meta": {
"total": 1,
"page": 1,
"limit": 20,
"pages": 1
}
} https://example.com/api/v1/products/{id}Tek bir ürünü tam detaylarıyla döndürür: açıklamalar, görseller, varyasyonlar ve çeviriler.
URL Parametreleri
| Parametre | Tip | Açıklama |
|---|---|---|
id | integer | Ürün ID |
Query Parametreleri
| Parametre | Tip | Zorunlu | Açıklama |
|---|---|---|---|
lang | string | Hayır | Yerelleştirilmiş alanlar için tercih edilen dil |
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
$productId = 6;
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
$result = apiRequest('GET', $baseUrl . '/products/' . (int) $productId, $apiKey);
print_r($result);curl -s -H "X-API-Key: YOUR_API_KEY" \
-H "Accept: application/json" \
"https://example.com/api/v1/products/6"{
"success": true,
"lang": "tr",
"data": {
"id": 6,
"name": "Pamuklu Tişört",
"slug": "pamuklu-tisort",
"price": 199.99,
"old_price": 0,
"stock": 15,
"active": 1,
"barcode": "86991233322",
"stock_code": "fs123",
"category_name": "Test Category",
"brand_name": "Test Brand",
"description_html": "<p>Yumuşak kumaş</p>",
"has_variations": true,
"images": [
{
"id": 12,
"url": "https://example.com/img/products/12.jpg",
"cover": true
}
],
"variations": [
{
"id": 1,
"sku": "TSH-RED-M",
"barcode": "8690000001001",
"options": { "Color": "Red", "Size": "M" },
"price": 249.9,
"stock": 12,
"active": 1
}
],
"translations": {
"tr": {
"name": "Pamuklu Tişört",
"slug": "pamuklu-tisort",
"description_html": "<p>Yumuşak kumaş</p>"
},
"en": {
"name": "Cotton T-Shirt",
"slug": "cotton-t-shirt",
"description_html": "<p>Soft fabric</p>"
}
}
}
} https://example.com/api/v1/productsYeni bir ürün oluşturur. Zorunlu alanlar: ürün adı, kategori, marka ve fiyat. Kategori/marka ID veya ad olarak gönderilebilir (eksik adlar otomatik oluşturulur).
İstek Gövdesi Parametreleri
| Parametre | Tip | Zorunlu | Açıklama |
|---|---|---|---|
name | string | Evet* | Ürün adı (veya translations kullanın) |
category / category_id | string / int | Evet | Kategori adı veya ID |
brand / brand_id | string / int | Evet | Marka adı veya ID |
price | float | Evet* | Satış fiyatı (veya doviz_price) |
cost | float | Hayır | Maliyet fiyatı |
old_price | float | Hayır | Liste / üzeri çizili fiyat |
stock | integer | Hayır | Stok miktarı |
active | integer | Hayır | 1 veya 0 |
barcode | string | Hayır | Barkod |
stock_code | string | Hayır | SKU / stok kodu |
description_html | string | Hayır | HTML açıklama |
short_description | string | Hayır | Kısa metin |
vat | float | Hayır | KDV oranı |
doviz | string | Hayır | try, usd, eur, xau |
doviz_price | float | Hayır | Döviz cinsinden fiyat |
cargo_day | integer | Hayır | Kargo günü |
label | string | Hayır | Ürün etiketi |
desi | float | Hayır | Desi / hacimsel ağırlık |
translations | object | Hayır | Dil koduna göre çok dilli alanlar |
variations | array | Hayır | Varyant satırları (sku, options, price, stock) |
POST /products/{id}/image ile yükleyin. Başarı durumunda HTTP durumu 201'dir.<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
// Multi-language product with variations
$payload = [
'translations' => [
'tr' => [
'name' => 'Pamuklu Tişört',
'slug' => 'pamuklu-tisort',
'description_html' => '<p>Yumuşak kumaş</p>',
],
'en' => [
'name' => 'Cotton T-Shirt',
'slug' => 'cotton-t-shirt',
'description_html' => '<p>Soft fabric</p>',
],
],
'category' => 'Test Category',
'brand' => 'Test Brand',
'cost' => 50,
'price' => 199.99,
'stock' => 15,
'active' => 1,
'barcode' => '86991233322',
'stock_code' => 'fs123',
'variations' => [
[
'sku' => 'TSH-RED-M',
'barcode' => '8690000001001',
'options' => [
'Color' => 'Red',
'Size' => 'M',
],
'price' => 249.90,
'stock' => 12,
'active' => true,
],
],
];
$result = apiRequest('POST', $baseUrl . '/products', $apiKey, $payload);
$productId = (int) ($result['response']['data']['id'] ?? 0);
print_r($result);# Simple create (category / brand by name)
curl -s -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"name": "Omega 3 Balık Yağı 90 Kapsül",
"category": "Vitamin & Takviye",
"brand": "Nutrof",
"price": 249.90,
"old_price": 299.90,
"stock": 50,
"active": 1,
"barcode": "8690000000888",
"stock_code": "OMEGA3-90",
"description_html": "<p>90 kapsül, soğuk sıkım.</p>"
}' \
"https://example.com/api/v1/products"
# Multi-language + variations
curl -s -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"translations": {
"tr": { "name": "Pamuklu Tişört", "slug": "pamuklu-tisort", "description_html": "<p>Yumuşak kumaş</p>" },
"en": { "name": "Cotton T-Shirt", "slug": "cotton-t-shirt", "description_html": "<p>Soft fabric</p>" }
},
"category": "Test Category",
"brand": "Test Brand",
"cost": 50,
"price": 199.99,
"stock": 15,
"active": 1,
"barcode": "86991233322",
"stock_code": "fs123",
"variations": [
{
"sku": "TSH-RED-M",
"barcode": "8690000001001",
"options": { "Color": "Red", "Size": "M" },
"price": 249.90,
"stock": 12,
"active": true
}
]
}' \
"https://example.com/api/v1/products"{
"success": true,
"message": "Ürün eklendi",
"lang": "tr",
"data": {
"id": 6,
"name": "Pamuklu Tişört",
"slug": "pamuklu-tisort",
"price": 199.99,
"stock": 15,
"active": 1,
"barcode": "86991233322",
"stock_code": "fs123",
"category_name": "Test Category",
"brand_name": "Test Brand",
"has_variations": true
}
} https://example.com/api/v1/products/{id}Tam ürün güncellemesi. Zorunlu alanlar (ad, kategori, marka) bulunmalıdır — bazı durumlarda eksik değerler mevcut üründen doldurulur, ancak en iyi uygulama önce GET /products/{id} ile almak, değişiklikleri birleştirmek ve ardından PATCH göndermektir.
Yalnızca fiyat / stok / aktif durumu için Stok ve Fiyat Güncelle (PATCH /products/{id}/quick) endpoint'ini kullanın.
URL Parametreleri
| Parametre | Tip | Açıklama |
|---|---|---|
id | integer | Ürün ID |
Gövde Parametreleri
Ürün Ekle ile aynı alanlar (translations ve variations dahil).
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
$productId = 6;
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
$payload = [
'name' => 'Test Name',
'category' => 'Category Name',
'brand' => 'Test',
'cost' => 100,
'price' => 299.51,
'stock' => 1,
'active' => 1,
'barcode' => 'AS85551212',
'stock_code' => 'fs123',
'description_html' => '<p>Test</p><p>Test 2</p>',
];
$result = apiRequest('PATCH', $baseUrl . '/products/' . (int) $productId, $apiKey, $payload);
print_r($result);curl -s -X PATCH \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"name": "Test Name",
"category": "Category Name",
"brand": "Test",
"cost": 100,
"price": 299.51,
"stock": 1,
"active": 1,
"barcode": "AS85551212",
"stock_code": "fs123",
"description_html": "<p>Test</p><p>Test 2</p>"
}' \
"https://example.com/api/v1/products/6"{
"success": true,
"message": "Ürün güncellendi",
"data": {
"id": 6,
"name": "Test Name",
"slug": "test-name",
"price": 299.51,
"stock": 1,
"active": 1,
"barcode": "AS85551212",
"stock_code": "fs123",
"category_name": "Category Name",
"brand_name": "Test",
"description_html": "<p>Test</p><p>Test 2</p>"
}
} https://example.com/api/v1/products/{id}/quickYalnızca fiyat, maliyet, stok ve aktif durumu için kısmi güncelleme. Değiştirmek istediğiniz alanları gönderin; diğerleri aynı kalır.
URL Parametreleri
| Parametre | Tip | Açıklama |
|---|---|---|
id | integer | Ürün ID |
Gövde Parametreleri
| Parametre | Tip | Zorunlu | Açıklama |
|---|---|---|---|
price | float | Hayır | Satış fiyatı |
old_price | float | Hayır | Liste fiyatı (takma ad: list_price) |
cost | float | Hayır | Maliyet fiyatı |
doviz_price | float | Hayır | Döviz fiyatı |
doviz_old_price | float | Hayır | Döviz liste fiyatı |
stock | integer | Hayır | Stok miktarı |
active | integer / bool | Hayır | 1/0 veya true/false |
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
$productId = 1;
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
$payload = [
'price' => 500,
'old_price' => 600,
'stock' => 10,
'active' => 1,
];
$result = apiRequest(
'PATCH',
$baseUrl . '/products/' . (int) $productId . '/quick',
$apiKey,
$payload
);
print_r($result);# Full quick update
curl -s -X PATCH \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"price":500,"old_price":600,"stock":10,"active":1}' \
"https://example.com/api/v1/products/1/quick"
# Stock only
curl -s -X PATCH \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"stock":10}' \
"https://example.com/api/v1/products/1/quick"{
"success": true,
"message": "Ürün hızlı güncellendi",
"lang": "tr",
"data": {
"id": 1,
"name": "Test Product",
"price": 500,
"old_price": 600,
"stock": 10,
"active": 1,
"has_variations": true
}
} https://example.com/api/v1/products/{id}/imageBir ürün görseli yükler. İlk görsel otomatik olarak kapak olur. Desteklenen formatlar: JPG, PNG, WEBP (en fazla 5 MB).
URL Parametreleri
| Parametre | Tip | Açıklama |
|---|---|---|
id | integer | Ürün ID |
Yükleme Yöntemleri
| Yöntem | Nasıl |
|---|---|
| Multipart dosya (önerilen) | Form alanı image |
| Uzak URL | JSON {"image_url":"https://..."} |
| Base64 | JSON {"image_base64":"data:image/jpeg;base64,..."} |
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
$productId = 6;
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
function apiUploadImage(string $url, string $apiKey, string $filePath): array
{
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
],
CURLOPT_POSTFIELDS => [
'image' => new CURLFile(
$filePath,
mime_content_type($filePath) ?: 'image/jpeg',
basename($filePath)
),
],
CURLOPT_SSL_VERIFYPEER => true,
]);
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
// Option A — local file
$result = apiUploadImage(
$baseUrl . '/products/' . $productId . '/image',
$apiKey,
__DIR__ . '/sample-product.jpg'
);
// Option B — remote URL
// $result = apiRequest('POST', $baseUrl . '/products/' . $productId . '/image', $apiKey, [
// 'image_url' => 'https://example.com/urun.jpg',
// ]);
print_r($result);# Multipart file upload
curl -s -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Accept: application/json" \
-F "image=@C:/path/urun.jpg" \
"https://example.com/api/v1/products/6/image"
# From remote URL
curl -s -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"image_url":"https://example.com/urun.jpg"}' \
"https://example.com/api/v1/products/6/image"{
"success": true,
"message": "Image uploaded",
"data": {
"id": 12,
"product_id": 6,
"url": "https://example.com/img/products/12.jpg"
}
} https://example.com/api/v1/products/{id}Bir ürünü ID'sine göre kalıcı olarak siler.
URL Parametreleri
| Parametre | Tip | Açıklama |
|---|---|---|
id | integer | Silinecek ürün ID |
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
$productId = 6;
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
$result = apiRequest('DELETE', $baseUrl . '/products/' . (int) $productId, $apiKey);
print_r($result);curl -s -X DELETE \
-H "X-API-Key: YOUR_API_KEY" \
-H "Accept: application/json" \
"https://example.com/api/v1/products/6"{
"success": true,
"message": "Ürün silindi"
} Ürün oluşturma ve güncellemede çok dilli metinler için translations, renk/beden gibi seçenekler için variations dizisini kullanın. Temel akış: Ürün Ekle ve Ürün Güncelle.
Çeviriler (translations)
Dil koduna göre anahtarlanan bir nesnedir (tr, en vb.). Her dil altında tipik alanlar:
name— ürün adıdescription_html— HTML açıklamashort_description— kısa metinslug— isteğe bağlı URL parçası
name / description_html alanları yeterlidir. Çok dilli mağazalarda translations tercih edin.Varyasyonlar (variations)
Her satır bir stok tutulabilir varyantı temsil eder:
sku— stok kodubarcode— barkodstock— varyant stoğuprice— varyant fiyatı (üst üründen farklı olabilir)options— seçenek haritası, örn.Renk/Bedenactive— isteğe bağlı etkinlik bayrağı
Üst stok mu, varyasyon stoğu mu?
- Varyasyonsuz ürün: stok ve fiyatı üst ürün alanlarında (
stock,price) tutun. - Varyasyonlu ürün: satılabilir miktar her varyantın
stockdeğerinden gelir. Üststocközet veya yedek amaçlı olabilir; satış mantığı varyant satırlarına dayanır.
Güncelleme en iyi uygulaması
GET /products/{id}ile mevcut ürünü alın (Ürün Getir.- İhtiyacınız olan alanları / varyantları birleştirin (merge).
- Eksik satır kaybetmemek için
PATCHile mümkün olduğunca tam gövdeyi gönderin Ürün Güncelle.
Örnek istek gövdesi
{
"translations": {
"tr": {
"name": "Pamuklu Tişört",
"description_html": "<p>Yumuşak kumaş</p>",
"short_description": "Günlük kullanım"
},
"en": {
"name": "Cotton T-Shirt",
"description_html": "<p>Soft fabric</p>",
"short_description": "Everyday wear"
}
},
"category": "Giyim",
"brand": "Demo Marka",
"price": 199.99,
"stock": 15,
"active": 1,
"variations": [
{
"sku": "TSH-KIRMIZI-M",
"barcode": "8690000001001",
"options": {
"Renk": "Kırmızı",
"Beden": "M"
},
"price": 249.90,
"stock": 12,
"active": true
},
{
"sku": "TSH-KIRMIZI-L",
"barcode": "8690000001002",
"options": {
"Renk": "Kırmızı",
"Beden": "L"
},
"price": 249.90,
"stock": 8,
"active": true
}
]
}İlgili sayfalar
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
$payload = [
'translations' => [
'tr' => [
'name' => 'Pamuklu Tişört',
'slug' => 'pamuklu-tisort',
'description_html' => '<p>Yumuşak kumaş</p>',
'short_description' => 'Günlük kullanım',
],
'en' => [
'name' => 'Cotton T-Shirt',
'slug' => 'cotton-t-shirt',
'description_html' => '<p>Soft fabric</p>',
'short_description' => 'Everyday wear',
],
],
'category' => 'Test Category',
'brand' => 'Test Brand',
'cost' => 50,
'price' => 199.99,
'stock' => 15,
'active' => 1,
'barcode' => '86991233322',
'stock_code' => 'fs123',
'variations' => [
[
'sku' => 'TSH-RED-M',
'barcode' => '8690000001001',
'options' => [
'Color' => 'Red',
'Size' => 'M',
],
'price' => 249.90,
'stock' => 12,
'active' => true,
],
[
'sku' => 'TSH-RED-L',
'barcode' => '8690000001002',
'options' => [
'Color' => 'Red',
'Size' => 'L',
],
'price' => 249.90,
'stock' => 8,
'active' => true,
],
],
];
$ch = curl_init($baseUrl . '/products');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . trim($apiKey),
'Content-Type: application/json',
'Accept: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
CURLOPT_SSL_VERIFYPEER => true,
]);
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo $httpCode . PHP_EOL;
echo $response;curl -s -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"translations": {
"tr": {
"name": "Pamuklu Tişört",
"slug": "pamuklu-tisort",
"description_html": "<p>Yumuşak kumaş</p>",
"short_description": "Günlük kullanım"
},
"en": {
"name": "Cotton T-Shirt",
"slug": "cotton-t-shirt",
"description_html": "<p>Soft fabric</p>",
"short_description": "Everyday wear"
}
},
"category": "Test Category",
"brand": "Test Brand",
"cost": 50,
"price": 199.99,
"stock": 15,
"active": 1,
"barcode": "86991233322",
"stock_code": "fs123",
"variations": [
{
"sku": "TSH-RED-M",
"barcode": "8690000001001",
"options": { "Color": "Red", "Size": "M" },
"price": 249.90,
"stock": 12,
"active": true
},
{
"sku": "TSH-RED-L",
"barcode": "8690000001002",
"options": { "Color": "Red", "Size": "L" },
"price": 249.90,
"stock": 8,
"active": true
}
]
}' \
"https://example.com/api/v1/products"{
"success": true,
"message": "Ürün eklendi",
"lang": "tr",
"data": {
"id": 6,
"name": "Pamuklu Tişört",
"slug": "pamuklu-tisort",
"price": 199.99,
"stock": 15,
"active": 1,
"barcode": "86991233322",
"stock_code": "fs123",
"category_name": "Test Category",
"brand_name": "Test Brand",
"has_variations": true
}
} https://example.com/api/v1/categoriesMağaza kategorilerini döndürür. Ürün oluşturmadan önce (ID çözümlemek veya adları doğrulamak için) kullanışlıdır.
Query Parametreleri
| Parametre | Tip | Zorunlu | Açıklama |
|---|---|---|---|
page | integer | Hayır | 0 tabanlı sayfa (varsayılan: 0) |
size / limit | integer | Hayır | Sayfa boyutu (varsayılan: 100, en fazla: 500) |
active | integer | Hayır | 1 yalnızca aktif (varsayılan), 0 pasif |
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
$result = apiRequest('GET', $baseUrl . '/categories?page=0&size=100&active=1', $apiKey);
print_r($result);curl -s -H "X-API-Key: YOUR_API_KEY" \
-H "Accept: application/json" \
"https://example.com/api/v1/categories?page=0&size=100&active=1"{
"totalElements": 4,
"content": [
{
"id": 3,
"name": "Vitamin & Takviye",
"slug": "vitamin-takviye",
"parentId": 1,
"parentName": "Sağlık",
"active": true
}
]
} https://example.com/api/v1/brandsMağaza markalarını döndürür.
Query Parametreleri
| Parametre | Tip | Zorunlu | Açıklama |
|---|---|---|---|
page | integer | Hayır | 0 tabanlı sayfa (varsayılan: 0) |
size / limit | integer | Hayır | Sayfa boyutu (varsayılan: 100, en fazla: 500) |
active | integer | Hayır | 1 yalnızca aktif (varsayılan), 0 pasif |
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
$result = apiRequest('GET', $baseUrl . '/brands?page=0&size=100&active=1', $apiKey);
print_r($result);curl -s -H "X-API-Key: YOUR_API_KEY" \
-H "Accept: application/json" \
"https://example.com/api/v1/brands?page=0&size=100&active=1"{
"totalElements": 2,
"content": [
{
"id": 1,
"name": "Nutrof",
"slug": "nutrof",
"active": true
}
]
} https://example.com/api/v1/ordersSiparişleri Trendyol benzeri bir yapıda döndürür: totalElements, content[], ayrıca shipmentAddress, invoiceAddress ve lines[].
Sayfa numaralandırması 0 tabanlıdır.
Durum Kodları
| Kod | Etiket | API status |
|---|---|---|
1 | Ödeme Bekliyor | AwaitingPayment |
2 | Hazırlanıyor | Picking |
3 | Kargoda | Shipped |
4 | Teslim Edildi | Delivered |
5 | İptal Edildi | Cancelled |
Query Parametreleri
| Parametre | Tip | Zorunlu | Açıklama |
|---|---|---|---|
page | integer | Hayır | 0 tabanlı sayfa (varsayılan: 0) |
size / limit | integer | Hayır | Sayfa boyutu (varsayılan: 30, en fazla: 100) |
status | integer | Hayır | 1–5, veya tümü için 0 |
date_from | string | Hayır | Başlangıç tarihi (YYYY-MM-DD veya datetime) |
date_to | string | Hayır | Bitiş tarihi |
startDate | integer | Hayır | Unix ms (Trendyol uyumlu) |
endDate | integer | Hayır | Unix ms |
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
$result = apiRequest(
'GET',
$baseUrl . '/orders?page=0&size=20&status=2&date_from=2026-06-10&date_to=2026-07-30',
$apiKey
);
print_r($result);curl -s -H "X-API-Key: YOUR_API_KEY" \
-H "Accept: application/json" \
"https://example.com/api/v1/orders?page=0&size=20&status=2&date_from=2026-06-10&date_to=2026-07-30"{
"totalElements": 1,
"totalPages": 1,
"page": 0,
"size": 20,
"content": [
{
"id": 15,
"orderNumber": "FS-20260617-0015",
"customerFirstName": "Ali",
"customerLastName": "Yılmaz",
"customerEmail": "ali@ornek.com",
"customerPhone": "05551234567",
"packageTotalPrice": 899.0,
"paymentMethod": "bankwire",
"paymentLabel": "Havale / EFT",
"statusCode": 2,
"status": "Picking",
"statusLabel": "Hazırlanıyor",
"cargoCompany": "",
"trackingNumber": "",
"commercial": false,
"orderDate": 1718611200000,
"invoice": null,
"shipmentAddress": {
"firstName": "Ali",
"lastName": "Yılmaz",
"city": "İstanbul",
"district": "Kadıköy",
"address1": "Moda Cad. No:5",
"fullAddress": "İstanbul / Kadıköy — Moda Cad. No:5",
"phone": "05551234567",
"countryCode": "TR"
},
"lines": [
{
"lineId": 42,
"contentId": 1,
"productName": "SolNutrof Total 30 Kapsül",
"quantity": 1,
"stockCode": "SOLNUTROF30",
"barcode": "8690000000011",
"lineUnitPrice": 899.0,
"lineAmount": 899.0,
"vatRate": 20.0,
"orderLineItemStatusName": "Picking"
}
]
}
]
} https://example.com/api/v1/orders/{id}Tek bir siparişi, liste öğeleriyle aynı alanlarla döndürür (adresler, satırlar, kargo, isteğe bağlı invoice).
URL Parametreleri
| Parametre | Tip | Açıklama |
|---|---|---|
id | integer | Sipariş ID |
invoice şu şekilde görünür: {"type":"url","url":"...","name":"..."} veya null.<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
$orderId = 15;
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
$result = apiRequest('GET', $baseUrl . '/orders/' . (int) $orderId, $apiKey);
print_r($result);curl -s -H "X-API-Key: YOUR_API_KEY" \
-H "Accept: application/json" \
"https://example.com/api/v1/orders/15"{
"id": 15,
"orderNumber": "FS-20260617-0015",
"customerFirstName": "Ali",
"customerLastName": "Yılmaz",
"customerEmail": "ali@ornek.com",
"packageTotalPrice": 899.0,
"statusCode": 2,
"status": "Picking",
"statusLabel": "Hazırlanıyor",
"cargoCompany": "",
"trackingNumber": "",
"invoice": null,
"lines": [
{
"lineId": 42,
"productName": "SolNutrof Total 30 Kapsül",
"quantity": 1,
"stockCode": "SOLNUTROF30",
"lineAmount": 899.0
}
]
} https://example.com/api/v1/orders/{id}Sipariş durumunu ve/veya kargo takip alanlarını günceller. Şunlardan en az birini gönderin: status, cargoCompany, trackingNumber (snake_case takma adları da kabul edilir).
Geçerli Durum Değerleri
1— Ödeme bekliyor2— Hazırlanıyor3— Kargoda4— Teslim edildi5— İptal edildi
Gövde Parametreleri
| Parametre | Tip | Zorunlu | Açıklama |
|---|---|---|---|
status | integer | Hayır* | Yeni durum (1–5) |
cargoCompany | string | Hayır* | Kargo firması adı |
trackingNumber | string | Hayır* | Takip numarası |
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
$orderId = 15;
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
$payload = [
'status' => 3,
'cargoCompany' => 'Yurtiçi Kargo',
'trackingNumber' => 'YT123456789',
];
$result = apiRequest('PATCH', $baseUrl . '/orders/' . (int) $orderId, $apiKey, $payload);
print_r($result);curl -s -X PATCH \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"status": 3,
"cargoCompany": "Yurtiçi Kargo",
"trackingNumber": "YT123456789"
}' \
"https://example.com/api/v1/orders/15"{
"success": true,
"message": "Sipariş güncellendi",
"content": {
"id": 15,
"orderNumber": "FS-20260617-0015",
"statusCode": 3,
"status": "Shipped",
"statusLabel": "Kargoda",
"cargoCompany": "Yurtiçi Kargo",
"trackingNumber": "YT123456789"
}
} https://example.com/api/v1/orders/{id}/invoiceBir siparişe harici fatura URL'si ekleyin veya kaldırın. Web API üzerinden dosya yükleme desteklenmez — yalnızca herkese açık bir URL.
Fatura URL'si ayarla
POST /api/v1/orders/{id}/invoice
| Parametre | Tip | Zorunlu | Açıklama |
|---|---|---|---|
url | string | Evet | Herkese açık fatura URL'si (PDF/görsel) |
name | string | Hayır | Görünen ad |
Faturayı kaldır
DELETE /api/v1/orders/{id}/invoice
<?php
$baseUrl = 'https://example.com/api/v1';
$apiKey = 'YOUR_API_KEY';
$orderId = 15;
function apiRequest(string $method, string $url, string $apiKey, ?array $data = null): array
{
$ch = curl_init();
$headers = [
'X-API-Key: ' . trim($apiKey),
'Accept: application/json',
];
if ($data !== null) {
$headers[] = 'Content-Type: application/json';
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => true,
]);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
$decoded = json_decode((string) $response, true);
return [
'http_code' => $httpCode,
'curl_error' => $error,
'response' => is_array($decoded) ? $decoded : $response,
];
}
// Attach invoice URL
$result = apiRequest('POST', $baseUrl . '/orders/' . $orderId . '/invoice', $apiKey, [
'url' => 'https://example.com/fatura.pdf',
'name' => 'Fatura',
]);
print_r($result);
// Remove invoice
// $result = apiRequest('DELETE', $baseUrl . '/orders/' . $orderId . '/invoice', $apiKey);# Attach invoice URL
curl -s -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/fatura.pdf","name":"Fatura"}' \
"https://example.com/api/v1/orders/15/invoice"
# Remove invoice
curl -s -X DELETE \
-H "X-API-Key: YOUR_API_KEY" \
"https://example.com/api/v1/orders/15/invoice"{
"success": true,
"message": "Fatura kaydedildi",
"content": {
"id": 15,
"orderNumber": "FS-20260617-0015",
"invoice": {
"type": "url",
"url": "https://example.com/fatura.pdf",
"name": "Fatura"
}
}
} Partner entegrasyonunda siparişler webhook ile itilmez; Web API üzerinden okunur, durum güncellenir ve fatura URL'si eklenir. Temel uç noktalar: Listele, Detay, Durum güncelle, Fatura.
Durum kodları
| Kod | Türkçe etiket | API adı |
|---|---|---|
1 | Ödeme Bekliyor | AwaitingPayment |
2 | Hazırlanıyor | Picking |
3 | Kargoda | Shipped |
4 | Teslim Edildi | Delivered |
5 | İptal Edildi | Cancelled |
Tipik akış
AwaitingPayment → Picking → Shipped → Delivered. Herhangi bir aşamada Cancelled olabilir.
- Ödeme tamamlanınca veya onaylanınca
2(Hazırlanıyor). - Paket çıktığında
3(Kargoda) + takip bilgisi. - Teslimatta
4(Teslim Edildi). - İptalde
5(İptal Edildi).
Kargo alanları
cargoCompany ve trackingNumber (veya cargo_company / tracking_number) genellikle durum 3 — Kargoda ile birlikte gönderilir. Yalnızca takip bilgisi de güncellenebilir; en az bir alan gerekir. Bkz. Sipariş Durumunu Güncelle.
Sorgulama stratejisi (polling)
GET /orders kullanın — sayfa 0 tabanlıdır.date_from/date_to—YYYY-MM-DDveya datetimestartDate/endDate— Unix milisaniye (Trendyol uyumlu)status— 1–5, veya tümü için0page/size— sayfalama (varsayılan boyut 30, en fazla 100)
Son başarılı senkron zamanından itibaren dar bir tarih penceresi tutmak gereksiz yükü azaltır. Kurallar için API Kuralları sayfasına bakın.
Fatura
Sevkiyattan sonra herkese açık bir fatura URL'si ekleyin (POST /orders/{id}/invoice). Dosya yükleme desteklenmez — yalnızca URL. Bkz. Sipariş Faturası.
İzinler
orders.read— listeleme ve detayorders.write— durum / kargo güncelleme ve fatura işlemleri
403 döner. Anahtarları Yönetim → API bölümünden ayarlayın — Kimlik Doğrulama.İlgili sayfalar
# 1) List picking orders (status=2), page is 0-based
curl -s -H "X-API-Key: YOUR_API_KEY" -H "Accept: application/json" \
"https://example.com/api/v1/orders?page=0&size=20&status=2&date_from=2026-06-01&date_to=2026-07-30"
# 2) Mark order 15 as shipped + cargo tracking
curl -s -X PATCH \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"status": 3,
"cargoCompany": "Yurtiçi Kargo",
"trackingNumber": "YT123456789"
}' \
"https://example.com/api/v1/orders/15"
# 3) Attach public invoice URL
curl -s -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"url": "https://example.com/fatura.pdf",
"name": "Fatura"
}' \
"https://example.com/api/v1/orders/15/invoice" Bu uç noktalar iş ortağı Web API değildir. Tema JavaScript'inin müşteri oturumu ve CSRF ile sepet, hesap, favori ve arama gibi vitrin işlemlerini yürütmesi içindir.
/api/v1/* ve X-API-Key kullanır. Aşağıdaki /api/*.php yolları tarayıcı oturumuna bağlıdır; Web API anahtarı ile çağrılmazlar.Uç noktalar
| Yöntem | Yol | İşlemler / not |
|---|---|---|
POST | /api/auth.php | login, register, logout, me |
POST | /api/cart.php | add, update, remove, clear, get |
POST | /api/coupon.php | apply, remove, set_cargo, set_payment, summary |
POST | /api/account.php | profil, şifre, adresler, bildirimler |
POST | /api/favorite.php | toggle, remove |
GET | /api/product.php?id= | Hızlı bakış / ürün özeti |
GET | /api/search-suggest.php?q= | Arama otomatik tamamlama |
GET / POST | /api/module.php?m={module}&action={action} | Modül API yönlendiricisi |
CSRF
Durum değiştiren isteklerde jeton zorunludur. Kaynaklar:
- Oturum:
$_SESSION['csrf_token'] - Başlık:
X-CSRF-Token - Form / AJAX alanı:
token
header.tpl içinde tanımlı global csrfToken değişkenini kullanın; jetonu şablona sabit yazmayın. Yönetim veya cron jetonunu vitrin AJAX'ında CSRF olarak kullanmayın.Modül API'leri
Modül uç noktaları her zaman /api/module.php üzerinden gider; /modules/{name}/api/*.php dosyalarına doğrudan erişim engellenir. Ayrıntılar: Yeni Modül Oluştur, Kanca Listesi, Şablonlar.
İlgili sayfalar
// Cart AJAX — CSRF from global csrfToken (header.tpl) or meta[name="csrf-token"]
(function () {
function getCsrfToken() {
if (typeof window.csrfToken === 'string' && window.csrfToken !== '') {
return window.csrfToken;
}
var meta = document.querySelector('meta[name="csrf-token"]');
return meta ? meta.getAttribute('content') : '';
}
function cartRequest(action, body) {
var data = Object.assign({ action: action, token: getCsrfToken() }, body || {});
return fetch('/api/cart.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-CSRF-Token': getCsrfToken(),
'Accept': 'application/json'
},
credentials: 'same-origin',
body: new URLSearchParams(data).toString()
}).then(function (res) {
return res.json();
});
}
// Add product id 12 (optional variation)
cartRequest('add', {
id_product: '12',
id_variation: '0',
qty: '1'
}).then(function (json) {
console.log(json);
});
})();// Login via storefront auth API (session + CSRF, not X-API-Key)
(function () {
function getCsrfToken() {
if (typeof window.csrfToken === 'string' && window.csrfToken !== '') {
return window.csrfToken;
}
var meta = document.querySelector('meta[name="csrf-token"]');
return meta ? meta.getAttribute('content') : '';
}
fetch('/api/auth.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-CSRF-Token': getCsrfToken(),
'Accept': 'application/json'
},
credentials: 'same-origin',
body: new URLSearchParams({
action: 'login',
token: getCsrfToken(),
login: 'customer@example.com',
password: 'secret',
remember: '1'
}).toString()
})
.then(function (res) { return res.json(); })
.then(function (json) {
console.log(json);
if (json.success && json.redirect) {
window.location.href = json.redirect;
}
});
})(); FriSay, döviz fiyatı yenileme, terk edilmiş sepet hatırlatmaları, modül cron'ları ve pazaryeri senkronu için HTTP tabanlı zamanlanmış işler sunar. Jeton, yönetim ayarlarındaki SHOP_TOKEN değeridir (Settings::get('SHOP_TOKEN')).
Kimlik doğrulama
- Query:
?token=YOUR_SHOP_TOKEN - Başlık:
X-Cron-Token: YOUR_SHOP_TOKEN
SHOP_TOKEN değerini şablonlarda, istemci JS'inde veya genel depolarda asla açığa çıkarmayın. Yalnızca sunucu crontab / gizli yapılandırmada tutun.Çekirdek cron — döviz
GET veya POST /api/cron.php?action=currency&token=... — döviz kurlarına göre ürün fiyatlarını yeniler.
curl -s "https://yoursite.com/api/cron.php?action=currency&token=YOUR_SHOP_TOKEN"
curl -s -X POST \
-H "X-Cron-Token: YOUR_SHOP_TOKEN" \
"https://yoursite.com/api/cron.php?action=currency"Terk edilmiş sepet
action=abandoned-cart — yalnızca abandoned-cart modülü kuruluysa anlamlıdır.
curl -s "https://yoursite.com/api/cron.php?action=abandoned-cart&token=YOUR_SHOP_TOKEN"Modül cron'ları
Modülde $apiActions içinde 'cron' => 'api/cron.php' kaydı varsa:
curl -s "https://yoursite.com/api/module.php?m=MODULE_NAME&action=cron&token=YOUR_SHOP_TOKEN"Modül mimarisi için Yeni Modül Oluştur sayfasına bakın.
Pazaryeri cron
Sipariş veya soru senkronu için: /api/marketplace.php?action=cron&type=orders|questions&platform=trendyol|hepsiburada|n11&token=...
curl -s "https://yoursite.com/api/marketplace.php?action=cron&type=orders&platform=trendyol&token=YOUR_SHOP_TOKEN"
curl -s "https://yoursite.com/api/marketplace.php?action=cron&type=questions&platform=hepsiburada&token=YOUR_SHOP_TOKEN"Crontab örneği
# Her 30 dakikada döviz
*/30 * * * * curl -s "https://yoursite.com/api/cron.php?action=currency&token=YOUR_SHOP_TOKEN" >/dev/null 2>&1
# Her saat terk edilmiş sepet (modül kuruluysa)
0 * * * * curl -s "https://yoursite.com/api/cron.php?action=abandoned-cart&token=YOUR_SHOP_TOKEN" >/dev/null 2>&1İlgili sayfalar
# Currency / FX product price refresh
curl -s "https://example.com/api/cron.php?action=currency&token=YOUR_SHOP_TOKEN"
# Same via header
curl -s -H "X-Cron-Token: YOUR_SHOP_TOKEN" \
"https://example.com/api/cron.php?action=currency"
# Abandoned-cart reminders (module must be installed)
curl -s "https://example.com/api/cron.php?action=abandoned-cart&token=YOUR_SHOP_TOKEN"
# Module cron (example: reviews invite job)
curl -s "https://example.com/api/module.php?m=reviews&action=cron&token=YOUR_SHOP_TOKEN"
# Marketplace order sync (Trendyol)
curl -s "https://example.com/api/marketplace.php?action=cron&type=orders&platform=trendyol&token=YOUR_SHOP_TOKEN"
# Marketplace Q&A sync (Hepsiburada), no stock push to marketplace
curl -s "https://example.com/api/marketplace.php?action=cron&type=questions&platform=hepsiburada&send=0&token=YOUR_SHOP_TOKEN" Modül, modules/{name}/ altında yaşayan bağımsız bir PHP sınıfıdır. Modüller tema dosyalarını değiştirmeden çalışır — tüm şablonlar modül klasöründe kalır.
Tam geliştirici kılavuzunu indirip herhangi bir yapay zeka asistanına (ChatGPT, Claude, Cursor vb.) yükleyin:
github.com/bera65/frisay/blob/main/modules/MODULE_DEVELOPER_GUIDE.md
Yapay zeka bu dosyayı okuduktan sonra modül mimarisini, adlandırma kurallarını, API kalıplarını, güvenlik gereksinimlerini ve yardımcı sınıfları anlar — böylece modülünüzü sade dille tarif edip çalışan kodu çok daha hızlı üretebilirsiniz.
Bir Modül Ne Yapabilir?
- Yönetim paneli yapılandırma ekranı (
adminPage()) - Görüntüleme kancaları ile vitrin HTML enjeksiyonu — bkz. Kanca Listesi
- JSON API uç noktaları (
api/{action}.php) $routesile özel mağaza sayfaları- Ödeme yöntemi entegrasyonu (ödeme)
install.sql/uninstall.sqlile veritabanı tabloları
Modül Dosya Şeması
modules/{name}/
{name}.php ← Main class (REQUIRED)
logo.png ← Admin module list icon
install.sql ← Install SQL (optional)
uninstall.sql ← Uninstall SQL (optional)
assets/
templates/
admin/
admin.tpl ← Admin configure page
front/
{hook_name}.tpl ← Storefront hook output
css/
front.css ← Storefront styles
admin.css ← Admin styles
js/
front.js ← Storefront scripts
admin.js ← Admin scripts
img/ ← Images
api/
{action}.php ← API endpoint files
front/
{page}.php ← Custom store route file
Adlandırma Kuralları (Kritik)
| Öğe | Kural | Örnek |
|---|---|---|
| Klasör adı | Küçük harf, tire kullanılabilir | my-module |
| Ana dosya | {name}/{name}.php | modules/my-module/my-module.php |
| PHP sınıfı | PascalCase + Module | my-module → MyModuleModule |
| Yönetim URL'si | Admin::url() + ADMIN_URI ile | Admin::url('module-my-module') |
| API URL'si | Modül yönlendiricisi ile | /api/module.php?m=my-module&action=save |
$name özelliği | Klasör adıyla birebir eşleşmeli | public string $name = 'my-module'; |
ModuleBase — Temel Özellikler
| Özellik | Amaç |
|---|---|
$displayHooks | Desteklenen kancalar: hook_name => description |
$defaultDisplayHooks | install() sırasında otomatik atanan kancalar |
$apiActions | action => api/file.php eşlemesi |
$routes | slug => front/page.php özel mağaza sayfaları |
$frontStylesheets / $frontScripts | assets/ altındaki vitrin CSS/JS dosya adları. Boş = hiçbir şey yüklenmez (dosyaları açıkça listelemelisiniz veya head.assets kullanın) |
$adminStylesheets / $adminScripts | Yalnızca modül yönetim yapılandırma sayfasındaki varlıklar |
$isPayment | true → ödeme adımında ödeme yöntemi olarak kaydeder |
$paymentMethodId | orders.payment_method içinde saklanan değer |
$paysBeforeOrder | true → sipariş oluşturulmadan önce ödeme (sanal POS) |
$frontStylesheets / $frontScripts artık assets/css veya assets/js içindeki her dosyayı otomatik yüklemez. İhtiyacınız olan her dosyayı bildirin, örn. ['front.css'].renderFrontTemplate — Önemli Kural
Değişkenleri doğrudan global Smarty'ye atamak yerine her zaman $this->renderFrontTemplate('hook_name', $vars) kullanın. Bu, izole bir alt şablonda render eder ve $isLoggedIn veya $cart gibi global değişkenleri kirletmez.
- Şablon yolu:
assets/templates/front/{name}.tpl - HTML dizesi döndürür — temada
{$hooks.footer nofilter}ile çıktılanır
Yönetim Yapılandırması
- URL:
Admin::url('module-{name}')— asla/admin/yolunu sabit yazmayın (yolconfig/env.phpiçindekiADMIN_URI'den gelir) - Şablon:
assets/templates/admin/admin.tpl - Yönetim görüntüleme kancaları:
admin_*kancaları içinrenderAdminDisplayHook($hook, $context)uygulayın — bkz. Kanca Listesi - İsteğe bağlı kenar çubuğu bağlantısı:
boot()içinde$this->registerAdminMenuLink('Label', 'catalog', 95) - Tüm POST formları
$adminTokendeğerinihash_equals()ile doğrulamak zorundadır
API Uç Noktaları
- URL:
POST /api/module.php?m={name}&action={action} - Vitrin API'leri
$_SESSION['csrf_token']doğrulamalıdır —header.tpliçindeki global JScsrfTokenkullanın - Vitrin AJAX'ında CSRF jetonu olarak yönetim veya cron jetonu kullanmayın
/modules/{name}/api/*.phpdosyalarına doğrudan erişim engellenir (403) — her zamanapi/module.phpüzerinden yönlendirin- Cron işleri:
'cron' => 'api/cron.php'olarak kaydedin ve/api/module.php?m={name}&action=cron&token=...kullanın
Kurulumdan Sonra
- Gerekli dosyalarla
modules/{name}/klasörünü oluşturun - Yönetim → Modüller sayfasına gidin — modül listede görünür
- Kur'a tıklayın —
defaultDisplayHooksotomatik atanır - Gerekirse modül detay sayfasında kanca atamalarını ayarlayın
- Kurulumdan sonra modül varsayılan olarak aktiftir
Güvenlik Kontrol Listesi
- Dosya koruması:
if (!defined('IN_SCRIPT') && !defined('IN_ADMIN')) exit; - Yönetim formları:
hash_equals($adminToken, $postToken) - Mağaza API: oturumdaki
csrfTokendoğrulayın - SQL: her zaman
DB::execute($sql, [$param])ile hazırlanmış ifadeler kullanın - Kullanıcı girdisi:
trim,strip_tags, Smarty|escape - Kanca şablonlarında satır içi
<script>kullanmayın —assets/js/+data-api-urlkullanın - Tema veya çekirdek dosyalarını değiştirmeyin
Referans Modüller
| Modül | Ne öğrenilir |
|---|---|
whatsapp | Basit alt bilgi kancası + SQL |
newsletter | API + alt bilgi formu + CSRF kalıbı |
reviews | Ürün sekmeleri + API + teslimat sonrası davet cron |
question | Ürün sekmeleri + giriş gerektiren API + bot koruması |
slider | Yönetim CRUD + görsel yükleme |
bankwire | Ödeme + sipariş onay kancası |
sanalpos | paysBeforeOrder + front route |
recaptcha | form.captcha.validate + auth/contact görüntüleme kancaları |
alert | product.updated + order.placed + stok gelince formu |
abandoned-cart | cart.changed takibi + hatırlatma cron |
facebook-pixel | head.top / footer / product_detail takibi |
instagram-gallery | home_bottom görüntüleme kancası |
export-excel | admin_header yönetim görüntüleme kancası |
product-set | Ürün setleri + product.updated |
match, str_starts_with(), adlandırılmış argümanlar veya birleşik tipler kullanmayın. Bunun yerine switch ve strpos() kullanın.<?php
if (!defined('IN_SCRIPT') && !defined('IN_ADMIN')) {
exit;
}
require_once dirname(__DIR__, 2) . '/core/ModuleBase.php';
class MyModuleModule extends ModuleBase
{
// ── Identity (REQUIRED) ───────────────────────────────
public string $name = 'my-module'; // must match folder name
public string $title = 'My Module';
public string $version = '1.0.0';
public string $description = 'Short description';
public string $author = 'FriSay';
// ── Display hooks ─────────────────────────────────────
public array $displayHooks = [
'footer' => 'Adds HTML to the footer area',
];
public array $defaultDisplayHooks = ['footer'];
// ── CSS/JS (empty = load nothing — list files explicitly) ─
public array $frontStylesheets = ['front.css'];
public array $frontScripts = [];
public array $adminStylesheets = ['admin.css'];
public array $adminScripts = [];
// ── API endpoints ─────────────────────────────────────
public array $apiActions = [
'save' => 'api/save.php',
];
// ── Custom store routes: site.com/{slug} ────────────
public array $routes = [
// 'my-page' => 'front/page.php',
];
// ── REQUIRED: install / uninstall ─────────────────────
public function install(): bool
{
return $this->runSqlFile('install.sql');
// No database table: return true;
}
public function uninstall(): bool
{
return $this->runSqlFile('uninstall.sql');
}
// ── Optional: runs when module is loaded ──────────────
public function boot(): void
{
Module::registerHook('order.placed', function (array $order): void {
// Run after order is created
});
}
// ── Admin configure page (Admin::url('module-my-module')) ────
public function adminPage(): void
{
global $smarty, $adminToken;
$flash = '';
if (Tools::isSubmit('saveMyModule')) {
$postToken = (string) Tools::getValue('token');
if (hash_equals($adminToken, $postToken)) {
Settings::set('MY_MODULE_SETTING', trim((string) Tools::getValue('setting')));
$flash = 'Saved';
} else {
$flash = 'Invalid request';
}
}
$smarty->assign([
'setting' => Settings::get('MY_MODULE_SETTING'),
'flash' => $flash,
]);
}
// ── Storefront hook output ────────────────────────────
public function renderDisplayHook(string $hook, array $context = []): ?string
{
if ($hook !== 'footer') {
return null;
}
$html = $this->renderFrontTemplate('footer', [
'message' => Settings::get('MY_MODULE_SETTING'),
]);
return $html !== '' ? $html : null;
}
}{* assets/templates/admin/admin.tpl *}
{if $flash}
<div class="alert alert-info">{$flash|escape}</div>
{/if}
<form method="post" class="admin-panel p-3" style="max-width:400px">
<input type="hidden" name="saveMyModule" value="1">
<input type="hidden" name="token" value="{$adminToken}">
<label class="form-label">Setting value</label>
<input type="text" name="setting" class="form-control" value="{$setting|escape}">
<button type="submit" class="btn btn-dark mt-3">Save</button>
</form><?php
if (!defined('IN_SCRIPT')) {
exit;
}
header('Content-Type: application/json; charset=utf-8');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Method not allowed']);
exit;
}
// Store CSRF — use global csrfToken in JS, NOT admin/cron tokens
$token = Tools::getValue('token') ?: ($_SERVER['HTTP_X_CSRF_TOKEN'] ?? '');
if (!hash_equals($_SESSION['csrf_token'] ?? '', (string) $token)) {
http_response_code(403);
echo json_encode(['success' => false, 'message' => 'Invalid request']);
exit;
}
$result = MyModuleModule::doSomething((string) Tools::getValue('field'));
echo json_encode($result);// csrfToken is defined in header.tpl — do not hardcode tokens in templates
$(document).on('submit', '.my-module-form', function (e) {
e.preventDefault();
var $form = $(this);
$.ajax({
url: $form.data('api-url'),
method: 'POST',
dataType: 'json',
data: {
token: typeof csrfToken !== 'undefined' ? csrfToken : '',
field: $.trim($form.find('[name="field"]').val())
}
});
});<?php
public bool $isPayment = true;
public string $paymentMethodId = 'bank_transfer';
public string $paymentMethodLabel = 'Bank Transfer';
public bool $paysBeforeOrder = false;
// order_payment.tpl — radio value must match $paymentMethodId
public function processPayment(array $order): array
{
return [
'success' => true,
'redirect' => '',
'message' => '',
];
}FriSay e-commerce project — write a module.
Rules: follow ALL rules in MODULE_DEVELOPER_GUIDE.md
PHP 7.4 compatible. Extend ModuleBase. Do NOT modify theme or core files.
Module info:
- name (slug): {my-module}
- title: {Display Name}
- description: {what it does}
Features:
- Display hooks: {footer, product_tab, ...}
- API actions: {action => description}
- Payment module: {yes/no}
- Database tables: {schema if any}
- Admin settings: {Settings keys or table fields}
Files to generate:
1. modules/{name}/{name}.php
2. install.sql / uninstall.sql (if needed)
3. assets/templates/admin/admin.tpl
4. assets/templates/front/{hook}.tpl (one per hook)
5. assets/css/, assets/js/ (if needed)
6. api/{action}.php (if needed)
7. front/{page}.php (if route needed)
Reference modules: whatsapp, newsletter, reviews, question, bankwire, sanalpos<?php
// Settings (use UPPERCASE keys with module prefix)
Settings::get('DOMAIN'); // site URL — NOT 'domain'
Settings::get('SITE_NAME');
Settings::set('MY_MODULE_KEY', 'value');
// Database
DB::getRowSafe('table', 'id = ?', [$id]);
DB::insert('table', ['col' => 'value']);
DB::update('table', ['col' => 'v'], 'id = :where_id', ['where_id' => $id]);
DB::execute('DELETE FROM table WHERE id = ?', [$id]);
// Store data
$product = Product::getById($idProduct); // includes 'url' field
$images = Product::getImages($idProduct);
$cart = Cart::getSummary();
$isFav = Favorite::isFavorite($idProduct);
// Customer
Customer::isLoggedIn();
Customer::getId();
Customer::getCurrent();
// Tools
Tools::displayPrice(99.90); // ₺99,90
Tools::createSlug('ana sayfa'); // ana-sayfa
Tools::maskName('John Smith'); // J** S****
// Admin URL
Admin::url('module-my-module'); // https://site.com/admin/module-my-module
// Pagination
$pagination = Pagination::build($total, $page, 30, Admin::url('module-my-module'));
// Mail
Mail::send($to, $subject, $bodyHtml); Modül sistemi iki tür kanca sağlar: olay kancaları (PHP geri çağrıları) ve görüntüleme kancaları (Smarty şablon enjeksiyon noktaları). Katalog kaynağı: core/Module.php içindeki Module::HOOKS, getHookCatalog() ve getDisplayHookCatalog().
Görüntüleme kancaları mağaza / yönetim şablonlarına
{$hooks.*} veya {$adminHooks.*} ile HTML basar.Olay Kancaları
Dinleyicileri modülünüzün boot() yönteminde Module::registerHook() ile kaydedin. Yalnızca Module::HOOKS listesinde yer alan kancalar kabul edilir.
| Kanca Adı | Ne Zaman Tetiklenir | Argümanlar |
|---|---|---|
smarty.assign | Module::bootstrap() sırasında — tüm etkin modüller yüklendikten sonra | $smarty örneği |
head.assets | Ön yüz CSS/JS varlıkları Module::getHeadAssets() ile toplanırken | css ve js anahtarlı &$assets dizisi |
footer.html | Eski alt bilgi HTML enjeksiyonu (bunun yerine görüntüleme kancalarını tercih edin) | — |
admin.menu | Yönetim kenar çubuğu menüsü oluşturulurken (registerAdminMenuLink veya registerHook) | &$items menü öğeleri dizisi |
order.placed | Yeni bir sipariş başarıyla oluşturulduktan sonra (ödeme / POS) | Sipariş veri dizisi |
order.updated | Sipariş güncellendiğinde (yönetim / API / durum değişikliği) | $order, önceki durum / bağlam ekleri |
product.updated | Ürün kaydedildiğinde (yönetim, Web API, hızlı stok, toplu araçlar) | $idProduct, $product, $isNew |
cart.changed | Sepet içeriği değiştikten sonra (ekle / güncelle / kaldır) | Cart::getSummary() sepet özet dizisi |
form.captcha.validate | Form gönderiminde CAPTCHA kontrolü (Captcha::validate()) | $form adı, &$error dizesi |
product.filter.sql | Katalog ürün SQL'i çalışmadan önce — modüller SQL / parametreleri değiştirebilir | &$sql, &$params, filtre bağlam dizisi |
catalog.filter.context | Ayrılmış katalog filtre bağlam kancası (HOOKS içinde kayıtlı) | Bağlam yükü (çağıranlar tarafından tetiklendiğinde) |
Görüntüleme Kancaları (Ön Yüz)
Yönetimde modül başına atanır ve module_display_hooks içinde saklanır. Çıktı Smarty'de {$hooks.hook_name} olarak kullanılabilir (noktalı adlar iç içe geçer, örn. {$hooks.head.top}).
| Kanca Adı | Konum | Smarty Değişkeni |
|---|---|---|
footer | Mağaza alt bilgisi | {$hooks.footer} |
header | Üst çubuk / üst bilgi | {$hooks.header} |
main_menu | Ana kategori menüsü | {$hooks.main_menu} |
mobile_menu | Mobil çekmece menüsü | {$hooks.mobile_menu} |
head.top | <head> içinde (üst) | {$hooks.head.top} |
home | Ana sayfa gövdesi | {$hooks.home} |
home_slider | Ana sayfa üst slayt | {$hooks.home_slider} |
home_promo_slider | Ana sayfa promosyon / kampanya slaydı | {$hooks.home_promo_slider} |
home_bottom | Ana sayfa alt bölümü | {$hooks.home_bottom} |
catalog_filters | Kategori / katalog filtreleri | {$hooks.catalog_filters} |
product | Ürün listesi bağlamı (id_product gerekir) | {$hooks.product} |
product_detail | Ürün detay sayfası | {$hooks.product_detail} |
product_tab | Ürün sekme düğmesi | {$hooks.product_tab} |
product_tab_content | Ürün sekme içerik paneli | {$hooks.product_tab_content} |
product_inf | Ürün detay bilgi bloğu | {$hooks.product_inf} |
order_payment | Ödeme adımı | {$hooks.order_payment} |
order_confirmation | Sipariş onay sayfası | {$hooks.order_confirmation} |
auth_social | Giriş / kayıt — sosyal düğmeler | {$hooks.auth_social} |
contact_form | İletişim formu — CAPTCHA / ek alanlar | {$hooks.contact_form} |
auth_login | Giriş formu — CAPTCHA | {$hooks.auth_login} |
auth_register | Kayıt formu — CAPTCHA | {$hooks.auth_register} |
Görüntüleme Kancaları (Yönetim)
Yönetim kancaları {$adminHooks.*} kullanır (Module::renderAdminHooks() ile). admin_ ile başlayan kancalar için modülünüzde renderAdminDisplayHook() uygulayın.
| Kanca Adı | Konum | Smarty Değişkeni |
|---|---|---|
admin_login | Yönetim giriş formu — CAPTCHA | {$adminHooks.admin_login} |
admin_product_button | Ürün düzenleme — Kaydet yanında | {$adminHooks.admin_product_button} |
admin_order_detail | Yönetim sipariş detayı | {$adminHooks.admin_order_detail} |
admin_header | Yönetim içerik üstü (tüm sayfalar) | {$adminHooks.admin_header} |
admin_footer | Yönetim alt bilgisi (tüm sayfalar) | {$adminHooks.admin_footer} |
admin_dashboard_top | Kontrol paneli — üst | {$adminHooks.admin_dashboard_top} |
admin_dashboard_kpi | Kontrol paneli — KPI kartlarının altı | {$adminHooks.admin_dashboard_kpi} |
admin_dashboard_main_left | Kontrol paneli — sol sütun | {$adminHooks.admin_dashboard_main_left} |
admin_dashboard_main_right | Kontrol paneli — sağ sütun | {$adminHooks.admin_dashboard_main_right} |
admin_dashboard_bottom | Kontrol paneli — sayfa altı | {$adminHooks.admin_dashboard_bottom} |
Module::refreshHook($smarty, $hookName, $context) veya yönetim render ile tembel yüklenen): product, product_tab, product_tab_content, product_inf, order_payment, order_confirmation, auth_social, admin_product_button, admin_order_detail, admin_header, admin_footer ve tüm admin_dashboard_* kancaları.Modülünüzde Görüntüleme Kancalarını Bildirme
Modül sınıfınızda hangi görüntüleme kancalarının desteklendiğini ve kurulumda varsayılan olarak hangilerinin etkinleştirileceğini tanımlayın:
$displayHooks— kanca adı => açıklama (yönetimde gösterilir)$defaultDisplayHooks—install()sırasında otomatik atanan kancalarrenderDisplayHook($hook, $context)— ön yüz HTMLrenderAdminDisplayHook($hook, $context)—admin_*kancaları için yönetim HTML
Yönetim Üzerinden Kanca Atama
Kanca atamaları Module::setDisplayHooks($moduleName, $hookNames) ile kaydedilir. Yalnızca hem $displayHooks hem de global görüntüleme kancası kataloğunda listelenen kancalar kabul edilir.
<?php
// ── Event hooks in boot() ───────────────────────────────────────
public function boot(): void
{
Module::registerHook('order.placed', function (array $order): void {
Logger::info('Order placed: ' . ($order['id_order'] ?? ''));
});
Module::registerHook('order.updated', function (array $order, $oldStatus = null): void {
// status / cargo / content edits
});
Module::registerHook('product.updated', function (int $idProduct, $product = null, $isNew = false): void {
// sync external catalog, clear caches, etc.
});
Module::registerHook('cart.changed', function (array $cart): void {
// abandoned-cart tracking, analytics
});
Module::registerHook('form.captcha.validate', function (string $form, string &$error): void {
// set $error when CAPTCHA fails
});
Module::registerHook('smarty.assign', function ($smarty): void {
if ($smarty) {
$smarty->assign('myModuleEnabled', true);
}
});
Module::registerHook('head.assets', function (array &$assets): void {
$assets['css'][] = 'https://cdn.example.com/extra.css';
});
}
// ── Display hooks: declare + render ────────────────────────────
public array $displayHooks = [
'footer' => 'Footer block',
'product_detail' => 'Product detail section',
'auth_login' => 'Login CAPTCHA',
];
public array $defaultDisplayHooks = ['footer'];
public function renderDisplayHook(string $hook, array $context = []): ?string
{
return match ($hook) {
'footer' => $this->renderFrontTemplate('footer', [
'message' => Settings::get('MY_MODULE_TEXT'),
]),
'product_detail' => $this->renderFrontTemplate('product_detail', [
'id_product' => $context['id_product'] ?? 0,
]),
default => null,
};
}{* assets/templates/front/footer.tpl *}
<div class="my-module-footer">
{if $message}
<p>{$message|escape}</p>
{/if}
</div><?php
// Call from product detail controller after $smarty is ready:
Module::refreshHook($smarty, 'product_detail', [
'id_product' => (int) $product['id_product'],
]);
// Smarty template:
// {$hooks.product_detail}<?php
public array $displayHooks = [
'admin_order_detail' => 'Extra block on order detail',
'admin_dashboard_top' => 'Dashboard banner',
];
public function renderAdminDisplayHook(string $hook, array $context = []): ?string
{
return match ($hook) {
'admin_order_detail' => $this->renderAdminTemplate('order-detail', $context),
'admin_dashboard_top' => $this->renderAdminTemplate('dashboard-top', $context),
default => null,
};
}<?php
// Minimum setup inside {name}.php:
public array $displayHooks = [
'footer' => 'Shown in store footer',
];
public array $defaultDisplayHooks = ['footer'];
public function renderDisplayHook(string $hook, array $context = []): ?string
{
if ($hook !== 'footer') {
return null;
}
return $this->renderFrontTemplate('footer', $context);
} Yaygın vitrin global'leri: denetleyicilerde/modüllerde PHP sütununu, tema şablonlarında Smarty (TPL) sütununu kullanın. Boş hücreler eşleşen karşılık olmadığını gösterir.
config/settings.php içinde atanır. Sayfa alanları (pageName, pageTitle, …) Page::add()'ten gelir. Ayar anahtarları veritabanında Settings::get() / Settings::set() ile tutulur.Site ve ayarlar
| PHP | Smarty (TPL) | Açıklama |
|---|---|---|
Settings::get('SITE_NAME') | {$siteName} | Mağaza / marka adı |
Settings::get('DOMAIN') veya $domain | {$domain} | Site temel URL'si (yapılandırmadaki sondaki eğik çizgi stiliyle) |
Settings::get('FOLDER') | {$rootDir} | Kurulum klasör yolu segmenti (alt klasör kurulumları) |
Settings::get('THEME') | {$activeTheme} | Aktif tema klasör adı |
Settings::get('SHOP_TOKEN') | Cron / modül API jetonu (TPL'de asla açığa çıkarmayın) | |
Settings::get('CONTACT_EMAIL') | {$contactEmail} | Herkese açık iletişim e-postası |
Settings::get('CONTACT_PHONE') | {$contactPhone} | Görüntülenen telefon |
Settings::get('CONTACT_PHONE_TEL') | {$contactPhoneTel} | tel: bağlantıları için telefon |
Settings::get('CONTACT_ADDRESS') | {$contactAddress} | Sokak adresi |
Settings::get('CONTACT_CITY') | {$contactCity} | Şehir |
Settings::get('CONTACT_COUNTRY') | {$addressCountry} | Ülke |
Settings::get('POSTAL_CODE') | {$postalCode} | Posta / ZIP kodu |
Settings::get('LATITUDE') | {$latitude} | Harita enlemi |
Settings::get('LONGITUDE') | {$longitude} | Harita boylamı |
Settings::get('OPEN_HOUR') | {$openHour} | Açılış saati |
Settings::get('CLOSE_HOUR') | {$closeHour} | Kapanış saati |
Settings::getFooterDescription() | {$footerDescription} | Alt bilgi hakkımızda metni |
Settings::get('FACEBOOK_LINK') | {$facebookLink} / {$socialLinks.facebook} | Facebook URL'si |
Settings::get('INSTAGRAM_LINK') | {$instagramLink} / {$socialLinks.instagram} | Instagram URL'si |
Settings::get('X_LINK') | {$xLink} / {$socialLinks.x} | X (Twitter) URL'si |
Settings::get('YOUTUBE_LINK') | {$youtubeLink} | YouTube URL'si |
Settings::get('LINKEDIN_LINK') | {$linkedinLink} | LinkedIn URL'si |
Settings::get('PINTEREST_LINK') | {$pinterestLink} | Pinterest URL'si |
Settings::get('TIKTOK_LINK') | {$tiktokLink} | TikTok URL'si |
Settings::get('SITE_VISIBILITY') | örn. members_only vitrin kapısı | |
Settings::get('MEMBER_APPROVAL') | manual = bekleyen üye hesapları | |
Settings::get('DEMO_MODE') | Demo kilidi (riskli yönetim işlemlerini engeller) |
Sayfa bağlamı
| PHP | Smarty (TPL) | Açıklama |
|---|---|---|
Page::add('home', $title, …) | {$pageName} | Geçerli sayfa anahtarı (örn. home, product, cart) |
Page::add() 2. argümanı | {$pageTitle} | Belge / SEO başlığı |
Page::add() 5. argümanı | {$pageDesc} | Meta açıklama |
Page::add() 3. / 4. argümanları | {$css} / {$js} | Sayfa başına ek CSS/JS dosya adları |
{$year} | Geçerli yıl (alt bilgi telif hakkı) | |
{$time} / {$minute} | Önbellek kırma zaman damgaları |
Müşteri, sepet ve oturum
| PHP | Smarty (TPL) | Açıklama |
|---|---|---|
Customer::isLoggedIn() | {$isLoggedIn} | Müşteri oturumunun var olup olmadığı |
Customer::getCurrent() (tipik) | {$customer} | Giriş yapmış müşteri dizisi / nesnesi |
Cart::getSummary() (tipik) | {$cart} | Sepet özeti (öğeler, toplamlar, adet) |
$_SESSION['csrf_token'] | {$token} | CSRF jetonu — JS genellikle başlıktan csrfToken okur |
{$saveToken} | Eski saatlik jeton (API'ler için CSRF tercih edin) | |
{$favoriteCount} | İstek listesi / favori sayısı | |
{$notificationCount} | Müşteri bildirim rozet sayısı | |
{$headerNotifications} | Üst bilgi bildirim listesi | |
{$cartI18n} / {$cartI18nJson} | Sepet arayüzü çeviri dizeleri (JS-güvenli JSON) |
Yollar ve varlıklar
| PHP | Smarty (TPL) | Açıklama |
|---|---|---|
_BASE_DIR_ | {$base_dir} | Dosya sistemi / temel dizin sabiti |
_THEME_BASE_DIR_ | {$tpl_dir} | Aktif tema şablon dizini |
_THEME_CSS_DIR_ | {$css_dir} | Tema CSS URL yolu |
_THEME_JS_DIR_ | {$js_dir} | Tema JS URL yolu |
_THEME_IMG_DIR_ | {$img_dir} | Tema görselleri URL yolu |
_BASE_IMG_DIR_ | {$base_img} | Ortak / ürün görselleri temeli |
_BASE_JS_DIR_ | {$base_js} | Ortak JS temeli |
_THEME_REEL_DIR_ | {$tRealDir} | Tema gerçek dosya sistemi yolu |
Performance::versionedUrl($url) | {'css/app.css'|asset_url} | Önbellek kıran varlık URL'si (?v=…) |
SiteAssets::resolveLogoUrl('header') | {$siteLogos.header} | Logo URL'leri (header, bar, footer) |
Theme::getColors($theme) | {$themeColors} | Tema renk değişkenleri dizisi |
{$themeOptions} | Tema seçenek bayrakları / yapılandırma | |
Module::… (toplu) | {$moduleAssets.css} / {$moduleAssets.js} | Aktif modül ön yüz CSS/JS URL listeleri |
Katalog, dil ve para birimi
| PHP | Smarty (TPL) | Açıklama |
|---|---|---|
{$menuCategories} | Üst bilgi / menü kategori ağacı | |
Cms::getFooterLinks() | {$cmsFooterLinks} | Alt bilgide bağlı CMS sayfaları |
Lang::getAvailable() | {$shopLanguages} | Etkin vitrin dilleri |
Lang::getDefault() | {$defaultLang} | Varsayılan dil kodu |
$_SESSION['selectLang'] | {$selectLang} | Geçerli dil kodu |
Lang::getSwitcherList() | {$langSwitcher} | Dil değiştirici girdileri |
Currency::getDisplayCurrency() | {$displayCurrency} | Müşteri görüntüleme para birimi |
Currency::getShopCurrency() | {$shopCurrencyCode} | Mağaza temel para birimi kodu |
Tools::displayPrice($price) | Geçerli görüntüleme para birimi için fiyat biçimlendirme | |
translate('Key') | {'Key'|translate} | Vitrin i18n dizesi |
Cargo::getDisplayHints() | {$freeShippingMin} / {$shippingFee} | Ücretsiz kargo eşiği ve kargo ücreti ipuçları |
Kancalar ve modüller
| PHP | Smarty (TPL) | Açıklama |
|---|---|---|
Module::getRenderedDisplayHooks() | {$hooks} | Tüm görüntüleme kancası HTML'i (örn. {$hooks.footer}) |
Module::refreshHook($smarty, 'product_detail', $ctx) | {$hooks.product_detail} | Geçerli sayfada ertelenmiş bir kancayı doldur |
Module::renderAdminHooks([…]) | {$adminHooks.admin_header} | Yönetim paneli görüntüleme kancaları |
{$newsletterApiUrl} | Varsayılan bülten abonelik API URL'si | |
Module::registerHook('order.placed', $cb) | Olay dinleyicisi (TPL değişkeni yok) |
Yönetim ve ortam (yalnızca PHP)
| PHP | Smarty (TPL) | Açıklama |
|---|---|---|
Admin::url('orders') | {$adminUrl} (yönetim şablonları) | config/env.php içindeki ADMIN_URI kullanan yönetim URL'si |
$_SESSION['admin_csrf_token'] / $adminToken | {$adminToken} | Yönetim CSRF jetonu |
adminT('Key') | {'Key'|adminT} | Yönetim paneli çevirisi |
FShop::VERSION | Çekirdek sürüm dizesi (yönetim alt bilgisi) | |
config/env.php → ADMIN_URI | Herkese açık yönetim yolu (klasör admin/ olarak kalır) | |
config/env.php → DB_*, REWRITE_BASE | Veritabanı ve rewrite temeli — şablonlar için değil |
{$isLoggedIn} veya {$cart} gibi global'lerin üzerine yazmamak için $this->renderFrontTemplate('hook', $vars) tercih edin. Üst Smarty değişkenleri modül TPL içinde yine de okunabilir.FriSay mağaza temaları proje kökündeki templates/ altında yer alır. Referans temalar arasında default, fyazilim, blue, restoran, nova ve shopmore (mobil öncelikli) bulunur. Kategori filtreleri çekirdek CatalogFilter kullanır (modül gerekmez).
Hızlı Başlangıç
- Bir referans tema klasörünü kopyalayın (örn.
templates/shopmoreveyatemplates/default). - Aynı
templates/dizinine yapıştırın. - Kopyayı tema adınıza yeniden adlandırın, örn.
fashion. - Yalnızca dosya içeriklerini düzenleyin — dosya veya klasörleri yeniden adlandırmayın.
- Temayı Yönetim → Temalar üzerinden etkinleştirin.
templates/default/ → kopyala → templates/fashion/- Tüm dosya ve klasör adlarını
defaultile birebir aynı tutun. - Yalnızca
.tpl,.cssve.jsdosyalarının içeriğini değiştirin. - Temanız mutlaka
header.tpliçermelidir; aksi halde tema listesinde görünmez. - Tema adlarında küçük harf, harf, rakam, tire veya alt çizgi kullanın (örn.
fashion,my-shop).
Tema Klasör Yapısı
templates/fashion/
header.tpl ← Main layout header (required)
footer.tpl ← Main layout footer
home.tpl ← Home page
product.tpl ← Product detail page
productList.tpl ← Product list partial
productGrid.tpl ← Product grid partial
catalog.tpl ← Category / catalog page
category.tpl
brand.tpl
cart.tpl
checkout.tpl
checkout-success.tpl
search.tpl
login.tpl
register.tpl
hesabim.tpl
favoriler.tpl
siparis.tpl
siparislerim.tpl
sifremi-unuttum.tpl
sifre-sifirla.tpl
contact.tpl
cms.tpl
special.tpl
truck.tpl
404.tpl
css/
bootstrap.min.css ← Do not rename
app.css ← Global styles — edit freely
colors.css ← Theme color variables (also editable in Admin)
home.css
product.css
cart.css
checkout.css
auth.css
js/
jquery-3.2.1.min.js ← Do not rename
bootstrap.min.js
popper.min.js
app.js ← Global scripts — edit freely
home.js
product.js
account.js
img/ ← Theme images (favicon, placeholders, etc.)
plugin/ ← Reusable partial templates
cart.tpl
pagination.tpl
headerTools.tpl
catalogToolbar.tpl
slide.tpl
schema-jsonld.tpl
left.tpl
cms/ ← CMS page layouts
hakkimizda.tpl
gizlilik.tpl
iade-degisim.tpl
mesafeli-satis.tpl
odeme-kargo.tpl
Ne Düzenlenir
| Dosya türü | Amaç | Yeniden adlandırılabilir mi? |
|---|---|---|
.tpl | HTML yapısı ve Smarty işaretlemesi — sayfa düzeni ve tasarımı kontrol eder | Hayır |
css/*.css | Renkler, boşluklar, tipografi, duyarlı düzen | Hayır |
js/*.js | Ön yüz etkileşimleri (sepet, ürün, hesap vb.) | Hayır |
css/colors.css | CSS değişkenleri (--brand-primary vb.) — her kurala dokunmadan hızlı marka renkleri | Hayır |
img/ | Görselleri değiştirin; şablonlarda referans varsa dosya adlarını koruyun | Adları korumak tercih edilir |
Temel Şablon Dosyaları
| Dosya | Açıklama |
|---|---|
header.tpl | Belge başı, CSS/JS dahil etmeleri, gezinme, sayfa gövdesini açar |
footer.tpl | Alt bilgi bağlantıları, betikler, sayfayı kapatır — {$hooks.footer} çıktılar |
home.tpl | Ana sayfa bölümleri, öne çıkan ürünler, slaytlar |
product.tpl | Ürün detay sayfası — sekmeler, galeri, sepete ekle |
catalog.tpl / category.tpl | Ürün listeleme sayfaları |
checkout.tpl | Ödeme akışı ve ödeme kancaları ({$hooks.order_payment}) |
plugin/productList.tpl | Birden fazla sayfada kullanılan ortak ürün kartı listesi |
Şablonlarda Smarty Değişkenleri
Tam PHP ↔ Smarty referansına bakın: Sabitler ve Değişkenler.
{$domain}— site temel URL'si{$css_dir}/{$js_dir}/{$img_dir}— tema varlık yolları{$siteName},{$pageName},{$pageTitle},{$pageDesc}{$hooks.footer},{$hooks.home_bottom}vb. — modül görüntüleme kancaları{$moduleAssets.css}/{$moduleAssets.js}— modül varlıkları{'path/file.css'|asset_url}— sürümlemeli varlık URL'si (önbellek güvenli)
Temanızı Etkinleştirme
- Yönetim → Temalar sayfasına gidin.
- Açılır listeden temanızı seçin (örn.
fashion). - Temayı Kaydet'e tıklayın — bu
THEMEayarını günceller. - İsteğe bağlı olarak aynı ekranda renkleri ayarlayın (
css/colors.cssdosyasına yazar). - Kaydetmeden önizleme:
{$domain}?theme_preview=fashion
Geliştirme İpuçları
- Büyük CSS dosyalarını yeniden yazmadan önce marka renkleri için
css/colors.cssile başlayın. - Önce
header.tplvefooter.tpldüzenleyin — her sayfayı etkilerler. - Kısmi şablonlar için
{include file='./plugin/cart.tpl'}kullanın — include yollarını değiştirmeyin. - Sayfa başına CSS: denetleyiciler
$cssayarlayabilir (örn.home.css) —header.tpliçinde otomatik yüklenir. - Büyük şablon değişikliklerinden sonra güncellemeler görünmezse Smarty önbelleğini temizleyin.
<?php
// Step 1 — Copy theme folder (command line example)
// cp -r templates/default templates/fashion
// Step 2 — Theme is auto-discovered when header.tpl exists:
// Theme::discover() scans templates/* folders (except admin)
// Step 3 — Activate programmatically (optional):
Theme::setActiveTheme('fashion');
// Step 4 — Per-page CSS in a controller:
$smarty->assign('css', 'home.css'); // loads templates/fashion/css/home.css
$smarty->display('home.tpl');{* templates/fashion/css/colors.css — brand colors (editable in Admin → Themes) *}
:root {
--brand-primary: #1a1a1a;
--brand-accent: #d6001c;
--surface: #ffffff;
--text-primary: #1a1a1a;
--footer-bg: #0c0c0c;
}<link rel="stylesheet" href="{$css_dir}bootstrap.min.css" />
<link rel="stylesheet" href="{$css_dir}app.css?v={$minute}" />
<link rel="stylesheet" href="{$css_dir}colors.css?v={$minute}" />
{if $css}
<link rel="stylesheet" href="{$css_dir}{$css}?v={$minute}" />
{/if}
<script src="{$js_dir}jquery-3.2.1.min.js"></script><section class="section-premium home-category-block">
<div class="section-head">
<h2 class="section-title">Best Sellers</h2>
<p class="section-subtitle">Discover our most popular products</p>
</div>
{include file='./productList.tpl' products=$featuredProducts}
</section>
{* Module display hooks *}
{$hooks.home}/* Use CSS variables from colors.css */
.section-title {
color: var(--brand-primary);
font-weight: 600;
}
.btn-primary {
background: var(--brand-accent);
border-color: var(--brand-accent);
}
.perks-strip .perk {
border-radius: 12px;
background: var(--surface-soft);
} Please sign in to start a topic or reply.
Bu tartışmaya yanıt yazmak ve etkileşimde bulunmak için lütfen hesabınıza giriş yapın.
Sign In







