Ana içeriğe atla

Rafplan API - Hizli Baslangic Kilavuzu

Bu kilavuz, Rafplan API'yi kullanmaya baslamak icin gereken tum adimlari icerir.

1. Kimlik Dogrulama

Tum API istekleri kimlik dogrulama gerektirir. API anahtarinizi Rafplan dashboard'undan olusturabilirsiniz:

  1. Rafplan Dashboard'a giris yapin
  2. Ayarlar > API Anahtarlari sayfasina gidin
  3. "Yeni Anahtar Olustur" butonuna tiklayin
  4. Anahtarinizi guvenli bir yerde saklayin (tekrar gosterilemez)

API anahtarinizi iki sekilde gonderebilirsiniz:

Authorization: Bearer rfp_live_xxxxxxxxxxxx
X-API-Key: rfp_live_xxxxxxxxxxxx
! API anahtarinizi herkese acik yerlerde (GitHub, frontend kodu vb.) paylasmayiniz. Anahtariniz ele gecirilirse derhal iptal edin ve yeni bir anahtar olusturun.

2. Ilk Istek

API'yi test etmek icin basit bir urun listesi istegi gonderelim:

curl -s -H "X-API-Key: rfp_live_xxxxxxxxxxxx" \
  "https://rafplan.com/api/v1/products?page=1&limit=5" | jq .
bash

Response:

{
  "data": [
    {
      "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "name": "Ulker Cikolatali Gofret 40g",
      "barcode": "8690504012345",
      "platformCode": "PLT-001234",
      "brandId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "brand": {
        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "name": "Ulker",
        "logoUrl": null,
        "status": "active"
      },
      "width": 12.5,
      "height": 8.0,
      "depth": 3.2,
      "weight": 0.04,
      "imageUrl": "https://storage.rafplan.com/products/img_001234.jpg",
      "createdAt": "2026-01-15T10:30:00.000Z",
      "updatedAt": "2026-03-20T14:45:00.000Z"
    }
  ],
  "meta": {
    "requestId": "req_a1b2c3d4-e5f6-7890",
    "timestamp": "2026-04-04T12:00:00.000Z",
    "page": 1,
    "limit": 5,
    "total": 150,
    "totalPages": 30
  }
}
json

3. Sayfalama (Pagination)

Liste endpoint'leri sayfalama destekler. `page` ve `limit` query parametreleri kullanilir.

ParameterAciklama
pageSayfa numarasi (1'den baslar, varsayilan: 1)
limitSayfa basi kayit sayisi (varsayilan: 20, maksimum: 100)

Response'daki `meta` objesi sayfalama bilgilerini icerir:

{
  "meta": {
    "requestId": "req_a1b2c3d4-e5f6-7890",
    "timestamp": "2026-04-04T12:00:00.000Z",
    "page": 2,
    "limit": 20,
    "total": 150,
    "totalPages": 8
  }
}
json

4. Hata Yonetimi

API hatalari tutarli bir formatta doner:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Gecersiz parametre degeri",
    "details": {
      "field": "limit",
      "issue": "Deger 1 ile 100 arasinda olmali"
    }
  }
}
json
HTTP StatusAdAciklama
400Bad RequestGecersiz istek parametreleri
401UnauthorizedGecersiz veya eksik API anahtari
403ForbiddenYetki yetersiz
404Not FoundKaynak bulunamadi
429Too Many RequestsRate limit asildi
500Internal Server ErrorSunucu hatasi

5. Rate Limiting

API istekleri tier bazli rate limit ile sinirlandirilir:

TierLimit
Starter60 istek/dk
Professional300 istek/dk
Enterprise1000 istek/dk

Response headers:

HeaderAciklama
X-RateLimit-LimitDakikadaki maksimum istek sayisi
X-RateLimit-RemainingKalan istek sayisi
X-RateLimit-ResetLimit sifirlama zamani (Unix timestamp)
Tip: Rate limit asimindan kacinmak icin isteklerinizi zamana yayarak gonderin ve cache'leme kullaninanin.

6. Webhook'lar

Webhook'lar ile planogram ve urun degisikliklerinden anlik haberdar olun.

EventAciklama
product.createdYeni urun olusturuldu
product.updatedUrun guncellendi
planogram.publishedPlanogram yayinlandi
planogram.assignedPlanogram subeye atandi

Webhook isteklerinin gercekligini dogrulamak icin HMAC-SHA256 imza kullanilir. `X-Webhook-Signature` header'indaki imza, paylasilan secret ile dogrulanir.

const crypto = require('crypto')

function verifyWebhookSignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex')
  return crypto.timingSafeEqual(
    Buffer.from(`sha256=${expected}`),
    Buffer.from(signature)
  )
}

// Express.js middleware ornegi
app.post('/webhooks/rafplan', (req, res) => {
  const signature = req.headers['x-webhook-signature']
  const isValid = verifyWebhookSignature(
    JSON.stringify(req.body),
    signature,
    process.env.WEBHOOK_SECRET
  )

  if (!isValid) {
    return res.status(401).json({ error: 'Invalid signature' })
  }

  const event = req.body
  console.log(`Event: ${event.type}`, event.data)
  res.status(200).json({ received: true })
})
javascript

