# Kişi hesapları

Contacts, storefront'unuza kişi hesapları kazandırır: kayıt, giriş, parola sıfırlama, profil ve kayıtlı adresler. Tüm rotalar `/v1/api/contacts` altındadır, yeniden adlandırmadan önce oluşturulmuş entegrasyonlar için eşdeğer `/v1/api/customers` üzerinde aynı alias sunulur. Contacts her mağazanın bir parçasıdır; önceden etkinleştirmeniz gereken bir modül yoktur.

## Alias: `/v1/api/customers`

Aşağıdaki her rota, eşdeğer `/v1/api/customers/...` path'inde de değişmeden sunulur (örneğin `POST /v1/api/contacts/register` yanında `POST /v1/api/customers/register`). Request ve response şekilleri her ikisinde de aynıdır. Bu alias kalıcıdır, bir kullanımdan kaldırma penceresi değildir: mevcut entegrasyonların asla geçiş yapması gerekmez; yeni entegrasyonların ise `/v1/api/contacts`'ı kullanması önerilir.

## İki token, iki header

Kişi endpoint'leri katmanlı bir kimlik doğrulama modeli kullanır:

1. **Tenant API anahtarı** (her zaman zorunlu). Her request, storefront API'nin geri kalanında olduğu gibi mağazanızın API anahtarını `Authorization: Bearer {api_key}` olarak taşır. Anahtar, tüm POST, PATCH ve DELETE endpoint'leri için `contacts:write`, GET endpoint'leri için `contacts:read` scope'una ihtiyaç duyar. Yeniden adlandırmadan önce oluşturulmuş bir anahtar bunun yerine `customers:write` / `customers:read` taşır ve yine de kabul edilir. Eksik bir scope `403 {"message":"Invalid ability provided."}` döner.
2. **Kişi oturum token'ı** (kişi kimliği doğrulanmış endpoint'lerde zorunlu). `POST /v1/api/contacts/register` ve `POST /v1/api/contacts/login` bir `token` dizesi döner. Sonraki request'lerde bunu `X-Customer-Token` header'ında gönderin. Eksik veya geçersiz bir kişi token'ı `401 {"message":"Invalid customer token."}` döner.

İki header birbirinden bağımsızdır ve kişi kimliği doğrulanmış rotalarda ikisi de zorunludur:

```
Authorization: Bearer {api_key}
X-Customer-Token: {token}
```

Hangi endpoint neye ihtiyaç duyar:

| Endpoint | API anahtarı scope'u | X-Customer-Token |
| --- | --- | --- |
| `POST /register` | `contacts:write` (`customers:write` kabul edilir) | Hayır |
| `POST /login` | `contacts:write` (`customers:write` kabul edilir) | Hayır |
| `POST /forgot-password` | `contacts:write` (`customers:write` kabul edilir) | Hayır |
| `POST /reset-password` | `contacts:write` (`customers:write` kabul edilir) | Hayır |
| `POST /logout` | `contacts:write` (`customers:write` kabul edilir) | Evet |
| `GET /me` | `contacts:read` (`customers:read` kabul edilir) | Evet |
| `PATCH /me` | `contacts:write` (`customers:write` kabul edilir) | Evet |
| `GET /me/addresses` | `contacts:read` (`customers:read` kabul edilir) | Evet |
| `POST /me/addresses` | `contacts:write` (`customers:write` kabul edilir) | Evet |
| `PATCH /me/addresses/{id}` | `contacts:write` (`customers:write` kabul edilir) | Evet |
| `DELETE /me/addresses/{id}` | `contacts:write` (`customers:write` kabul edilir) | Evet |

Kişi oturum token'larının süresi bir zamanlayıcıyla dolmaz. `POST /logout` (tek token) ve başarılı bir parola sıfırlama (kişinin tüm token'ları) ile iptal edilirler.

## Herkese açık kişi nesnesi

Kişiye dönük endpoint'ler bu yapıyı döner (`is_guest`, `type` ve `customer_group_id` gibi dahili alanlar storefront API'de asla açığa çıkarılmaz):

