Payment Orchestrator
Webhook'lar

Ödeme Webhook'ları

Ödemeler durumlarını asenkron değiştirir: 3-D Secure doğrulaması yönlendirmeden dakikalar sonra tamamlanır, bir capture takasa girer, bir yetkilendirme gece süresi dolar, bir ters ibraz haftalar sonra gelir. Webhook'lar bu yaşam döngüsü değişikliklerinin her birini sunucunuza push eder — böylece GET /payments/:id endpoint'ini döngüde poll etmeniz gerekmez ve sipariş durumunuz ödeme durumundan sessizce kopamaz.

Bu sayfa hangi ödeme eventlerinin var olduğunu ve ne zaman tetiklendiğini anlatır. Endpoint yapılandırması, imza doğrulama ve genel teslimat sözleşmesi tüm ürünlerde ortaktır — bkz. Webhook kurulumu.

Ödeme eventleri

On beş event tipi ödeme yaşam döngüsünün tamamını kapsar; ödeme linklerinin dokuz yaşam döngüsü event'i aşağıda, senetle ödemenin senet event'leri ayrıca listelenir:

EventNe zaman tetiklenir
payment.authorizedYalnız-authorize hold başarılı: tutar kartta bloke, para henüz çekilmedi (AUTHORIZED). Bir hold asla payment.captured üretmez — teslimat için capture'ı bekleyin.
payment.capturedPara sizde — siparişi bunun üzerine teslim edin. Tahsilatın gerçekleştiği her rayda atar: anında satışta (kart, 3-D veya cüzdan) ya da açık bir capture kümülatif çekimi yetkilendirilen tutarın tamamına getirdiğinde (CAPTURED). Fiilen çekilen tutarı (capturedAmountMinor) taşır ve muhasebe kaydı bu event'ten doğar. Parçalı ödemede ana ödemenin sonucu da bu event'le gelir: settlementMode: "SPLIT" ve legs[] taşır.
payment.partially_capturedKısmi capture tamamlandı (PARTIALLY_CAPTURED). capturedThisCaptureMinor, cumulativeCapturedMinor, remainingCapturableMinor taşır.
payment.failedÖdeme başarısız oldu, para çekilmedi (FAILED). amountMinor ve varsa ret detayı taşır (normalizedErrorCode, retryability, failureSource). Parçalı ödemede ana ödemenin başarısız sonucu da bu event'le gelir (settlementMode: "SPLIT").
payment.refundedİade ödemeyi tam-iadeye getirdi (REFUNDED). refundId, refundAmountMinor, totalRefundedMinor, fullyRefunded taşır.
payment.partially_refundedKısmi iade tamamlandı (PARTIALLY_REFUNDED). Alanları payment.refunded ile aynıdır.
payment.voidedCapture edilmemiş bir yetkilendirme serbest bırakıldı (VOIDED)
payment.expiredCapture edilmemiş bir yetkilendirme 7 gün boyunca çekilmedi ve platform tarafında EXPIRED (terminal) olarak kapatıldı. Karttaki bloke bu event'le kalkmaz — bankaya iptal gönderilmez; bloke bankanın kendi süresiyle düşer. Hold'u erken serbest bırakmak için payment.voided'a yol açan void çağrısını kullanın.
payment.split.completedParçalı ödeme: bütün parçalar çekildi (COMPLETED). splitPaymentId ve parça özetleri taşır. Aynı anda ana ödeme için payment.captured de gelir; siparişi bir kez, o event'le kapatın. Parçaların kendi event'leri yalnız payment.split.leg_events seçeneği açıksa gönderilir → Parça olayları
payment.split.voidedParçalı ödemenin telafisi tamamlandı: bir parça başarısızdı, provizyonlar bırakıldı ve çekilmiş parçalar iade edildi — müşteri net sıfır ödedi (VOIDED)
payment.split.compensatingParçalı ödeme operasyon takibine düştü (COMPENSATING + reconciliation): cause alanı CAPTURE_FAILURE veya COMPENSATION_INCOMPLETE; parçalı ödeme başına bir kez ateşlenir
payment.split.outcome_pendingParçalı ödemede geri alınamayan parçanın sonucu bekleniyor; kart provizyonları sonuç netleşene kadar tutulur. pendingLegId ve pendingLegSequence taşır
payment.split.reconciliation_requiredParçalı ödemede geri alınamayan parça çekildi ama siparişin kalanı tahsil edilemedi; teslimata siz karar verirsiniz. shortfallAmountMinor, reason ve varsa unsettledLegId taşır
payment.split.leg_refund_pendingParçalı ödemenin çekilmiş bir parçası için telafi iadesi istendi; iade başarılı olana kadar denenir. Parça event'idir: yalnız payment.split.leg_events seçeneği açıksa gönderilir
payment.disputedTers ibraz açıldı ya da faz değiştirdi — chargebackId, disputeState, disputedAmountMinor, openChargebackCount taşır; bkz. Ters İbraz

