Müşteri Uçları

668 görüntülenme Markdown

Müşteri kaynağının yedi ucu: okuma, oluşturma, güncelleme, silme, kimlik bilgisi doğrulama ve oturum açma. Her biri için gönderilecek alanlar, dönen alanlar ve çalışan örnek.

Genel Bakış

Müşteri kaynağı, panelin Müşteriler ekranının API karşılığıdır. Aynı iş kurallarını uygular: bir müşteri oluştururken şifre uzunluğu ya da e-posta benzersizliği panelde ne ise burada da odur, çünkü uç panelin kullandığı işleyiciyi yeniden kullanır.

Yedi ucun tamamı admin kitlesine aittir ve anahtarın ilgili kapsamı taşımasını bekler. Kapsam adları aşağıda her ucun kimlik satırında yazılıdır.

Aynı uçlar WISECP içinden de çağrılır

Bir modül ya da eklenti geliştiriyorsanız HTTP'ye çıkmanız gerekmez: Api::Clients()->GetClients() aynı ucu süreç içinde çalıştırır ve aynı zarfı döndürür. Her örnekte ilk sekme bu çağrıyı gösterir.

Referans

Müşterileri Listeleme

get/api/v1/admin/clients
Clients/GetClients admin model users::list

Müşterileri filtreleyerek ve sayfalayarak listeler. Sayfa boyutu 100 ile sınırlıdır; daha büyük bir değer sessizce düşürülür.

Sorgu parametreleri 5
searchstringAd veya e-postada arama.
statusstringactive veya blocked. Tam liste: reference/statuses?entity=client.
group_idintMüşteri grubu kimliği. Liste: clients/groups.
pageintVarsayılan 1.
limitintVarsayılan 25, en çok 100.
Dönen alanlar data[] — her müşteri
idintMüşteri kimliği.
full_namestringAd soyad.
company_namestringŞirket adı; bireysel müşteride boş.
emailstringE-posta adresi.
phonestringTelefon; yalnız rakam saklanır.
statusstringactive veya blocked.
groupobjectMüşterinin bağlı olduğu grup.
idintGrup kimliği.
namestringGörünen ad.
languagestringDil kodu.
country_codestringISO ülke kodu, örneğin TR.
currency_codestringPara birimi kodu, örneğin TRY.
email_verifiedboolE-posta doğrulanmış mı.
phone_verifiedboolTelefon doğrulanmış mı.
active_servicesintKullanımdaki hizmet sayısı.
created_atstringOluşturma zamanı.
last_login_atstringSon giriş zamanı.
Sayfalama meta
metaobjectHer liste ucunda aynıdır.
totalintToplam kayıt sayısı.
pageintBulunulan sayfa.
limitintSayfa boyutu.
next_pageintSonraki sayfa; yoksa 0.
Hatalar 1
insufficient_scope403Anahtar gerekli kapsamı taşımıyor.
İstek
curl -G 'https://panel.ornek.com/api/v1/admin/clients' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Accept: application/json' \
  -d status=active \
  -d limit=25
const url = new URL('https://panel.ornek.com/api/v1/admin/clients');
url.searchParams.set('status', 'active');
url.searchParams.set('limit', '25');

const res  = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();

for (const c of body.data) console.log(c.id, c.full_name);
$url = 'https://panel.ornek.com/api/v1/admin/clients?' . http_build_query([
    'status' => 'active',
    'limit'  => 25,
]);

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Accept: application/json',
    ],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);

foreach ($body['data'] as $client) {
    echo $client['id'], ' ', $client['full_name'], PHP_EOL;
}
// WISECP içinden: HTTP yok, aynı zarf döner.
$response = Api::Clients()->GetClients([], [
    'status' => 'active',
    'limit'  => 25,
]);

if (isset($response['error'])) {
    Logger::error($response['error']['message']);
    return;
}

