Skip to main content
Payven API’leri OAuth 2.0 Client Credentials akışı ile çalışır. Backend’inizden Identity servisinden bir access token alır, ürün API’lerine Authorization: Bearer <token> header’ı ile istek atarsınız. İki kısa adım:
  1. Token alclient_id + client_secret ile Identity’den access_token iste.
  2. API’yi çağırAuthorization: Bearer <access_token> header’ı ile Sanal POS, Para Transferi, Fraud veya Identity endpoint’lerini kullan.
Token süresi dolduğunda yenisini alır veya refresh_token ile yenilersin. Kod örnekleri bu döngüyü sizin için yönetir.

Akış (özet)

1. Adım — Token al

POST https://identity.payven.com.tr/api/v1/auth/{slug}/token
Content-Type: application/json
{slug} = tenant slug’ınız (örn. payven). Onboarding sırasında size verilir.
curl https://identity.payven.com.tr/api/v1/auth/payven/token \
  -H "Content-Type: application/json" \
  -d '{
    "client_id":     "pvk_live_xxxxxxxxxxxxxxxxxxxx",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'
Yanıt (200 OK):
{
  "access_token":        "eyJhbGciOiJSUzI1NiI...",
  "refresh_token":       "eyJhbGciOiJIUzI1NiI...",
  "expires_in":          300,
  "refresh_expires_in":  0,
  "token_type":          "Bearer",
  "scope":               "openid profile email"
}
AlanAnlam
access_tokenAPI çağrılarında kullanacağınız Bearer token (imzalı JWT formatında)
refresh_tokenAccess token expire olunca yenisini almak için
expires_inAccess token kaç saniye geçerli (varsayılan 300s = 5 dakika)
refresh_expires_inRefresh token süresi (0 = idle timeout uygulanır, bkz. tablo)
token_typeDaima Bearer
scopeTahsis edilen scope’lar (kimlik bilgisi üst kümesi). Endpoint-bazlı yetki bu alan üzerinden değil, kullanıcı/anahtar rollerinizden çözümlenir

2. Adım — API’yi çağır

curl https://vpos.payven.com.tr/api/v1/payments \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiI..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1001-payment" \
  -d '{
    "external_id": "ORDER-1001",
    "amount":      { "amount": 15000, "currency": "TRY" },
    "installment": 1,
    "card": {
      "holder_name":  "Test Kullanici",
      "number":       "4546711234567894",
      "expire_month": "12",
      "expire_year":  "2030",
      "cvv":          "000"
    }
  }'
Ek bir merchant header’ı göndermeniz gerekmez; merchant bağlamı token’dan çözümlenir. Birden fazla merchant adına işlem yapıyorsanız X-Merchant-Id ile override edebilirsiniz — bkz. Sanal POS → Kimlik Doğrulama.

3. Adım — Token yenile

Access token expire olduğunda (expires_in süresi geçince) yenisini almak için refresh_token ile:
curl https://identity.payven.com.tr/api/v1/auth/payven/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "eyJhbGciOiJIUzI1NiI..."
  }'
Yanıt yine access_token + (rotasyonlu) refresh_token döner. Eski refresh_token artık geçersizdir.

Token süreleri

TokenVarsayılan süreAnlam
Access token5 dakika (300s)API isteklerinde kullanılır. Süre dolunca refresh ile yenilenir.
Refresh token30 gün (idle)30 gün hareketsiz kalırsa expire olur. Düzenli kullanımda her refresh’te rotate edilir, pratikte expire olmaz. Şüpheli durumda client_credentials ile baştan token alın.
Pratik öneri: Access token’ı expiry’sine 60 saniye kala yenileyin (clock-skew + network latency için margin). Aşağıdaki kod örnekleri bunu otomatik yapar.
Token cache’leyin — her API çağrısında yeniden token almayın. Identity üzerinde rate limit vardır (token endpoint’i IP başına dakikada 10). Token’ı bellekte tutup expire olana kadar yeniden kullanın.

Kod örnekleri (auto-refresh)

class PayvenClient {
  constructor({ slug, clientId, clientSecret, identityBaseUrl }) {
    this.slug = slug;
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.identityBaseUrl = identityBaseUrl;
    this.accessToken = null;
    this.refreshToken = null;
    this.expiresAt = 0;
  }