Tek bir ödeme yaşamı boyunca genellikle birden fazla event üretir — örneğin payment.authorized → payment.captured → aylar sonra payment.disputed.

⚠️

Teslimatı payment.captured'a bağlayın. Tahsilatın gerçekleştiği tek sinyal budur — kart, 3-D ve cüzdan raylarının hepsinde.

payment.succeeded KALDIRILDI ve artık üretilmiyor. Bu event payment.captured ile aynı ödemeyi aynı intentId/orderId ile anlatıyordu; ikisine birden abone olan entegrasyonlar tek satış için iki webhook alıyor ve siparişi orderId üzerinden işliyorlarsa iki kez işliyorlardı. Kodunuzda type === 'payment.succeeded' dalı varsa payment.captured'a taşıyın; tutar alanı amountMinor değil capturedAmountMinor'dır.

Ödeme linki event'leri

Linkle Ödeme linklerinin yaşam döngüsü dokuz event üretir: payment.link.created, payment.link.updated, payment.link.paused, payment.link.resumed, payment.link.deactivated, payment.link.expired, payment.link.completed, payment.link.taken_down, payment.link.takedown_reverted.

Linkle alınan ödemeler yukarıdaki ödeme event'lerini üretir; bu event'lerin data'sına source: "PAYMENT_LINK", paymentLinkId, paymentLinkCode ve paymentLinkAttemptId alanları eklenir. Siparişi yine payment.captured ile teslim edin — payment.link.completed ikinci bir para sinyali değildir. Payload ve alanlar: Linkle Ödeme Webhook'ları.

Senet event'leri

Senetle Öde ile alınan ödeme, senetler düzenlendiğinde payment.captured üretir; tahsilat ise vadede, borçludan doğrudan sizin sağlayıcı hesabınıza yapılır. Her senedin vadedeki sonucu ayrı bir event'tir:

EventNe zaman tetiklenir
payment.promissory_note.paidBir senet ödendi
payment.promissory_note.protestedBir senet ödenmedi ve protesto edildi
payment.promissory_note.cancelledBir senet iptal edildi

Bu event'ler senet ve durum başına bir kez gönderilir, ödemenin durumunu değiştirmez (status CAPTURED kalır) ve senedin alanlarını data.promissoryNote içinde taşır: serialNumber, sequence, amountMinor, dueDate, status, occurredAt. Senet durumları sağlayıcıdan düzenli aralıklarla okunur; event anlık değildir. Panelde bu event'ler Senetle Ödeme grubundadır. Payload ve okuma sıklığı: Senetle Öde — Webhook'lar.

Teslimat payload'u

Her teslimat, endpoint'inize şu şekilde bir JSON gövdeyle yapılan HTTP POST'tur:

{
  "id": "6a1f0c9e-2b3d-5e4f-8a7b-1c2d3e4f5a6b",
  "type": "payment.captured",
  "sourceService": "payment-service",
  "organizationId": "org_…",
  "productCode": "PAYMENT_GATEWAY",
  "environment": "LIVE",
  "eventVersion": 1,
  "schemaVersion": "v1",
  "createdAt": "2026-07-06T12:00:02.000Z",
  "data": {
    "intentId": "9c6f2b1a-4e8d-4a3f-b5e6-2d7c1a9b8e30",
    "merchantId": "m_…",
    "orderId": "ORD-84722",
    "currency": "TRY",
    "status": "CAPTURED",
    "capturedAmountMinor": "10050"
  }
}

data her zaman intentId, merchantId, orderId, currency ve status taşır; buna yukarıdaki tabloda listelenen event'e özgü alanlar eklenir. data içindeki tutar alanları minor-unit string'dir ve çekilen tutarı gösterir: taksitli ödemede, isteğinizdeki amount'a (sepet toplamı) sistemin eklediği vade farkı dahildir.

Parçalı ödemenin ana ödeme ve payment.split.* event'lerinde data ayrıca splitPaymentId taşır. intentId yalnız Checkout Formu parçalı ödemesinin ana ödeme event'lerinde bulunur ve siparişin ödeme kaydıdır; API ile başlatılan parçalı ödemede ana ödemenin kimliği splitPaymentId'dir → Parçalı Ödeme › Olaylar.

status, createdAt anındaki durumdur — "şu anki" durum değil.

Bir teslimat başarısız olursa 12 denemeye kadar, toplam ~2 gün boyunca yeniden denenir; event sırası da garanti edilmez. Bu yüzden status alanı event'in anlattığı andaki durumu bildirir. Teslimat kararı için bu yeterlidir (gövde imzalıdır, id ile idempotent işleyin). Ödemenin güncel durumunu görmek için GET /payments/:id çağırın.