foreach ($response['data'] as $client) {
    echo $client['id'], ' ', $client['full_name'], PHP_EOL;
}
Yanıt
{
  "data": [
    {
      "id": 42,
      "full_name": "Ayse Yilmaz",
      "company_name": "",
      "email": "[email protected]",
      "phone": "5550100",
      "status": "active",
      "group": { "id": 1, "name": "Standart" },
      "email_verified": true,
      "phone_verified": false,
      "active_services": 3,
      "created_at": "2026-01-01 10:00:00",
      "last_login_at": "2026-06-20 09:00:00"
    }
  ],
  "meta": { "total": 128, "page": 1, "limit": 25, "next_page": 2 }
}
{
  "error": {
    "code": "insufficient_scope",
    "message": "API key lacks the required scope."
  }
}

Müşteri Detayı

get/api/v1/admin/clients/{id}
Clients/GetClient admin model users::get

Tek bir müşterinin tam profilini döndürür. Listede olmayan alanlar burada gelir: ad ve soyad ayrı ayrı, bakiye, ülke ve para birimi kimlikleri.

Yol parametresi 1
idintzorunluMüşteri kimliği.
Dönen alanlar 15
idintMüşteri kimliği.
full_namestringAd soyad.
namestringAd. Listede yalnız full_name gelir.
surnamestringSoyad.
statusstringactive, blocked veya cancelled. Listede cancelled geçmez.
country_idintÜlke kimliği. Karşılığı: reference/countries.
currency_idintPara birimi kimliği.
group_idintMüşteri grubu kimliği. Listedeki group nesnesinin yerine burada yalnız kimlik gelir.
balancefloatHesap bakiyesi.
company_namestringŞirket adı.
emailstringE-posta adresi.
phonestringTelefon.
languagestringDil kodu.
created_atstringOluşturma zamanı.
last_login_atstringSon giriş zamanı.
Hatalar 2
not_found404Müşteri bulunamadı.
insufficient_scope403Anahtar gerekli kapsamı taşımıyor.
İstek
curl 'https://panel.ornek.com/api/v1/admin/clients/42' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Accept: application/json'
const res  = await fetch('https://panel.ornek.com/api/v1/admin/clients/42', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.ornek.com/api/v1/admin/clients/42');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->GetClient(['id' => 42]);

if (isset($response['error'])) {
    // not_found ya da insufficient_scope
    return false;
}

$client = $response['data'];
echo $client['name'], ' ', $client['surname'], PHP_EOL;

Müşteri Oluşturma

post/api/v1/admin/clients
Clients/CreateClient admin panelle aynı işleyici

Yeni müşteri oluşturur ve oluşturulan kaydı detay şemasıyla döndürür. İş kuralları panelin kullandığı işleyicide çalışır, bu yüzden şifre uzunluğu gibi ayarlar burada da geçerlidir.

Gövde 15 alan, 3'ü zorunlu
full_namestringzorunluAd soyad.
emailstringzorunluBiçimi doğrulanır ve kurulumdaki tüm müşteriler arasında benzersiz olmalıdır.
passwordstringzorunluEn az options/password-length karakter; varsayılan 6.
typestringindividual veya corporate. Varsayılan individual.
phonestringTelefon. Yalnız rakamlar saklanır, biçim işaretleri atılır.
languagestringDil kodu. Varsayılan general/local.
group_idintMüşteri grubu. Liste: clients/groups.
country_codestringISO ülke kodu, örneğin TR. Karşılığı: reference/countries.
currency_codestringPara birimi kodu, örneğin USD.
companyobjectKurumsal müşteride vergi bilgileri.
namestringTicari unvan.
tax_numberstringVergi kimlik numarası.
tax_officestringVergi dairesi.
identitystringKimlik ya da vergi numarası.
marketing_notificationsboolPazarlama bildirimlerini açar.
verify_emailboolE-postayı doğrulanmış işaretler.
verify_phoneboolTelefonu doğrulanmış işaretler.
send_welcome_emailboolKarşılama e-postası gönderir.
Dönen alanlar data — 15
dataobjectOluşturulan müşteri, 201 ile döner. Detay ucuyla aynı şekil.
Hatalar 6
full_name_required422Ad soyad boş gönderildi.
email_invalid422E-posta biçimi geçersiz.
email_exists422Bu e-posta zaten kayıtlı.
password_required422Şifre boş gönderildi.
create_failed500Kayıt oluşturulamadı.
insufficient_scope403Anahtar gerekli kapsamı taşımıyor.
İstek
curl -X POST 'https://panel.ornek.com/api/v1/admin/clients' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"full_name":"Ayse Yilmaz","email":"[email protected]","password":"Str0ngP@ssw0rd"}'
const res = await fetch('https://panel.ornek.com/api/v1/admin/clients', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    full_name: 'Ayse Yilmaz',
    email: '[email protected]',
    password: 'Str0ngP@ssw0rd',
  }),
});

