Cüzdan
Transfer

Transfer

Bu uçları kim çağırır?Dış API (s2s)
Taban yol
/api/v1/wallet
Kimlik
Ed25519 istek imzası — X-Key-Id, X-Date, X-Nonce, Digest (body varsa), X-Signature. Zaman penceresi ±5 dk, nonce tek kullanımlık, anahtarda IP allow-list olabilir.
Yetki
Anahtarınız cüzdan ürününü ve çağrının gerektirdiği cüzdan iznini taşımalıdır; izni olmayan çağrı 403 WALLET_INSUFFICIENT_SCOPE alır. Uç başına izinler: Anahtar izinleri.
Çapraz yüzey
Bu uçlar s2s yüzeyindedir. Son kullanıcının kendi transferi /api/portal/v1/wallet/me/transfers/* altındadır ve step-up ister. Panelde transfer BAŞLATMA ucu yoktur; panel yalnız izler ve müdahale eder.

İki transfer tipi vardır ve ikisi de quote → execute → complete desenini izler:

TipNereyeOperasyon tipiDurum
Cüzdandan cüzdana (W2W)Aynı programdaki başka bir cüzdanaW2W_TRANSFER✅ Çalışıyor
IBAN'a transfer (W2IBAN)Üçüncü şahsın banka hesabınaW2IBAN_WITHDRAW🚧 Banka entegrasyonu gerektirir

Kullanıcının kendi IBAN'ına çekim ayrı bir işlemdir (WITHDRAW_BANK) — bkz. Para Çekme. Ayrım keyfi değildir: limit sayacı, ücret şeması ve raporlama ikisini ayrı sayar.

Cüzdandan cüzdana transfer

1. Fiyatlandırın

POST /api/v1/wallet/transfers/w2w/quote
AlanTipZorunluAçıklama
endUserIdstring✅İşlemi adına yaptığınız son kullanıcı — gönderen
sourceWalletAccountIdstring✅Gönderenin hesabı; endUserId'ye ait olmalıdır
destinationWalletNostring⚠️Alıcının cüzdan numarası
destinationPhonestring⚠️Alıcının telefonu
amountstring✅Minor-unit
currencystring✅
paymentPurposestring—İşlem amacı (raporlama)

⚠️ destinationWalletNo veya destinationPhone'dan biri zorunludur.

endUserId işlemi kimin adına yaptığınızı bildirir. Cüzdan, sourceWalletAccountId'nin bu son kullanıcıya ait olduğunu doğrular; değilse istek 404 ile reddedilir. Böylece sizin tarafınızdaki bir hata, bir kullanıcının parasını başka bir kullanıcı adına harcatamaz. Aynı alan başlatma ve tamamlama adımlarında da zorunludur.

{
  "status": "success",
  "code": "SUCCESS",
  "data": {
    "feeTotal": "250",
    "taxTotal": "125",
    "totalCharge": "10425",
    "requiredAmount": "10425",
    "maskedDestinationName": "Meh*** Ö***",
    "sourceBalance": "25000",
    "sufficientBalance": true,
    "limitBlocked": false,
    "scheduleVersionId": "2d8b5e1c-7f4a-4c9e-a3b6-8e1f5c2d9a74"
  }
}
⚠️

maskedDestinationName'i kullanıcıya mutlaka gösterin. Telefonla gönderimde en sık şikâyet "yanlış kişiye gitti"dir; maskeli isim teyidi bunu kullanıcının kendisine doğrulatır.

limitBlocked: true ise transferi başlatmayın — sebebi Limitler sayfasında açıklanmıştır.

2. Başlatın

POST /api/v1/wallet/transfers/w2w

Quote gövdesine ek olarak:

AlanTipAçıklama
scheduleVersionIdstringQuote'tan gelen ücret sürümü — göndermezseniz güncel şema uygulanır
otpDestinationstringOTP'nin gideceği numara/adres
otpChannelstringSMS, EMAIL …
localestringOTP mesajının dili
{
  "status": "success",
  "code": "SUCCESS",
  "data": {
    "operationId": "7a3e9c1d-2b4f-4e8a-9d6c-0f5b8e2a1c93",
    "status": "CHALLENGED",
    "otpRequired": true,
    "otpChannel": "SMS",
    "stepUpRequired": false,
    "integrityHash": "9c1f…",
    "nonce": "4b82…"
  }
}

Bu noktada fon rezerve edilmiştir, para taşınmamıştır. Kullanıcı vazgeçerse operasyon süre dolumuyla EXPIRED olur ve rezervasyon çözülür.

3. Tamamlayın

POST /api/v1/wallet/transfers/w2w/{operationId}/complete
AlanTipZorunlu
endUserIdstring✅
operationIdstring✅
integrityHashstring✅
noncestring✅
otpCodestringotpRequired ise
trustedActionTokenstringStep-up token'ı varsa
{
  "status": "success",
  "code": "SUCCESS",
  "data": {
    "operationId": "7a3e9c1d-2b4f-4e8a-9d6c-0f5b8e2a1c93",
    "status": "COMPLETED",
    "ledgerTxnRef": "ltx_01J8ZU…",
    "receiptUrl": "https://api-gateway.lunixi.com/api/v1/wallet/operations/7a3e9c1d-2b4f-4e8a-9d6c-0f5b8e2a1c93/receipt"
  }
}
⚠️

nonce tek kullanımlıktır. complete ağ hatası verirse aynı nonce ile tekrar denemeyin — GET /wallet/operations/{operationId} ile durumu okuyun. COMPLETED ise iş bitmiştir; CHALLENGED ise yeni bir complete denenebilir.

Alıcıyı önceden doğrulama

Kullanıcıya "bu numarada cüzdan var mı?" sorusunu transferi başlatmadan sordurabilirsiniz:

POST /api/v1/wallet/wallets/lookup
{ "programId": "5b9e2f3a-7c41-4d8e-a6f2-9e1b3c4d5f60", "phone": "+905339876543" }
{ "status": "success", "code": "SUCCESS", "data": { "exists": true, "walletNo": "90455132", "maskedName": "Meh*** Ö***" } }

Bu uç isim döndürmez, maskeli isim döndürür — numara sorgulayarak kişi ismi toplanmasını engellemek için. Sık sorgulama rate limit'e takılır.

IBAN'a transfer

Yakında — banka entegrasyonu gerektirir

Aşağıdaki sözleşme tamamlanmıştır ve değişmeyecektir. Üretimde çalışması için programınıza bir banka entegrasyonu bağlanmalıdır; para dışarı bu entegrasyonla çıkar. Entegrasyon bağlı değilken bu uçlar istek kabul etmez. Durum: Yol Haritası.

Üçüncü şahsın banka hesabına gönderim. Akış W2W ile aynıdır, alıcı alanları farklıdır:

POST /api/v1/wallet/transfers/w2iban/quote
POST /api/v1/wallet/transfers/w2iban
POST /api/v1/wallet/transfers/w2iban/{operationId}/complete
AlanTipZorunluAçıklama
endUserIdstring✅İşlemi adına yaptığınız son kullanıcı; üç adımda da zorunlu
sourceWalletAccountIdstring✅endUserId'ye ait olmalıdır
beneficiaryNamestring✅Alıcı adı — banka tarafında eşleşmesi beklenir
ibanstring✅
bankCodestring—Hangi banka entegrasyonunun kullanılacağını etkileyebilir
amount, currencystring✅
paymentPurposestring—

Tamamlama gövdesi W2W ile aynıdır: endUserId, operationId (yoldaki ile aynı kimlik), integrityHash ve nonce zorunludur; başlatma yanıtı istiyorsa otpCode ya da trustedActionToken ekleyin.

İzin: başlatma ve tamamlama adımları parayı platform dışına çıkardığı için anahtarınızda wallet:bank-payout:operate izni bulunmalıdır; bu izin açık devre (Growth) planında sunulur. Quote adımı için okuma izni yeterlidir. İzin yoksa çağrı 403 WALLET_INSUFFICIENT_SCOPE döner — bkz. Anahtar izinleri.

IBAN transferi banka tarafında asenkron ilerler; operasyon BANK_PROCESSING durumundan geçer ve sonucu bankStatus, bankRef alanlarıyla döner.

⚠️

Banka tarafında sonuç belirsiz kalırsa operasyon AWAITING_RECON olur — başarısız değildir. Bu durumdaki bir transferi yeniden denemek çifte gönderim riski taşır; mutabakat sonucunu bekleyin.

Kişi rehberi

Kullanıcının sık gönderdiği kişileri saklayabilirsiniz:

GET    /api/portal/v1/wallet/me/contacts
POST   /api/portal/v1/wallet/me/contacts
PUT    /api/portal/v1/wallet/me/contacts/{id}
DELETE /api/portal/v1/wallet/me/contacts/{id}

Kişi kaydı alias + (contactPhone veya contactWalletNo) taşır; okuma yanıtlarında telefon maskeli döner. Ayrıntı: Portal — Hesap & İşlemler.

Webhook'lar

OlayNe zaman
wallet.transfer.completedTransfer tamamlandı, bakiyeler güncellendi
wallet.transfer.failedTransfer başarısız, rezervasyon çözüldü
wallet.transfer.reversedTamamlanmış transfer ters kaydedildi

Yaygın hatalar

DurumSebepÇözüm
INVALID_SIGNATURE (complete'te)nonce/integrityHash yeniden üretildiİkisini de init yanıtından birebir taşıyın
Bakiye yetiyor ama redÜcret+vergi dahil requiredAmount bakiyeyi aşıyorQuote'taki requiredAmount'a bakın, amount'a değil
limitBlocked: trueGünlük/aylık limit dolduLimitler
OTP kodu kabul edilmiyorKod başka bir operasyona aitHer operasyon kendi OTP'sini üretir; ekranı operasyona bağlayın