| Alan | Tür | Açıklama |
| --- | --- | --- |
| `id` | string (uuid) | Kişi id'si. Misafirden hesaba yükseltmede sabit kalır. |
| `name` | string | Ad soyad. |
| `email` | string or null | E-posta adresi, mevcutsa her zaman küçük harf. Mağaza başına benzersiz. Storefront üzerinden kayıt her zaman bir e-posta gerektirir; bir kişi yalnızca satıcı tarafından e-posta olmadan oluşturulduysa (örneğin klient panelinden veya CSV içe aktarımıyla eklenen bir şirket kişisi) null e-postaya sahip olabilir. |
| `phone` | string or null | Telefon numarası. |
| `kind` | string | `person` veya `company`. Bu API üzerinden salt okunur: storefront tarafından değil, satıcı tarafından ayarlanır. |
| `company_name` | string or null | Şirket adı. Yalnızca `kind` `company` olduğunda ayarlanır. Bu API üzerinden salt okunur. |
| `registration_number` | string or null | Şirket sicil numarası (örneğin Hollanda KvK numarası). Bu API üzerinden salt okunur. |
| `vat_id` | string or null | KDV numarası. Bu API üzerinden salt okunur. |
| `external_id` | string or null | Satıcı tarafından atanan tanımlayıcı, örneğin bir ERP veya muhasebe sistemi id'si. Bu API üzerinden salt okunur. |
| `display_name` | string | Varsa şirket adı, yoksa kişinin adı. Türetilmiş bir değerdir, saklanmaz. |
| `created_at` | string (ISO 8601) | Oluşturulma zaman damgası, örneğin `2026-08-14T10:32:11+00:00`. |

`kind`, `company_name`, `registration_number`, `vat_id` ve `external_id` her kişide okunabilir ancak `register` veya `PATCH /me` üzerinden yazılabilir değildir: bir storefront hesabı kendini bir şirket kişisine dönüştüremez. Bu alanları satıcı, client panelin Kişiler sayfalarından ayarlar.

## Misafir yaşam döngüsü

Commerce modülündeki sepet e-posta yakalama, yalnızca e-postası olan ve parolası olmayan bir kişi satırı (misafir) oluşturabilir. Bu üç yerde önem taşır:

- **Kayıt, misafirleri yükseltir.** Bir misafir satırına ait e-postayla kayıt olmak o satırı yerinde yükseltir: aynı `id`, sipariş geçmişi korunur. Yalnızca gerçek (misafir olmayan) bir hesaba ait e-posta `422 "Email already registered."` ile başarısız olur.
- **Misafirler giriş yapamaz.** Parolaları yoktur; bu nedenle giriş, genel `422 "Invalid credentials."` hatasıyla başarısız olur.
- **Parola sıfırlama da yükseltir.** Sıfırlamayı tamamlamak bir parola belirler, hesabı misafir olmayan olarak işaretler ve ayarlanmamışsa `email_verified_at` alanını ayarlar. Bu, misafir checkout'undan tam hesaba giden ikinci yoldur.

## Rate limit'ler

Tüm kişi endpoint'leri, mağaza başına dakikada 120 request'lik storefront limitini paylaşır (aşıldığında `Retry-After` header'ıyla 429). Dört kimlik bilgisi endpoint'i (`register`, `login`, `forgot-password`, `reset-password`), brute force denemelerini yavaşlatmak için istemci IP'si başına dakikada 10 request'lik ek bir limit taşır.

## Hatalar

Tüm hata zarfları standart storefront yapılarındadır:

| Durum | Gövde | Neden |
| --- | --- | --- |
| 401 | `{"message":"Unauthenticated."}` | Tenant API anahtarı eksik veya geçersiz. |
| 401 | `{"message":"Invalid customer token."}` | Kişi kimliği doğrulanmış bir rotada `X-Customer-Token` eksik veya geçersiz. |
| 403 | `{"message":"Invalid ability provided."}` | API anahtarı gerekli scope'tan yoksun. |
| 403 | `{"message":"Tenant suspended.","code":"tenant_suspended"}` | Mağaza askıya alınmış. |
| 404 | `{"message":"..."}` | Kaynak bulunamadı (veya kimliği doğrulanmış kişiye ait değil). |
| 422 | `{"message":"...","errors":{"field":["..."]}}` | Doğrulama hatası. |
| 429 | `{"message":"Too Many Attempts."}` | Rate limit aşıldı. |