const body = await res.json();
$ch = curl_init('https://panel.ornek.com/api/v1/admin/clients');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'full_name' => 'Ayse Yilmaz',
        'email'     => '[email protected]',
        'password'  => 'Str0ngP@ssw0rd',
    ]),
]);

$created = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->CreateClient([
    'full_name'     => 'Ayse Yilmaz',
    'email'         => '[email protected]',
    'password'      => 'Str0ngP@ssw0rd',
    'type'          => 'individual',
    'country_code'  => 'TR',
    'currency_code' => 'TRY',
]);

if (isset($response['error'])) {
    // email_exists en sık karşılaşılan durumdur.
    throw new Exception($response['error']['message']);
}

$clientId = $response['data']['id'];
Yanıt
{
  "data": {
    "id": 43,
    "full_name": "Ayse Yilmaz",
    "name": "Ayse",
    "surname": "Yilmaz",
    "email": "[email protected]",
    "status": "active",
    "country_id": 792,
    "currency_id": 1,
    "group_id": 0,
    "balance": 0.00
  }
}
{
  "error": {
    "code": "email_exists",
    "message": "A client with this email already exists."
  }
}

Müşteri Güncelleme

patch/api/v1/admin/clients/{id}
Clients/UpdateClient admin kısmi güncelleme

Yalnızca gönderdiğiniz alanı değiştirir; göndermediğiniz alana dokunmaz. Tüm gövde alanları opsiyoneldir.

Gövde 17 alan, hepsi opsiyonel
full_namestringAd soyad. Gönderilirse boş olamaz.
emailstringGeçerli ve benzersiz e-posta.
statusstringactive, blocked veya cancelled.
phonestringCep telefonu. Boş gönderilirse silinir.
landline_phonestringSabit telefon. Boş gönderilirse silinir.
typestringindividual veya corporate.
identitystringKimlik ya da vergi numarası.
birthdaystringDoğum tarihi. Boş ya da geçersiz bir değer alanı siler.
languagestringDil kodu.
group_idintMüşteri grubu.
currency_codestringPara birimi kodu, örneğin USD.
companyobjectKurumsal bilgiler.
namestringTicari unvan.
tax_numberstringVergi kimlik numarası.
tax_officestringVergi dairesi.
faturalama işaretleribooltrue işareti açar, false kaldırır, alanı hiç göndermemek değiştirmez.
tax_exemptionboolVergiden muaf tutar.
never_suspendboolHizmetleri asla askıya almaz.
never_cancelboolHizmetleri asla iptal etmez.
separate_invoicesboolHer hizmeti ayrı faturalandırır.
never_late_feeboolGecikme ücreti uygulamaz.
Dönen alanlar data — 15
dataobjectMüşterinin güncel hâli. Detay ucuyla aynı şekil.
Hatalar 6
not_found404Müşteri bulunamadı.
full_name_required422Ad soyad boş gönderildi.
email_invalid422E-posta biçimi geçersiz.
email_exists422Bu e-posta zaten kullanımda.
phone_invalid422Telefon numarası geçersiz.
insufficient_scope403Anahtar gerekli kapsamı taşımıyor.
İstek
curl -X PATCH 'https://panel.ornek.com/api/v1/admin/clients/42' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"status":"blocked","never_suspend":false}'
const res = await fetch('https://panel.ornek.com/api/v1/admin/clients/42', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ status: 'blocked', never_suspend: false }),
});