7. Ornek Kodlar

Farkli dillerde API kullanim ornekleri:

cURL

# Urun listesi
curl -s -H "X-API-Key: rfp_live_xxxxxxxxxxxx" \
  "https://rafplan.com/api/v1/products?page=1&limit=10"

# Urun detayi
curl -s -H "X-API-Key: rfp_live_xxxxxxxxxxxx" \
  "https://rafplan.com/api/v1/products/f47ac10b-58cc-4372-a567-0e02b2c3d479"

# Sube listesi (sehir filtresi)
curl -s -H "X-API-Key: rfp_live_xxxxxxxxxxxx" \
  "https://rafplan.com/api/v1/branches?city=Istanbul&page=1&limit=20"

# Planogram detayi (raflar ve urunler dahil)
curl -s -H "X-API-Key: rfp_live_xxxxxxxxxxxx" \
  "https://rafplan.com/api/v1/planograms/c3d4e5f6-7890-1234-abcd-ef4567890123"
bash

TypeScript / JavaScript

const API_KEY = 'rfp_live_xxxxxxxxxxxx'
const BASE_URL = 'https://rafplan.com/api/v1'

// --- Tip Tanimlari ---

interface PaginationMeta {
  requestId: string
  timestamp: string
  page: number
  limit: number
  total: number
  totalPages: number
}

interface Product {
  id: string
  name: string
  barcode: string
  platformCode: string
  brandId: string | null
  width: number
  height: number
  depth: number
  weight: number | null
  imageUrl: string | null
  createdAt: string
  updatedAt: string
}

interface ApiResponse<T> {
  data: T
  meta: PaginationMeta
}

interface ApiError {
  error: { code: string; message: string; details?: Record<string, unknown> }
}

// --- API Client ---

async function apiRequest<T>(path: string): Promise<T> {
  const response = await fetch(`${BASE_URL}${path}`, {
    headers: { 'X-API-Key': API_KEY },
  })

  if (!response.ok) {
    const err: ApiError = await response.json()
    throw new Error(`[${err.error.code}] ${err.error.message}`)
  }

  return response.json()
}

// --- Kullanim ---

// Urun listesi
const products = await apiRequest<ApiResponse<Product[]>>(
  '/products?page=1&limit=10'
)
console.log(`Toplam ${products.meta.total} urun, sayfa ${products.meta.page}/${products.meta.totalPages}`)

// Urun detayi
const product = await apiRequest<{ data: Product; meta: { requestId: string } }>(
  '/products/f47ac10b-58cc-4372-a567-0e02b2c3d479'
)
console.log(product.data.name, product.data.barcode)

// Tum sayfalari iterasyon ile cek
async function fetchAllProducts(): Promise<Product[]> {
  const all: Product[] = []
  let page = 1

  while (true) {
    const res = await apiRequest<ApiResponse<Product[]>>(
      `/products?page=${page}&limit=100`
    )
    all.push(...res.data)
    if (page >= res.meta.totalPages) break
    page++
  }

  return all
}
typescript

Python

import requests

API_KEY = "rfp_live_xxxxxxxxxxxx"
BASE_URL = "https://rafplan.com/api/v1"

headers = {"X-API-Key": API_KEY}


def api_request(path: str, params: dict | None = None) -> dict:
    """API istegi gonder ve JSON dondur."""
    response = requests.get(f"{BASE_URL}{path}", headers=headers, params=params)
    response.raise_for_status()
    return response.json()


# Urun listesi
result = api_request("/products", {"page": 1, "limit": 10})
products = result["data"]
meta = result["meta"]
print(f"Toplam {meta['total']} urun, sayfa {meta['page']}/{meta['totalPages']}")

for product in products:
    print(f"  {product['name']} - {product['barcode']}")


# Urun detayi
detail = api_request("/products/f47ac10b-58cc-4372-a567-0e02b2c3d479")
p = detail["data"]
print(f"Urun: {p['name']}, Boyut: {p['width']}x{p['height']}x{p['depth']} cm")


# Sube listesi (sehir filtresi)
branches = api_request("/branches", {"city": "Istanbul", "page": 1, "limit": 20})
for branch in branches["data"]:
    print(f"  {branch['name']} ({branch['code']}) - {branch['city']}")


# Tum sayfalari cek
def fetch_all_products() -> list[dict]:
    """Tum urunleri sayfa sayfa ceker."""
    all_products = []
    page = 1

    while True:
        result = api_request("/products", {"page": page, "limit": 100})
        all_products.extend(result["data"])
        if page >= result["meta"]["totalPages"]:
            break
        page += 1

    return all_products


# Webhook imza dogrulama
import hashlib
import hmac


def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    """Webhook HMAC-SHA256 imzasini dogrula."""
    expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)
python