Payven hata yanıtları RFC 9457 — Problem Details for HTTP APIs standardına uyar. Tüm hata response’larının body’leri application/problem+json MIME tipindedir; HTTP durum kodu kategoriyi, gövdedeki code alanı ise hatanın programatik tipini belirtir.
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json
X-Correlation-Id: 9f1c8e76-2a3b-4f12-9c8d-12cb24a8a8a8
{
"type": "https://docs.payven.com.tr/errors/refund_exceeds_original",
"title": "İade tutarı orijinali aşıyor",
"status": 422,
"code": "refund_exceeds_original",
"detail": "Toplam iade tutarı orijinal tutarı (15000 kuruş) aşamaz.",
"instance": "/api/v1/payments/8e3f5c12-.../refund",
"correlation_id": "9f1c8e76-2a3b-4f12-9c8d-12cb24a8a8a8"
}
| Alan | Zorunluluk | Açıklama |
|---|
type | Zorunlu | Hata tipinin kanonik URI’si. Aynı zamanda dokümantasyon linki olarak çalışır. |
title | Zorunlu | Kısa, insan-okur başlık (Türkçe). |
status | Zorunlu | HTTP status kodu (header ile aynı değer, gövdede yinelenir). |
code | Zorunlu | Programatik hata kodu (snake_case). type URI’sinin son segmenti. |
detail | Opsiyonel | Bu özel duruma ilişkin açıklayıcı mesaj. |
instance | Opsiyonel | Hatanın oluştuğu kaynak yolu. |
correlation_id | Opsiyonel | İstek zinciri kimliği. Response header’ı X-Correlation-Id ile aynı. Destek talebinde paylaşın. |
HTTP kategorileri
| HTTP | Kategori | Örnek code | Retry |
|---|
400 Bad Request | Geçersiz JSON, eksik alan, format hatası | bad_request, invalid_json | Düzeltmeden tekrarlamayın |
401 Unauthorized | Token yok, geçersiz, expire | unauthorized, invalid_token, token_expired | Yeniden token alın |
403 Forbidden | Yetki yok, lisans yok, merchant pasif | forbidden, merchant_inactive, product_not_licensed | Hayır — yetki/lisans çözülmeden tekrar göndermeyin |
404 Not Found | Kaynak bulunamadı | resource_not_found, payment_not_found | Hayır |
409 Conflict | Idempotency çakışması, durum geçişi geçersiz | conflict, idempotency_key_in_use, invalid_state_transition | Hayır |
422 Unprocessable Entity | İş kuralı veya validasyon ihlali | validation_failed, refund_exceeds_original, bank_declined | Hayır — istek/iş kuralı düzeltilmeden retry yapmayın |
429 Too Many Requests | Rate limit aşıldı (Retry-After header gönderilir) | rate_limit_exceeded | Backoff |
500/502/503 | Sunucu / bağlantı hatası | internal_server_error, bad_gateway, service_unavailable | Backoff |
| Network timeout | Ağ veya gateway timeout | — | Idempotency-Key ile |
Banka reddi de bir hata yanıtıdır. Gateway’e ulaşmış ama banka tarafında reddedilen
ödeme 422 Unprocessable Entity + application/problem+json ile döner; gövdede
code: "bank_declined" ve banka ham yanıt kodu (provider_error_code, örn. "51" —
yetersiz bakiye) yer alır. Sonuç değerlendirmesini yalnız HTTP durum kodu üzerinden
yapın.
Yaygın hata kodları
Kimlik doğrulama / yetki
code | HTTP | Anlam |
|---|
unauthorized | 401 | Authorization header eksik |
invalid_token | 401 | Access token geçersiz veya kurcalanmış |
token_expired | 401 | Access token süresi doldu — refresh edin |
invalid_credentials | 401 | client_id / client_secret yanlış |
invalid_refresh_token | 401 | Refresh token expire / revoke edilmiş |
realm_not_found | 404 | Auth endpoint’inde {slug} geçersiz |
realm_suspended | 403 | Tenant askıya alınmış |
forbidden | 403 | Bu rol bu kaynağı göremez |
merchant_inactive | 403 | Belirtilen merchant pasif |
merchant_not_found | 403 | X-Merchant-Id / X-External-Merchant-Id çözümlenemedi |
product_not_licensed | 403 | Modül planınızda etkin değil |
ip_not_allowed | 403 | İstek IP’si izin listesinde değil |
code | HTTP | Anlam |
|---|
bad_request | 400 | Genel istek hatası |
invalid_json | 400 | Gövde geçersiz JSON |
validation_failed | 422 | Validasyon kuralı ihlali — errors[] ile alan listesi |
invalid_amount | 422 | Tutar pozitif değil veya minimum altında |
invalid_currency | 422 | Para birimi desteklenmiyor (TRY dışı için kapalı) |
invalid_card | 422 | Kart numarası Luhn checksum’ı geçmiyor |
invalid_expiry | 422 | Son kullanma tarihi geçmiş veya format yanlış |
invalid_installment | 422 | Taksit sayısı bu BIN için desteklenmiyor |
invalid_iban | 422 | IBAN checksum’ı geçmiyor |
Validasyon hatalarında alan-bazlı detay errors[] array’inde döner:
{
"type": "https://docs.payven.com.tr/errors/validation_failed",
"title": "Validasyon hatası",
"status": 422,
"code": "validation_failed",
"detail": "1 alan geçersiz",
"errors": [
{
"field": "amount.amount",
"code": "below_minimum",
"message": "Tutar 100 kuruşun altında olamaz."
}
]
}
Ödeme / işlem kuralları
code | HTTP | Anlam |
|---|
payment_not_found | 404 | İşlem bulunamadı |
payment_already_refunded | 422 | Tüm tutar zaten iade edilmiş |
payment_not_refundable | 422 | Mevcut durumda iade yapılamaz |
refund_exceeds_original | 422 | İade tutarı orijinali aşıyor |
void_not_allowed_after_settlement | 422 | Mutabakat sonrası iptal yapılamaz |
capture_amount_exceeds_authorization | 422 | Çekim tutarı ön provizyonu aşıyor |
invalid_state_transition | 409 | Durum geçişi izin verilmiyor (örn. Failed → Captured) |
duplicate_external_id | 409 | Aynı external_id ile başka işlem zaten var |
Yönlendirme ve banka
code | HTTP | Anlam |
|---|
routing_no_match | 422 | Hiçbir konnektör koşullara uymadı |
connector_unavailable | 503 | Hedef konnektör geçici olarak devre dışı (circuit breaker açık) |
bank_declined | 422 | Banka işlemi reddetti — provider_error_code bankadan dönen kod |
bank_timeout | 504 | Banka yanıt vermedi (Smart Retry tetiklenmiş olabilir) |
fraud_blocked | 422 | Fraud kuralı tetiklendi (fraud.decision: "block") |
fraud_review_required | 422 | Manuel inceleme bekliyor |
Idempotency / rate limit
code | HTTP | Anlam |
|---|
idempotency_key_in_use | 409 | Aynı Idempotency-Key farklı body için kullanılmış |
rate_limit_exceeded | 429 | İstek limiti aşıldı (Retry-After header’ına uyun) |
Banka yanıt kodlarının ve konnektör hata mesajlarının tam listesi: Sanal POS → Hata Kodları.
Retry stratejisi
Sadece geçici hatalar için retry uygun: 429, 5xx, network timeout. Idempotent olmayan işlemleri retry ederken mutlaka aynı Idempotency-Key kullanın — yoksa aynı işlem birden fazla yapılır.
Önerilen pattern: exponential backoff + jitter.
1. deneme: hemen
2. deneme: ~1 sn sonra
3. deneme: ~2 sn sonra
4. deneme: ~4 sn sonra
5. deneme: ~8 sn sonra (üst limit)
async Task<HttpResponseMessage> SendWithRetryAsync(
Func<HttpRequestMessage> requestFactory,
int maxAttempts = 5,
CancellationToken ct = default)
{
var rng = new Random();
for (int attempt = 0; ; attempt++)
{
var response = await _http.SendAsync(requestFactory(), ct);
if (response.IsSuccessStatusCode) return response;
var status = (int)response.StatusCode;
var retryable = status == 429 || status >= 500;
if (!retryable || attempt >= maxAttempts - 1) return response;
var delaySec = Math.Pow(2, attempt);
if (response.Headers.RetryAfter?.Delta is { } ra) delaySec = ra.TotalSeconds;
var jitter = rng.NextDouble() * 0.5;
await Task.Delay(TimeSpan.FromSeconds(delaySec + jitter), ct);
}
}
Tekrar denerken aynı Idempotency-Key ile gönderin. Aynı işlemin iki kez
oluşmasını engeller. Bkz. Idempotency.
Müşteriye gösterme
detail alanı son kullanıcıya gösterilebilir nitelikte yazılır. Yine de hassas durumlarda (fraud, fraud-review, banka tarafı reddi) genel bir mesajla geçiştirmek daha güvenlidir:
function userMessage(problem) {
switch (problem.code) {
case "validation_failed":
return problem.detail;
case "bank_declined":
case "fraud_blocked":
case "fraud_review_required":
return "İşleminiz onaylanmadı. Farklı bir kart deneyebilirsiniz.";
case "rate_limit_exceeded":
return "Şu an çok fazla istek alıyoruz. Lütfen kısa bir süre sonra tekrar deneyin.";
default:
return "İşleminiz gerçekleştirilemedi. Lütfen tekrar deneyin.";
}
}
Destek talebi açarken
Her hata yanıtında dönen correlation_id değerini paylaşın — log zincirini saniyeler içinde bulabiliriz. Response header’ındaki X-Correlation-Id ile gövdedeki correlation_id aynı değerdir.
Konu: Refund 422 — refund_exceeds_original
Detay: correlation_id: 9f1c8e76-2a3b-4f12-9c8d-12cb24a8a8a8
instance: /api/v1/payments/8e3f5c12-.../refund
zaman: 2026-05-05T14:23:11+03:00