const body = await res.json();
$ch = curl_init('https://panel.ornek.com/api/v1/admin/clients/42');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PATCH',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'status'        => 'blocked',
        'never_suspend' => false,
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Yalnız iki alan gönderiliyor; kalan profil olduğu gibi kalır.
$response = Api::Clients()->UpdateClient([
    'id'            => 42,
    'status'        => 'blocked',
    'never_suspend' => false,
]);

Müşteri Silme

delete/api/v1/admin/clients/{id}
Clients/DeleteClient admin geri alınamaz

Müşteriyi siler ve silinen kimliği geri döndürür.

Dönen alanlar data
deletedboolSilme başarılı mı.
idintSilinen müşterinin kimliği.
Hatalar 2
not_found404Müşteri bulunamadı.
insufficient_scope403Anahtar gerekli kapsamı taşımıyor.
İstek
curl -X DELETE 'https://panel.ornek.com/api/v1/admin/clients/42' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.ornek.com/api/v1/admin/clients/42', {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.ornek.com/api/v1/admin/clients/42');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'DELETE',
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->DeleteClient(['id' => 42]);

if (($response['data']['deleted'] ?? false) === true) {
    // Kayıt gitti; ona bağlı kendi verinizi de temizleyin.
}

Müşteri Kimlik Bilgilerini Doğrulama

post/api/v1/admin/clients/validate
Clients/ValidateClient admin hiçbir şey yazmaz

Tek bir soruyu yanıtlar: bu e-posta ve parola canlı bir müşteri hesabına mı ait? Hiçbir şey oluşturmaz, değiştirmez. Kimlik bilgilerini kendi arayüzünüz topluyorsa ve arkasındaki müşteri kimliğine ihtiyacınız varsa kullanılır.

Saklanan parola hiçbir zaman dönmez

Kurulumun kendi şifrelemesiyle sarılmış bir bcrypt özetidir. Dönseydi yönetici anahtarına sahip herkes tahminlerini çevrimdışı doğrulayabilirdi. Yanıt yalnızca müşteri kimliğini taşır.

Gövde 2 alan, ikisi de zorunlu
emailstringzorunluHesabın e-posta adresi.
passwordstringzorunluDoğrulanacak parola.
Dönen alanlar data — 2
validboolHer zaman true. Eşleşmeyen kimlik bilgisi valid: false olarak değil, hata olarak döner.
user_idintKimlik bilgilerinin ait olduğu müşteri.
Hatalar 3
credentials_required422email ya da password eksik.
invalid_credentials422Bu ikiliyle eşleşen canlı bir müşteri hesabı yok.
insufficient_scope403Anahtar gerekli kapsamı taşımıyor.
İstek
curl -X POST 'https://panel.ornek.com/api/v1/admin/clients/validate' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"email":"[email protected]","password":"correct horse battery staple"}'
const res = await fetch('https://panel.ornek.com/api/v1/admin/clients/validate', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ email: '[email protected]', password }),
});

if (res.ok) {
  const body = await res.json();
  console.log(body.data.user_id);          // kimlik bilgilerinin arkasındaki müşteri
}
$ch = curl_init('https://panel.ornek.com/api/v1/admin/clients/validate');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'email'    => '[email protected]',
        'password' => $password,
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->ValidateClient([
    'email'    => '[email protected]',
    'password' => $password,
]);

// Yanlış ikili hata olarak döndüğü için bu satıra ulaşmak zaten eşleştiler demektir.
$clientId = $response['data']['user_id'] ?? 0;

Müşteri Adına Oturum Açma

post/api/v1/admin/clients/sso
Clients/CreateClientSsoToken admin tek kullanımlık, 60 saniye

