Yönlendirme, “bu ödeme hangi POS’tan geçsin?” sorusunun cevabıdır. Poyra’nın cevabı iki iddiada toplanır: karar deterministiktir (aynı ödeme, aynı veriyle her zaman aynı kapıya gider) ve açıklanabilirdir (neden o kapıya gittiği ödemenin üzerine yazılır).

Karar sırası

  1. Aktif hesaplar öncelik sırasıyla toplanır. Hiç yoksa karar boştur ve ödeme routing.no_route ile reddedilir.
  2. Sağlık filtresi: canary yoklamasının Down işaretlediği hesaplar elenir; skipUnhealthy korumasıyla yalnızca Healthy olanlara da daraltabilirsiniz. Kaç hesabın atlandığı karar gerekçesine yazılır.
  3. Kural eşleşmesi: aktif kural dokümanındaki kurallar sırayla denenir, ilk eşleşen kazanır. Koşulsuz kural her zaman eşleşir.
  4. Kural eşleşmediyse hacim bölüşümü: ödeme kimliğinin SHA-256’sından 0–99 arası bir kova türetilir ve kümülatif yüzdeye göre hesap seçilir. Rastgele sayı yoktur — aynı ödeme her değerlendirmede aynı kovaya düşer, karar sonradan yeniden üretilebilir.
  5. O da yoksa doküman stratejisi devreye girer (varsayılan: öncelik sırası).

Kural dili

Kural dokümanı JSON’dur ve sürümlenir:
Hesaplar etiketle ya da kimlikle referanslanır; bilinmeyen referans sessizce atlanır. Koşullarda kullanılabilecek olgular:
Kart sinyalleri yalnızca biliniyorsa değerlendirilir. Hosted akışta müşteri BIN girmediyse kart kuralları eşleşmez ve değerlendirme sıradaki kurala ya da stratejiye geçer — “bilinmeyen kart” hiçbir kuralı yanlışlıkla tetiklemez.

Stratejiler

Kural sabit bir rota vermek yerine sıralamayı bir stratejiye bırakabilir: Performans sinyalleri son 7 günden hesaplanır ve en az 20 örnek yoksa güvenilmez sayılır. Sinyali olmayan hesap elenmez, sıralamada sona alınır — yeni açılan bir POS “veri yok” diye dışlanmaz ama öne de geçmez. Eşitlikte giriş sırası korunur; karar her koşulda deterministiktir.

Yük devretme zinciri

Nihai zincir üç parçadan kurulur: seçilen rota + dokümandaki fallback listesi + kalan tüm uygun adaylar. guards.maxAttempts (varsayılan 2) kaç adayın fiilen deneneceğini sınırlar. Devretmenin ne zaman mümkün olduğu akışa bağlıdır:
  • Hosted akışta yalnızca başlatma aşamasında. Bankaya form üretilirken hesap erişilemezse sıradaki adaya geçilir. Müşteri bankanın sayfasına gittikten sonra artık dönüş beklenir; geriye sarma yoktur.
  • Direct akışta banka cevabına göre. Yalnızca yeniden denenebilir kodlarda (connector_unavailable, issuer_unavailable, processing_error) sıradaki POS denenir. Kart reddi devretmez: limit yetersizliği kartın kararıdır, başka POS’tan denemek sonucu değiştirmez ve kart deneme desenine benzer.
  • Taksidi desteklemeyen ya da o taksit için şeması olmayan hesap deneme bile açılmadan atlanır.
Her devretme adımı ayrı bir attempt ve failover: true işaretli bir attempt.failed olayı bırakır — zincir zaman çizelgesinde eksiksiz görünür.

Açıklanabilirlik: “neden bu bankaya gitti?”

Karar iki katmanda saklanır. İnsan için gerekçe cümlesi:
Kural eşleşti: yüksek tutar → İş POS — strateji: en düşük komisyon
Hacim bölüşümü: kova %37 → İş POS
Strateji: en yüksek başarı oranı — İş POS başarı oranı %97,2
Makine için de kararın tamamı ödemenin üzerine JSON olarak yazılır: eşleşen kural ve sürümü, strateji, değerlendirilen adaylar ve her adayın o anki sinyalleri (beklenen komisyon, başarı oranı, ortanca gecikme). GET /v1/payments/{id}/timeline bu kaydı olay zinciriyle birlikte döner; panelin ödeme detayı da aynı kaydı gösterir. Aylar sonra “bu işlem neden Garanti’den geçti?” sorusunun cevabı, o günkü verilerle birlikte ödemenin üzerindedir.

Kural sürümleme ve simülasyon

Kurallar silinmez, sürümlenir:
  • POST /v1/routing/rules yeni sürümü pasif oluşturur.
  • POST /v1/routing/rules/{id}/activate anında etkinleştirir; geri almak, eski sürümü yeniden etkinleştirmektir.
  • GET /v1/routing/rules/active ve GET /v1/routing/rules okuma uçlarıdır.
Canlıya almadan önce POST /v1/routing/simulate, aday dokümanı geçmiş işlemler üzerinde koşturur ve mevcut kuralla farkını gösterir — hangi işlemler başka kapıya giderdi, maliyet nasıl değişirdi. Panelde bu işin görsel karşılığı Yönlendirme tasarımcısı ekranıdır.