Bu yazıyı yazma sebebimiz: Trendyol, Hepsiburada ve N11 API entegrasyonlarında aynı sorunları defalarca gördük. Tekrar tekrar aynı hatayı yapmayın diye.
Ortak Sorunlar
1. Rate Limiting
Her pazaryerinin farklı rate limit politikası var ve bunlar dokümantasyonda her zaman net değil:
- Trendyol: ~200 istek/dakika (deneyimlerimize göre)
- Hepsiburada: Daha sıkı, başarısız istekler 429 yerine bazen timeout atıyor
- N11: En liberal ama en az stabil
Çözüm: Merkezi bir request queue oluşturun. Token bucket algoritması ile rate limit yönetimi, her kanal için ayrı bucket.
2. 5xx Sıklığı
Özellikle yoğun dönemlerde (kampanya günleri, alışveriş sezonu) tüm pazaryerlerinde 5xx response oranı dramatik artar. Bunu plan dışı bırakmak ciddi veri kaybına yol açar.
// Retry with exponential backoff
async function fetchWithRetry(
url: string,
options: RequestInit,
maxRetries = 3
): Promise<Response> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options)
if (response.ok || response.status < 500) return response
if (attempt === maxRetries) return response
} catch (error) {
if (attempt === maxRetries) throw error
}
const delay = Math.min(1000 * 2 ** attempt, 30000)
await new Promise(resolve => setTimeout(resolve, delay))
}
throw new Error('Unreachable')
}3. Webhook Güvenilirliği
Pazaryerleri webhook gönderir ama:
- Tekrar göndermez (fire-and-forget)
- Gönderim garantisi yoktur
- Bazen dakikalarca gecikir
Çözüm: Webhook'u alın ama ana veri kaynağı olarak kullanmayın. Polling ile senkronize edin, webhook sadece anlık bildirim olsun.
Mimari Öneri: Unified Ingestion
Tüm pazaryerlerinden gelen siparişleri tek bir formata dönüştüren adapter katmanı:
interface UnifiedOrder {
externalId: string
marketplace: 'trendyol' | 'hepsiburada' | 'n11'
status: OrderStatus
customer: Customer
items: OrderItem[]
shipping: ShippingInfo
createdAt: Date
}
abstract class MarketplaceAdapter {
abstract fetchOrders(since: Date): Promise<UnifiedOrder[]>
abstract updateOrderStatus(id: string, status: string): Promise<void>
abstract updateStock(sku: string, quantity: number): Promise<void>
}Bu pattern ile pazaryerine bağımlılığı soyutlarsınız. Yeni bir pazaryeri eklemek = yeni bir adapter yazmak.
Stok Senkronizasyonu: Race Condition'ı Nasıl Önlersiniz?
En büyük tehlike: Aynı ürüne iki farklı kanaldan eş zamanlı sipariş geldiğinde overselling.
Çözüm: Optimistic locking veya Redis atomic operations
// Redis ile atomic stok düşme
async function decrementStock(sku: string, quantity: number): Promise<boolean> {
const key = `stock:${sku}`
const result = await redis.eval(
`
local current = tonumber(redis.call('GET', KEYS[1])) or 0
if current >= tonumber(ARGV[1]) then
redis.call('DECRBY', KEYS[1], ARGV[1])
return 1
end
return 0
`,
[key],
[quantity]
)
return result === 1
}Event-driven stok güncellemesi yerine Redis atomic operasyonları + veritabanı transaction kombinasyonunu tercih ediyoruz. Daha basit, daha öngörülebilir.
Monitoring
Çoklu entegrasyonda monitoring olmadan debug etmek imkânsız. OpenTelemetry ile her API çağrısını trace edin:
- Hangi pazaryeri kaçıncı denemede başarılı oldu?
- Rate limit kaç kez tetiklendi?
- Ortalama response time ne?
Bu verilere üretim ortamında sahip olmak, sorunları müşteri şikayetiyle değil kendiniz fark etmenizi sağlar.