Payment + Booking Distributed Transaction
Travel booking'de payment ve supplier booking adımlarını distributed transaction olarak modelleyin; authorize, capture, compensation, UNKNOWN outcome ve reconciliation desenlerini uygulayın.
Travel booking'de payment gateway ve supplier booking API aynı ACID transaction içinde değildir. Bu nedenle booking + payment akışı distributed transaction olarak tasarlanmalı; her adımın success, failure ve unknown sonucu ayrı ele alınmalıdır.
Temel problem
Şu iki işlem birlikte başarılı olmak zorundadır:
- müşteri ödeme yükümlülüğü,
- supplier rezervasyonu.
Ama bunlar farklı sistemlerde gerçekleşir. Dolayısıyla aşağıdaki ara durumlar normaldir:
- payment authorized, booking failed,
- booking confirmed, payment capture failed,
- payment request timeout, outcome unknown,
- booking request timeout, outcome unknown,
- cancellation succeeded ama refund başarısız,
- refund succeeded ama local booking state stale.
Tek transaction yoktur
Payment and booking distributed transaction
Neden authorize → book → capture?
Birçok modelde önce authorization yapıp booking confirmed olduktan sonra capture etmek riski azaltır:
- karttan para tahsil edilip booking oluşmaması önlenir,
- booking failed olursa authorization void edilebilir.
Ama bu her provider/payment modelinde mümkün değildir. Bazı akışlar full capture ister, bazıları pay-at-property veya virtual-card settlement kullanır.
Diğer sequencing modelleri
Book → pay
Supplier önce booking oluşturur, ardından ödeme alınır.
Risk: payment başarısız olursa confirmed booking'i iptal etmek gerekebilir.
Pay/capture → book
Ödeme önce kesinleşir.
Risk: booking başarısız olursa refund/void compensation gerekir.
Pay at property
Platform payment orchestration yapmayabilir; booking state ile payment liability yine ayrı tutulmalıdır.
Canonical transaction state
Tek bir transaction_status yerine ayrı state machine'ler kullanın:
PaymentState:
CREATED -> AUTHORIZING -> AUTHORIZED -> CAPTURING -> CAPTURED
-> FAILED
-> UNKNOWN
-> VOIDED
-> REFUNDED
BookingState:
REQUESTED -> CONFIRMED
-> FAILED
-> UNKNOWN
-> CANCELLEDÜstte bir orchestration state olabilir:
BookingTransaction:
STARTED
PAYMENT_READY
BOOKING_PENDING
BOOKING_CONFIRMED
PAYMENT_FINALIZING
COMPLETED
RECOVERY_REQUIRED
COMPENSATING
FAILEDSaga yaklaşımı
Distributed transaction rollback yapılamadığı için compensation gerekir.
Travel booking compensation saga
Compensation, gerçek rollback değildir. Supplier cancellation fee veya payment cost oluşturmuş olabilir.
UNKNOWN outcome iki tarafta da mümkündür
Booking UNKNOWN
Provider request timeout olur. Create çağrısını tekrar etmeyin; lookup/reconciliation yapın.
Payment UNKNOWN
Gateway timeout olur. Aynı payment'ı yeni transaction ID ile tekrar başlatmak double charge yaratabilir. Payment provider idempotency key veya lookup mekanizması kullanılmalıdır.
Idempotency sınırları
Her external operation için ayrı key kullanın:
- booking create idempotency key,
- payment authorize idempotency key,
- capture key,
- cancel key,
- refund key.
Aynı key'i farklı semantic operation'larda reuse etmeyin.
Transaction log
Orchestrator yalnız current state değil immutable attempt history de saklamalıdır.
{
"bookingIntentId": "bi_123",
"operation": "BOOK",
"attempt": 1,
"idempotencyKey": "book_bi_123_v1",
"status": "UNKNOWN",
"providerReference": null,
"startedAt": "2026-09-27T10:00:00Z"
}Recovery policy
Recovery kararı operation tipine göre değişmelidir:
| Durum | İlk aksiyon |
|---|---|
| Payment authorize UNKNOWN | provider lookup / idempotent status query |
| Booking create UNKNOWN | booking lookup / reconciliation |
| Booking FAILED + payment AUTHORIZED | void authorization |
| Booking CONFIRMED + capture FAILED | capture retry / ops; gerekiyorsa cancel |
| Cancellation CONFIRMED + refund FAILED | refund retry/reconciliation |
| Refund UNKNOWN | payment lookup; blind retry yok |
Outbox ve local consistency
Provider booking confirmed olduktan sonra local DB write başarılı ama event publish başarısız olabilir. Transactional outbox kullanın:
local transaction:
update booking state
insert outbox event
commit
background publisher:
publish outbox event
mark publishedBu desen external booking call'ı ACID yapmaz; yalnız local state + event publication tutarlılığını iyileştirir.
Retry politikası
Retry sadece "teknik olarak retryable" olduğu için yapılmamalıdır.
Güvenli retry için:
- operation idempotent olmalı veya idempotency key desteklemeli,
- önceki attempt'in outcome'u bilinmeli,
- exponential backoff + jitter kullanılmalı,
- retry budget sınırlandırılmalı,
- provider rate limit dikkate alınmalı.
Booking create, idempotency yoksa en riskli retry operation'larından biridir.
Reconciliation worker
Reconciliation worker şu kayıtları taramalıdır:
- uzun süredir UNKNOWN booking,
- AUTHORIZED payment + terminal booking yok,
- CONFIRMED booking + payment incomplete,
- CANCELLED booking + refund incomplete,
- local/provider state mismatch.
Her kayıt için authoritative provider evidence alınır ve canonical state düzeltilir.
Manual operations queue
Bazı divergence'lar otomatik çözülemez. Ops queue'da şu bilgiler olmalıdır:
- traveler-safe booking summary,
- provider reference,
- payment reference,
- current booking/payment states,
- attempt history,
- recommended next action,
- risk: duplicate charge / duplicate booking / cancellation fee.
Failure modes
- booking confirmed ama capture başarısız,
- payment captured ama booking FAILED/UNKNOWN,
- compensation operation'ının kendisinin timeout olması,
- duplicate capture/refund retry,
- local outbox/state ile provider state'in ayrışması,
- manual ops sırasında ikinci booking veya ikinci refund yaratılması.
Observability
Takip edin:
- authorization success rate,
- booking confirmation rate,
- capture success rate,
- compensation rate,
- void/refund latency,
- booking UNKNOWN rate,
- payment UNKNOWN rate,
- payment-booking divergence count,
- reconciliation age,
- manual intervention rate,
- duplicate prevention hits.
Production checklist
- payment ve booking state machine'lerini ayırın,
- sequencing modelini provider/commercial contract bazında explicit seçin,
- her write operation için idempotency key kullanın,
- UNKNOWN outcome'u first-class state yapın,
- compensation policy tanımlayın,
- reconciliation worker ve manual ops queue sağlayın,
- transactional outbox ile local state/event tutarlılığını koruyun.
Tasarım prensibi
Payment ve Booking iki ayrı state machine; orchestration bunların koordinasyon katmanıdır.
Bir tarafı diğerinin status alanına gömmek yerine intent, attempt, evidence ve compensation modelleri explicit tutulduğunda timeout ve partial failure senaryoları yönetilebilir hale gelir.
Benzer bir entegrasyon mu planlıyorsunuz?
Gereksinim, feed/API tasarımı ve production yaklaşımını birlikte değerlendirebiliriz.