Teslimat header'ları:

HeaderDeğer
x-lunixi-event-idEvent id'si (gövdedeki id ile aynı) — dedupe anahtarı
x-lunixi-event-typeEvent tipi (gövdedeki type ile aynı)
x-lunixi-signatureVirgülle ayrılmış imza listesi: bir veya iki v2=<hex HMAC> öğesi (ikincisi yalnız 24 saatlik secret-rotasyonu grace penceresinde) — aşağıya bakın
x-lunixi-signature-timestampISO-8601 imzalama zamanı

v2 imzası ham gövdenin HMAC'idir: HMAC-SHA256(secret, eventId + "." + timestamp + "." + rawBody) — parse yok, yeniden serileştirme yok. Başlık bir veya iki v2= öğesi taşır; ikincisi yalnız secret rotasyonunun 24 saatlik grace penceresinde bulunur. Doğrulama kodu ve detaylar: Webhook kurulumu.

Teslimat modeli

Teslimatlar platform genelindeki webhook sözleşmesine uyar (detaylar Webhook kurulumu sayfasında):

  • İmzalı — her teslimat, endpoint'inizin webhook secret'ıyla hesaplanan x-lunixi-signature header'ını taşır. Gövdeye güvenmeden önce imzayı doğrulayın; doğrulamayı geçemeyen teslimatları reddedin.
  • Retry'lı — endpoint'iniz 2xx dönmezse teslimat backoff ile tekrar denenir. Handler'ınızı hızlı tasarlayın: önce onaylayın, işlemeyi asenkron yapın.
  • At-least-once — retry'lar aynı eventi birden fazla kez görebileceğiniz anlamına gelir. Handler'ları idempotent yapın: işlemeyi event id'sine (ya da (paymentId, event) çiftine) anahtarlayın ve tekrarları yok sayın.
  • Sırasız — eventlerin gerçekleşme sırasıyla geleceğini varsaymayın. Mantığınızı event geliş sırasından değil, ödemenin güncel status değerinden yürütün (şüphedeyseniz GET /payments/:id ile teyit edin).

Bir eventi işleme

İmzayı doğrulayın

Webhook secret'ınızla beklenen imzayı hesaplayın ve teslimattaki imzayla karşılaştırın. Eşleşmeyen her şeyi atın.

Hızlı onaylayın

Eventi kaydettikten hemen sonra 2xx dönün. Yavaş olan her iş (e-posta, sipariş teslimi, defter kayıtları) arka plan işine aittir — yavaş bir handler başarısızlık gibi görünür ve retry tetikler.

İdempotent işleyin

Bu event id'sini daha önce işleyip işlemediğinizi kontrol edin. İşlediyseniz 2xx dönüp hiçbir şey yapmayın. İşlemediyseniz durum değişikliğini siparişinize uygulayın.

Webhook ile 3-D Secure callback'inin farkı

3-D Secure akışını kullanıyorsanız, doğrulama bittiğinde alıcının tarayıcısı da callback URL'inize geri POST edilir — _signature ve _timestamp alanlarını taşıyan HMAC-imzalı bir form POST.

Bu iki kanalın görevleri farklıdır:

3-D callback (form POST)Webhook
TaşıyıcıAlıcının tarayıcısıPlatformdan sunucudan-sunucuya
AmaçAlıcının oturumunu devam ettirmek, sonuç sayfasını göstermekİş sonucunu kesinleştirmek
GüvenilirlikBest-effort — alıcı sekmeyi kapatabilir, bağlantısı kopabilir, yönlendirmeyi kurcalayabilirİmzalı, retry'lı, at-least-once
⚠️

Callback bilgilendirir; webhook otoritedir. Callback'i (HMAC imzasını ve _timestamp değerini doğruladıktan sonra) yalnızca alıcının arayüzünü yönetmek için kullanın. Bir siparişi asla yalnızca callback'e dayanarak ödendi saymayın — ürünü gönderme, bakiye tanımlama ve sipariş kapatma işlemlerini payment.captured webhook'una ya da sunucu tarafı bir GET /payments/:id kontrolüne bağlayın.

Hatalar & operasyonel notlar

DurumNe yapmalı
İmza doğrulaması başarısızNon-2xx ile reddedin; araştırın — teslimat platformdan gelmiyor ya da secret bayat.
Endpoint'iniz kapalıydıTeslimatlar otomatik retry edilir; toparlandıktan sonra boşlukları GET /payments ile mutabık edin.
Aynı event tekrar geldiAt-least-once teslimatta beklenen durum — idempotent handler'ınız no-op yapmalı.
Bilinmeyen ödeme id'sine ait eventLoglayın ve alarm kurun; genellikle sistemleriniz ile platformun ayrıştığı anlamına gelir — Ödemeleri Listeleme ile mutabık edin.

Sonraki adımlar