Bir müşteri için tek kullanımlık giriş bileti üretir ve bileti harcayan adresi döndürür. Müşteriyi o adrese gönderirsiniz, giriş yapmış olarak varır. Entegrasyonunuz parolayı hiç görmez.

Ziyaretçinin kim olduğunu zaten bilen sistemler içindir: kendi portalınız, bir kontrol paneli, bir destek aracı. login_as_client PHP oturumunu yerinde değiştirdiğinden API'ye açılmaz; bu uç onun bilet tabanlı karşılığıdır.

Biletin davranışı

Tek kullanımlık — adres açıldığı anda harcanır. Tekrar kullanılan bağlantı açıklamalı bir mesajla giriş formuna düşer. 60 saniye geçerli, üretilip hemen izlenmek üzere tasarlandı. Müşteri başına bir tane: yeni bilet önceki bileti sessizce geçersiz kılar. Bu kuruluma sınırlı, başka bir yeri gösteren destination yok sayılır ve müşteri panosuna düşer. Giriş kapısı da yine işler. Hesap durumu, ülke engeli ve modül vetoları bilet harcanırken parola girişindeki gibi değerlendirilir. Bilet kim olduğunu kanıtlar, şu anda giriş yapabilir mi sorusunu değil.

Gövde 3 alan, 1 zorunlu
client_idintzorunluBilet üretilecek müşteri. user_id eski adı olarak hâlâ kabul edilir.
destinationstringMüşterinin giriş sonrası ineceği yer: bu kurulumdaki mutlak bir adres ya da services gibi bir route anahtarı. Verilmezse panosu.
destination_valuesarraydestination bir route anahtarıysa route parametreleri.
Dönen alanlar data — 3
tokenstringBilet, {client_id}-{gizli} biçiminde.
urlstringBileti taşıyan giriş adresi; müşteriyi buraya gönderin.
expires_atstringSon geçerlilik, ISO 8601 + saat farkı.
Hatalar 5
client_id_required422client_id eksik.
client_not_found404Bu kimlikle müşteri yok.
client_not_active422Hesap pasif, engelli ya da giriş yapılamaz durumda.
sso_ticket_failed422Anahtar üretilemedi.
insufficient_scope403Anahtar gerekli kapsamı taşımıyor.
İstek
curl -X POST 'https://panel.ornek.com/api/v1/admin/clients/sso' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"client_id":42,"destination":"services","destination_values":[128]}'
const res = await fetch('https://panel.ornek.com/api/v1/admin/clients/sso', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ client_id: 42, destination: 'services', destination_values: [128] }),
});

const body = await res.json();

window.location = body.data.url;         // hemen harcayın; ömrü 60 saniye
$ch = curl_init('https://panel.ornek.com/api/v1/admin/clients/sso');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'client_id'          => 42,
        'destination'        => 'services',
        'destination_values' => [128],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->CreateClientSsoToken(['client_id' => 42]);

$link = $response['data']['url'];

Tuzaklar

Liste ile detay aynı şemayı döndürmez

Listede grup bir nesnedir (group), detayda yalnız kimliktir (group_id). Ad soyad da listede tek alandır, detayda ikiye ayrılır. Listeden okuyup detay bekleyen bir eşleyici sessizce boş alan üretir.

Güncellemede boş değer silmektir

Telefon ve doğum tarihi alanlarında boş bir değer göndermek alanı siler. Bir alanı korumak istiyorsanız onu gövdeye hiç koymayın; kısmi güncelleme tam da bunun içindir.

Telefon yazdığınız gibi saklanmaz

Biçim işaretleri atılır ve yalnız rakamlar kalır. +90 555 010 00 00 gönderirseniz geri okuduğunuzda 905550100000 alırsınız; karşılaştırma yapan kod bunu hesaba katmalıdır.

Faydalı oldu mu?

Geri bildiriminiz için teşekkürler!

Hâlâ Yardıma mı İhtiyacınız Var?

Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.