# Başka satıcılar için uygulama yapmak — davetli

> **Ne yaparsınız:** Başka satıcıların onayıyla onların verisine erişen bir uygulama kaydeder; OAuth ile izin alır, belirteci yeniler ve iptal edersiniz.
> **Hangi uçlar:** `GET /oauth/authorize`, `POST /oauth/token`, `POST /oauth/revoke`; bağlantıyı doğrulamak için [Bağlantıyı doğrula](https://developer.siparis.app/uc/baglantiyi-dogrula)
> **Gereken izin:** Satıcının onay ekranında verdiği kapsamlar.

API adresi `https://api.siparis.app/v1`. Uygulamalar OAuth 2.1 Authorization Code + PKCE S256 kullanır.
Kişisel API anahtarı istemeyin. Davetli geliştirme erişimi için destek@siparis.app ile görüşün.

## 1. Uygulamanızı kaydedin

Kurucu e-posta adresinizi davetli geliştirici olarak tanımlar. Aynı adresi doğruladığınız hesabınızla
Geliştirici > Uygulamalarım > Uygulama kaydet bölümünü açın. Ad, açıklama, web sitesi, destek e-postası,
gizlilik politikası, ISO 3166-1 alfa-2 barındırma ülkesi, dönüş adresleri ve gerekli kapsamları (uygulamanın
isteyeceği erişim izinleri) girin.
Uygulama sözleşmesi ve KVKK taahhüdünü okuyup ayrı ayrı kabul edin. Logo adresi isteğe bağlıdır.

Sunucunuz sırrı saklayabiliyorsa gizli istemci seçin: `client_secret` yalnız ilk yanıtta gösterilir,
veritabanında SHA-256 özeti tutulur. Tarayıcı veya mobil uygulama açık istemcidir, sır almaz.
İki türde de PKCE zorunludur. Sırları sohbet, kaynak kodu, URL veya günlüklerde saklamayın.

Mevcut API sözleşmesi gereği CORS kapalıdır: web uygulamasında belirteç ve `/v1` çağrılarını kendi
sunucunuzdan yapın. Tarayıcı PKCE üretebilir ve onay sayfasına yönlenebilir; başka kökenden doğrudan
API çağrısı yapamaz. Mobil ve masaüstü istemciler doğrudan HTTPS kullanabilir.

Dönüş adresi HTTPS olmalıdır. Yerel geliştirmede yalnız `http://127.0.0.1:8765/callback` veya
`http://localhost:8765/callback` gibi açık portlu adres kabul edilir. Yol, sorgu ve port dahil birebir eşleşir;
alt alan, sonuna eklenen `/` veya sorgu da farklı adrestir. Joker, fragment ve kullanıcı bilgisi kabul edilmez.

## 2. PKCE ve state üretin, satıcıyı onaya yönlendirin

Her akış için yeni, kriptografik rastgele 43–128 karakterli `code_verifier` ve en az 128 bit `state` üretin.
`code_challenge = BASE64URL(SHA256(code_verifier))`; base64 dolgu karakterlerini (`=`) kaldırın.
Verifier ve state'i işlemi başlatan kullanıcının oturumunda saklayın; birbirine karıştırmayın.

Python (standart kütüphane):

```python
import base64, hashlib, secrets
from urllib.parse import urlencode
verifier = secrets.token_urlsafe(32)
state = secrets.token_urlsafe(32)
challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).rstrip(b'=').decode()
params = dict(client_id='app_UYGULAMA_KIMLIGI', response_type='code',
              redirect_uri='https://example.com/callback', scope='orders:read products:read',
              state=state, code_challenge=challenge, code_challenge_method='S256')
print('https://siparis.app/oauth/authorize?' + urlencode(params))
```

TypeScript (Web Crypto):

```ts
const b64 = (bytes: Uint8Array) => btoa(String.fromCharCode(...bytes))
  .replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
const verifier = b64(crypto.getRandomValues(new Uint8Array(32)));
const state = b64(crypto.getRandomValues(new Uint8Array(32)));
const challenge = b64(new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))));
const params = new URLSearchParams({ client_id: 'app_UYGULAMA_KIMLIGI', response_type: 'code',
  redirect_uri: 'https://example.com/callback', scope: 'orders:read',
  state, code_challenge: challenge, code_challenge_method: 'S256' });
const authorizationUrl = `https://siparis.app/oauth/authorize?${params}`;
```

Yalnız işletme sahibi izin verir. Tek markada seçim görünmez; birden çok markada sahibi bir marka seçer.
İzinler insan diliyle gösterilir; kişisel veri izinleri kırmızı başlık altında ayrı onay ister.
Türkiye dışında barındırmada KVKK m.9 uyarısı vardır. Onay tek başına hukuki aktarım güvencesi değildir.

## 3. Dönüşü doğrulayın ve kodu belirtece çevirin

Önce gelen `state` değerini oturumda sakladığınız değerle karşılaştırın; yoksa veya farklıysa işlemi durdurun.
`iss` değeri `https://api.siparis.app` olmalıdır. `error=access_denied` satıcının reddidir.
Başarılı dönüşteki `code` tek kullanımlıktır ve **60 saniye** geçerlidir. Verifier URL'ye yazılmaz.

```sh
curl https://api.siparis.app/oauth/token \
  --data-urlencode grant_type=authorization_code \
  --data-urlencode client_id="$CLIENT_ID" \
  --data-urlencode code="$CODE" \
  --data-urlencode redirect_uri=https://example.com/callback \
  --data-urlencode code_verifier="$VERIFIER"
```

Gizli istemcide ayrıca `--user "$CLIENT_ID:$CLIENT_SECRET"` kullanın (HTTP Basic).
Alternatif `client_secret` form alanıdır; iki yöntemi aynı istekte kullanmayın. Açık istemci sır göndermez.
Başarılı yanıtta `access_token` (`sat_`), `token_type=Bearer`, `expires_in` (en çok 3600 saniye),
`refresh_token` (`srt_`) ve boşlukla ayrılmış `scope` döner. Belirteçler opaktır; JWT gibi çözmeye çalışmayın.

```sh
curl https://api.siparis.app/v1/me -H "Authorization: Bearer $ACCESS_TOKEN"
```

`/me` yanıtında `actor_type=app`, `application.id`, `application.name` ve `application.installation_id`
vardır. Mevcut bütün `/v1` uçları aynı kapsam ve marka denetimlerini uygular. Yazma kapsamı okuma vermez.
Tekrar anahtarları (`Idempotency-Key`: aynı isteği güvenle yeniden göndermek için ürettiğiniz metin) ve hız sayacı
kurulumun bağlantı kimliğine (satıcının uygulamanıza verdiği iznin kalıcı kaydı) bağlıdır; belirteç yenilenince sıfırlanmaz.

## 4. Yenileyin

```sh
curl https://api.siparis.app/oauth/token \
  --data-urlencode grant_type=refresh_token \
  --data-urlencode client_id="$CLIENT_ID" \
  --data-urlencode refresh_token="$REFRESH_TOKEN"
```

Gizli istemci her yenilemede de doğrulanır. Her başarılı yanıt yeni erişim ve yenileme belirteci verir.
İkisini aynı işlemde güvenle saklayın. Eski yenileme belirtecini tekrar kullanmak **bütün aileyi**, daha önce
verilmiş erişim belirteçleri dahil iptal eder. Paralel yenilemeleri uygulamanızda tekilleştirin; eşzamanlı iki
istekte biri başarılı olsa bile diğeri aileyi kapatır. Yanıt kaybolursa eski belirteci tekrar tekrar denemeyin;
yeniden kullanıcı onayı alın. Ailenin mutlak ömrü 30 gündür; döndürme bu süreyi uzatmaz.
Kapsam değişikliği ve yeniden kurulum yeni onay gerektirir; önceki kurulumun erişimi kapanır, geçmişi kalır.

## 5. İptal edin

```sh
curl https://api.siparis.app/oauth/revoke \
  --data-urlencode client_id="$CLIENT_ID" \
  --data-urlencode token="$REFRESH_TOKEN"
```

Gizli istemci yine doğrulanır. Erişim veya yenileme belirteci sunulabilir; ikisi de aileyi kapatır.
Bilinmeyen belirteçte de boş `200` döner (RFC 7009). İşletme sahibi Entegrasyonlar > Bağlı uygulamalar'dan
bütün kurulumu kaldırabilir; AI da onay kartıyla kaldırır. Kurucu uygulamayı askıya aldığında bütün
kurulumlar ve belirteçler hemen kapanır; kayıtlar korunur.

## Sık hatalar

| Hata | Yapılacak iş |
| --- | --- |
| `invalid_request` | state, S256, tekil parametreler, form türü ve birebir dönüş adresini kontrol edin. |
| `invalid_client` | İstemci kimliği/sırrını ve uygulamanın askıda olmadığını kontrol edin. |
| `invalid_grant` | Kod kullanılmış/eski, verifier yanlış veya aile iptal edilmiş olabilir; yeniden onay alın. |
| `invalid_scope` | Uygulama kaydındaki izinleri isteyin; yenilemede izin büyütmeyin. |
| `insufficient_scope` (403, `/v1`) | Sahibinden gereken kapsam için yeni onay alın. |
| 429 | `Retry-After` süresince bekleyin; yenilemeyi eşzamanlı çalıştırmayın. |

Metadata: `GET https://api.siparis.app/.well-known/oauth-authorization-server` (RFC 8414).
OpenAPI `securitySchemes.oauth2` Authorization Code akışını tanımlar. Kişisel veri ve sorumluluklar:
[Kapsamlar](https://developer.siparis.app/kapsamlar), [Uygulama sözleşmesi ve KVKK taahhüdü](https://developer.siparis.app/uygulama-sozlesmesi).
