Transfer
- 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_SCOPEalı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:
| Tip | Nereye | Operasyon tipi | Durum |
|---|---|---|---|
| Cüzdandan cüzdana (W2W) | Aynı programdaki başka bir cüzdana | W2W_TRANSFER | ✅ Çalışıyor |
| IBAN'a transfer (W2IBAN) | Üçüncü şahsın banka hesabına | W2IBAN_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| Alan | Tip | Zorunlu | Açıklama |
|---|---|---|---|
endUserId | string | ✅ | İşlemi adına yaptığınız son kullanıcı — gönderen |
sourceWalletAccountId | string | ✅ | Gönderenin hesabı; endUserId'ye ait olmalıdır |
destinationWalletNo | string | ⚠️ | Alıcının cüzdan numarası |
destinationPhone | string | ⚠️ | Alıcının telefonu |
amount | string | ✅ | Minor-unit |
currency | string | ✅ | |
paymentPurpose | string | — | İş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/w2wQuote gövdesine ek olarak:
| Alan | Tip | Açıklama |
|---|---|---|
scheduleVersionId | string | Quote'tan gelen ücret sürümü — göndermezseniz güncel şema uygulanır |
otpDestination | string | OTP'nin gideceği numara/adres |
otpChannel | string | SMS, EMAIL … |
locale | string | OTP 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| Alan | Tip | Zorunlu |
|---|---|---|
endUserId | string | ✅ |
operationId | string | ✅ |
integrityHash | string | ✅ |
nonce | string | ✅ |
otpCode | string | otpRequired ise |
trustedActionToken | string | Step-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
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| Alan | Tip | Zorunlu | Açıklama |
|---|---|---|---|
endUserId | string | ✅ | İşlemi adına yaptığınız son kullanıcı; üç adımda da zorunlu |
sourceWalletAccountId | string | ✅ | endUserId'ye ait olmalıdır |
beneficiaryName | string | ✅ | Alıcı adı — banka tarafında eşleşmesi beklenir |
iban | string | ✅ | |
bankCode | string | — | Hangi banka entegrasyonunun kullanılacağını etkileyebilir |
amount, currency | string | ✅ | |
paymentPurpose | string | — |
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
| Olay | Ne zaman |
|---|---|
wallet.transfer.completed | Transfer tamamlandı, bakiyeler güncellendi |
wallet.transfer.failed | Transfer başarısız, rezervasyon çözüldü |
wallet.transfer.reversed | Tamamlanmış transfer ters kaydedildi |
Yaygın hatalar
| Durum | Sebep | Çö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şıyor | Quote'taki requiredAmount'a bakın, amount'a değil |
limitBlocked: true | Günlük/aylık limit doldu | Limitler |
| OTP kodu kabul edilmiyor | Kod başka bir operasyona ait | Her operasyon kendi OTP'sini üretir; ekranı operasyona bağlayın |