  async getToken() {
    if (this.accessToken && Date.now() < this.expiresAt - 60_000) {
      return this.accessToken;
    }

    if (this.refreshToken && (await this.tryRefresh())) {
      return this.accessToken;
    }

    const res = await fetch(`${this.identityBaseUrl}/api/v1/auth/${this.slug}/token`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        client_id: this.clientId,
        client_secret: this.clientSecret,
      }),
    });
    if (!res.ok) throw new Error(`Token alınamadı: ${res.status}`);
    const data = await res.json();
    this.accessToken = data.access_token;
    this.refreshToken = data.refresh_token;
    this.expiresAt = Date.now() + data.expires_in * 1000;
    return this.accessToken;
  }

  async tryRefresh() {
    const res = await fetch(`${this.identityBaseUrl}/api/v1/auth/${this.slug}/refresh`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ refresh_token: this.refreshToken }),
    });
    if (!res.ok) {
      this.refreshToken = null;
      return false;
    }
    const data = await res.json();
    this.accessToken = data.access_token;
    this.refreshToken = data.refresh_token;
    this.expiresAt = Date.now() + data.expires_in * 1000;
    return true;
  }

  async call(productBaseUrl, path, init = {}) {
    const token = await this.getToken();
    return fetch(productBaseUrl + path, {
      ...init,
      headers: {
        ...(init.headers ?? {}),
        Authorization: `Bearer ${token}`,
      },
    });
  }
}

Hata response’ları

HTTPCodeAnlamÇözüm
400bad_requestRequest body geçersiz JSON veya zorunlu alan eksikBody’nin geçerli JSON olduğunu ve client_id + client_secret alanlarının dolu gönderildiğini kontrol edin
401invalid_credentialsclient_id / client_secret yanlışKonsoldan API anahtarınızı kontrol edin
401invalid_tokenAccess token geçersizRefresh akışını çalıştırın
401token_expiredAccess token süresi geçtiRefresh edin
401invalid_refresh_tokenRefresh token expire / revokeYeni client_credentials ile baştan token alın
403realm_suspendedTenant askıya alınmışMüşteri ilişkileriniz ile iletişime geçin
404realm_not_found{slug} geçersizOnboarding’de verilen slug’ı kontrol edin
429rate_limit_exceededToken endpoint’ine çok istekRetry-After header’ına uyun, token’ı cache’leyin
Tam yanıt formatı için: Hata Yönetimi.

Güvenlik kuralları

client_secret server-side saklanır — frontend kodunuza, mobil uygulamanıza, public repo’ya asla koymayın.
Token cache bellek-içi — diske yazmayın; restart sonrası yeniden token alın.
HTTPS zorunlu — HTTP istekleri reddedilir.
Token rotasyonu — refresh otomatik yapar; manuel rotation gerekmez. Şüpheli durumda Identity’den client’ın secret’ını rotate edin.
Loglarda maskeleaccess_token, refresh_token, client_secret değerlerini log’lara yazmayın.

SSS

Hayır — Payven iki adımlı bir akış kullanır: önce client_id + client_secret ile kısa ömürlü bir access token alırsınız, sonra her API çağrısında bu token’ı kullanırsınız. Üstteki kod örneklerindeki istemci sınıfı bu adımı sizin için saklar; tek bir call(...) ile çağırırsınız.
Standart yapılandırmada 5dk access / 30 gün refresh idle. Daha uzun süre ihtiyacı için müşteri başarısı ekibine ulaşın — offline_access scope ile expire olmayan refresh token tahsis edilebilir.
Evet — Identity’den alınan access token, planınızdaki tüm aktif ürünlerde geçerlidir. Sanal POS, Para Transferi, Fraud, Identity — hepsi aynı token ile çağrılır.
Sandbox endpoint’leri ayrı bir tenant slug ile yayında: https://identity-sandbox.payven.com.tr ve https://vpos-sandbox.payven.com.tr. Sandbox client_id’leri canlıdan ayrıdır; üretim parasıyla işlem yapılmaz, kart numarası test BIN’leri kullanır.