Para Transferi
- Taban yol
/api/v1/bank-connect- Kimlik
- Her istekte Ed25519 istek imzası —
X-Key-Id,X-Date,X-Nonce,X-Signature. Zaman penceresi ±5 dk, nonce tek kullanımlık. Bearer jetonu ya da panel oturumu bu uçlarda kabul edilmez (401 AUTH_HEADERS_MISSING). - Yetki
- Anahtarınız Banka Bağlantıları ürününü (
BANK_CONNECT) ve bu sayfadaki uçların yetkisini taşımalıdır:bank-connect:transfer. Ürünü olmayan anahtar403 PRODUCT_NOT_ENABLED, yetkisi olmayan anahtar403 API_INSUFFICIENT_SCOPEalır. Bkz. API'ye Giriş › Yetkiler. - Çapraz yüzey
- Bu uçlar s2s yüzeyindedir; kendi sunucunuzdan imzalı istekle çağırırsınız. Aynı verinin panelde ekranları var, ama panel uçları API anahtarıyla çağrılamaz; bu uçlar da panel oturumuyla çağrılamaz. Bankayı bağlama ve kesme yalnız panelde yapılır.
Bağlantınız transfer destekliyorsa bağlı banka hesabınızdan bir IBAN'a para gönderebilirsiniz. Bir bağlantının transfer destekleyip desteklemediğini Bağlantılar ucundaki capabilities alanından okursunuz: listede OUTGOING_TRANSFER varsa bu bağlantının hesaplarından transfer yapılabilir.
API anahtarıyla gönderilen transferde e-posta doğrulama kodu istenmez. Onay; anahtarınızın yetkisi, isteğin imzası ve isteğin IP izin listesindeki bir adresten gelmesidir. Transfer oluşturulduğu anda SUBMITTED durumuna geçer ve bankaya iletilmek üzere sıraya alınır. Transferin hangi yoldan gideceğini (hesaplarınız arası, aynı banka içi, FAST ya da EFT) banka belirler.
IP izin listesi zorunludur. bank-connect:transfer yetkisi yalnız IP izin listesi tanımlı bir API anahtarına verilebilir ve istek bu listedeki bir IP adresinden gelmelidir. Listesi boş anahtar 403 bank_connect.auth.ip_allowlist_required, listede olmayan bir adresten gelen istek 403 bank_connect.auth.ip_not_allowed alır. Bu kural okuma uçları dahil bu sayfadaki bütün uçlarda geçerlidir. → API'ye Giriş › IP izin listesi
Uçlar
| Yöntem | Yol | Açıklama |
|---|---|---|
GET | /transfer-purposes?connectionId=… | Bankanın kabul ettiği ödeme amaçları |
POST | /transfers | Transfer oluşturur. Idempotency-Key zorunlu. |
GET | /transfers | Transferleri listeler |
GET | /transfers/{transferId} | Tek transfer |
Bütün uçların yetkisi bank-connect:transfer'dır. Ortamı anahtarınız belirler; istekte environment gönderilmez.
Gövdeli isteği imzalama
Okuma uçlarının imzası API'ye Giriş sayfasındaki gibidir. POST isteklerinde gövdenin SHA-256 özeti Digest başlığında gönderilir ve kanonik dizinin son satırı olur. Bu sayfadaki cURL örnekleri aşağıdaki kabuk fonksiyonunu kullanır:
# lunixi-sign.sh — Kullanım: source lunixi-sign.sh; lunixi_sign POST "/api/v1/bank-connect/transfers" "$BODY"
LUNIXI_KEY_ID="mk_test_..."
LUNIXI_KEY_FILE="lunixi-signing.key"
lunixi_sign() {
local method="$1" req_path="$2" body="$3"
DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
NONCE=$(openssl rand -hex 16)
if [ -n "$body" ]; then
DIGEST="SHA-256=$(printf '%s' "$body" | openssl dgst -sha256 -binary | base64)"
printf '%s\n%s\nX-Date:%s\nX-Nonce:%s\nDigest:%s' "$method" "$req_path" "$DATE" "$NONCE" "$DIGEST" > canonical.txt
else
DIGEST=""
printf '%s\n%s\nX-Date:%s\nX-Nonce:%s' "$method" "$req_path" "$DATE" "$NONCE" > canonical.txt
fi
SIGNATURE=$(openssl pkeyutl -sign -inkey "$LUNIXI_KEY_FILE" -rawin -in canonical.txt | base64 | tr -d '\n')
}Gövdeyi tek satırlık (boşluksuz) JSON olarak gönderin ve özeti tam olarak gönderdiğiniz metinden hesaplayın. Kurallar: Kimlik Doğrulama › Digest başlığının biçimi.
PHP örnekleri PHP SDK'nın 0.22.0 ve üstü sürümünü kullanır; $bankConnect->transfers() bu sayfadaki uçları çağırır ve her isteği imzalar.
Ödeme amaçları
GET /api/v1/bank-connect/transfer-purposes?connectionId={connectionId}
Bankanın bu bağlantıda kabul ettiği ödeme amacı kodlarını döner. Transferde paymentPurpose alanına bu kodlardan birini yazabilirsiniz; alanı göndermezseniz bankanın varsayılanı kullanılır.
{
"status": "success",
"code": "SUCCESS",
"data": {
"items": [
{ "code": "OTHER", "name": "Diğer", "order": 1 },
{ "code": "RENT", "name": "Kira", "order": 2 }
]
},
"statusCode": 200,
"requestId": "00000000-0000-0000-0000-000000000000"
}Kodlar bankaya göre değişir; listeyi sabit kodlamayın, bu uçtan okuyun.
Transfer oluşturma
POST /api/v1/bank-connect/transfers
| Başlık | Değer |
|---|---|
Idempotency-Key | Zorunlu. 8–200 yazdırılabilir ASCII karakter (boşluksuz). → Idempotency-Key |
Content-Type | application/json |
| Gövde alanı | Tip | Zorunlu | Açıklama |
|---|---|---|---|
accountId | string (UUID) | Evet | Paranın çıkacağı hesap. Hesabın bağlantısı OUTGOING_TRANSFER desteklemelidir. |
receiverIban | string | Evet | Alıcının TR IBAN'ı. Boşluklu ya da boşluksuz yazılabilir; kontrol hanesi doğrulanır. |
receiverName | string | Evet | Alıcının adı ya da unvanı, 2–140 karakter. |
amountMinor | integer | Evet | Tutar, minor birimde (kuruş), sıfırdan büyük tam sayı: 150075 = ₺1.500,75. |
currency | string | Hayır | ISO 4217. Varsayılan ve bugün desteklenen tek değer TRY. |
paymentPurpose | string | Hayır | Ödeme amaçları ucundan bir kod. |
description | string | Hayır | Açıklama, en çok 140 karakter. |
Gövdede başka bir alan (environment, merchantId …) gönderilirse istek 400 VALIDATION_ERROR alır.
source lunixi-sign.sh
REQ_PATH="/api/v1/bank-connect/transfers"
BODY='{"accountId":"00000000-0000-0000-0000-000000000100","receiverIban":"TR000000000000000000000000","receiverName":"Örnek Tedarik A.Ş.","amountMinor":150075,"description":"Fatura 2026-0042"}'
lunixi_sign POST "$REQ_PATH" "$BODY"
curl -X POST "https://api-gateway.lunixi.com$REQ_PATH" \
-H "Content-Type: application/json" \
-H "X-Key-Id: $LUNIXI_KEY_ID" \
-H "X-Date: $DATE" \
-H "X-Nonce: $NONCE" \
-H "Digest: $DIGEST" \
-H "X-Signature: $SIGNATURE" \
-H "Idempotency-Key: payout-2026-0042" \
--data-raw "$BODY"Örnekteki IBAN yer tutucudur; kontrol hanesi geçerli bir IBAN gönderin.
Başarılı yanıt 201 döner:
{
"status": "success",
"code": "SUCCESS",
"data": {
"transfer": {
"id": "00000000-0000-0000-0000-000000002000",
"connectionId": "00000000-0000-0000-0000-000000000010",
"accountId": "00000000-0000-0000-0000-000000000100",
"environment": "live",
"status": "SUBMITTED",
"rail": null,
"amountMinor": 150075,
"currency": "TRY",
"sourceIbanMasked": "TR00 **** **** **** **** **00 00",
"receiverIbanMasked": "TR00 **** **** **** **** **00 00",
"receiverNameMasked": "Ö**** T****** A.Ş.",
"receiverBankCode": "0000",
"paymentPurpose": null,
"description": "Fatura 2026-0042",
"idempotencyKey": "payout-2026-0042",
"failureCode": null,
"outcomeUnknown": false,
"initiatedVia": "API",
"sanctionStatus": "NOT_SCREENED",
"bankTransactionId": null,
"bankState": null,
"createdAt": "2026-01-01T07:00:00.000Z",
"submittedAt": "2026-01-01T07:00:00.000Z",
"completedAt": null,
"failedAt": null,
"returnedAt": null,
"canceledAt": null,
"otpExpiresAt": null
},
"stepUp": null,
"replayed": false
},
"statusCode": 201,
"requestId": "00000000-0000-0000-0000-000000000000"
}| Alan | Açıklama |
|---|---|
transfer | Oluşan transfer; alanları aşağıda. |
stepUp | API anahtarıyla yapılan isteklerde her zaman null (doğrulama kodu yoktur). |
replayed | true: aynı Idempotency-Key ve aynı gövdeyle daha önce oluşturulmuş transfer döndü; yeni transfer açılmadı. |
Lunixi bakiye kontrolü yapmaz; bakiyenin yeterli olup olmadığına banka karar verir. Bakiye yetmezse transfer FAILED olur ve failureCode bank_connect.transfer.insufficient_balance döner.
Idempotency-Key
Aynı transferin iki kez gönderilmemesi için her transfer isteği bir Idempotency-Key taşır. Anahtar; organizasyonunuz ve ortam içinde tekildir.
| Durum | Sonuç |
|---|---|
| Yeni anahtar | Yeni transfer oluşturulur, replayed: false. |
| Aynı anahtar, aynı gövde | Yeni transfer açılmaz; mevcut transfer replayed: true ile döner. |
| Aynı anahtar, farklı gövde | 409 bank_connect.transfer.idempotency_conflict. |
| Başlık yok | 400 bank_connect.transfer.idempotency_key_required. |
| Biçim dışı anahtar | 400 bank_connect.transfer.idempotency_key_invalid. |
- Anahtarı kendi sisteminizdeki ödeme kaydından türetin (ör.
payout-<ödeme kimliğiniz>). Böylece süreciniz yeniden başlasa da aynı ödeme için aynı anahtarı gönderirsiniz. - Zaman aşımı ya da ağ hatası aldığınızda isteği aynı anahtar ve aynı gövdeyle yeniden gönderin. Transfer oluştuysa mevcut transfer döner; oluşmadıysa şimdi oluşur.
- SDK'lar anahtarı siz vermezseniz kendisi üretir ve kendi yeniden denemelerinde aynı anahtarı kullanır. Kullanılan anahtar yanıttaki
transfer.idempotencyKeyalanındadır.
Transferi izleme
Transferin sonucu iki yoldan gelir:
- Webhook:
bank_transfer.created,bank_transfer.completed,bank_transfer.failedvebank_transfer.returnedolayları. → Webhook Olayları › Transfer olayları - Sorgu:
GET /api/v1/bank-connect/transfers/{transferId}. Webhook'u kaçırdıysanız ya da endpoint'iniz yoksa transferi bu uçtan izleyin.
Durumların anlamı ve olası geçişler: Kavramlar › Transfer durumları.
Tek transfer
GET /api/v1/bank-connect/transfers/{transferId} tek bir transferi döner; data transfer nesnesinin kendisidir. Bilinmeyen, başka bir organizasyona ya da diğer ortama ait transfer 404 bank_connect.transfer.not_found döner.
source lunixi-sign.sh
REQ_PATH="/api/v1/bank-connect/transfers/00000000-0000-0000-0000-000000002000"
lunixi_sign GET "$REQ_PATH"
curl "https://api-gateway.lunixi.com$REQ_PATH" \
-H "X-Key-Id: $LUNIXI_KEY_ID" \
-H "X-Date: $DATE" \
-H "X-Nonce: $NONCE" \
-H "X-Signature: $SIGNATURE"Transferleri listeleme
GET /api/v1/bank-connect/transfers transferleri en yeniden eskiye listeler. Panelden ve API'den başlatılan transferler birlikte listelenir (initiatedVia).
| Sorgu parametresi | Tip | Açıklama |
|---|---|---|
accountId | string (UUID) | Yalnız bu hesaptan yapılan transferler. |
status | string | Virgülle ayrılmış durum listesi, ör. COMPLETED,RETURNED. |
createdFrom | string | ISO 8601, dahil. |
createdTo | string | ISO 8601, hariç. |
limit | integer | Sayfa başına kayıt, 1–100. Varsayılan 25. |
cursor | string | Önceki yanıttaki pageInfo.nextCursor. |
Sayfalama kuralları okuma uçlarıyla aynıdır. → API'ye Giriş › Sayfalama
Sonucu doğrulanamayan transfer
Bankadan kesin cevap alınamadığında (ör. banka zamanında yanıt vermediğinde) transfer SUBMITTED durumunda kalır ve outcomeUnknown true olur. Bu durumda:
- Transferi yeniden göndermeyin. Para bankada çıkmış olabilir; yeni bir transfer çift ödemeye yol açabilir.
- Lunixi sonucu bankayla doğrular. Transfer sonuçlandığında durumu
COMPLETEDya daFAILEDolur ve ilgili webhook gönderilir. - Banka kontrolünde transferin gerçekleşmediği anlaşılırsa
failureCodebank_connect.transfer.failed_after_bank_checkolur. Bu durumda aynı ödemeyi yeni birIdempotency-Keyile yeniden gönderebilirsiniz.
failed_after_bank_check ile FAILED olduktan sonra ve yeni bir Idempotency-Key ile tekrar gönderin.Transfer nesnesi
Transfer, döviz teklifi ve döviz işlemi yanıtlarında değeri olmayan alanlar null olarak döner; alan yanıttan çıkarılmaz. Bu, okuma uçlarındaki "değer yoksa alan yoktur" kuralından farklıdır.
| Alan | Tip | Açıklama |
|---|---|---|
id | string (UUID) | Transferin kimliği. |
connectionId | string (UUID) | Hesabın bağlantısı. |
accountId | string (UUID) | Paranın çıktığı hesap. |
environment | string | test ya da live. |
status | string | Transferin durumu. → Transfer durumları |
rail | string | null | Bankanın seçtiği yol: INTERNAL, INTRABANK, FAST, EFT ya da UNKNOWN. Banka bildirene kadar null. |
amountMinor | integer | Tutar, minor birimde. |
currency | string | Para birimi (ISO 4217). |
sourceIbanMasked | string | null | Kaynak hesabın maskeli IBAN'ı. |
receiverIbanMasked | string | Alıcının maskeli IBAN'ı. |
receiverNameMasked | string | Alıcının maskeli adı. |
receiverBankCode | string | null | Alıcı bankanın EFT kodu. |
paymentPurpose | string | null | Gönderdiğiniz ödeme amacı kodu. |
description | string | null | Gönderdiğiniz açıklama. |
idempotencyKey | string | Transferin oluşturulduğu Idempotency-Key. |
failureCode | string | null | FAILED, RETURNED ya da CANCELED transferde sebep kodu (bank_connect.transfer.*). → Hata Kodları › Transfer sonuç kodları |
outcomeUnknown | boolean | true: sonuç bankadan henüz doğrulanamadı. → Sonucu doğrulanamayan transfer |
initiatedVia | string | API ya da PANEL. |
sanctionStatus | string | Alıcı tarama durumu: NOT_SCREENED, CLEAR, POTENTIAL_MATCH, ERROR. Bilgi amaçlıdır; transferi durdurmaz. |
bankTransactionId | string | null | Transferin hesabınızdaki giden hareketi bulunduğunda o hareketin transactionId'si. → Hareketler |
bankState | string | null | Bankanın transfer için bildirdiği durum metni; yalnız bilgi amaçlıdır, kodunuzu status'a göre yazın. |
createdAt | string | null | Oluşturulma zamanı. |
submittedAt | string | null | Onaylanıp bankaya iletilmek üzere sıraya alındığı zaman. |
completedAt | string | null | Tamamlandığı zaman. |
failedAt | string | null | Başarısız olduğu zaman. |
returnedAt | string | null | İade edildiği zaman. |
canceledAt | string | null | İptal edildiği zaman. |
otpExpiresAt | string | null | Yalnız panelden başlatılıp doğrulama kodu bekleyen transferde kodun geçerlilik sonu. |
IBAN ve alıcı adı yalnız maskeli döner; tam IBAN ve tam ad hiçbir yanıtta yoktur. → Kavramlar › Maskeleme
Hatalar
| HTTP | code | Ne yapmalı |
|---|---|---|
400 | VALIDATION_ERROR | Tanımlanmamış bir alan ya da parametre gönderdiniz, ya da alan biçimi hatalı. |
400 | bank_connect.validation.invalid | IBAN, alıcı adı, tutar, açıklama ya da ödeme amacı kurala uymuyor; details.errors[] alanına bakın. |
400 | bank_connect.transfer.idempotency_key_required | Idempotency-Key başlığını ekleyin. |
400 | bank_connect.transfer.idempotency_key_invalid | 8–200 yazdırılabilir ASCII karakter kullanın. |
403 | API_INSUFFICIENT_SCOPE | Anahtarınıza bank-connect:transfer yetkisini ekleyin. |
403 | bank_connect.auth.ip_allowlist_required | Anahtarınıza IP izin listesi tanımlayın. |
403 | bank_connect.auth.ip_not_allowed | İsteği izin listesindeki bir IP'den gönderin ya da listeyi güncelleyin. |
404 | bank_connect.transfer.not_found | Kimliği ve anahtarınızın ortamını kontrol edin. |
404 | bank_connect.account.not_found | Hesap kimliğini kontrol edin. |
409 | bank_connect.transfer.account_not_eligible | Hesap, bağlantı ya da banka bilgileri şu anda etkin değil. |
409 | bank_connect.transfer.idempotency_conflict | Bu anahtarı başka bir gövdeyle kullandınız; yeni transfer için yeni anahtar kullanın. |
412 | bank_connect.transfer.capability_unavailable | Bu bağlantı transfer desteklemiyor. |
422 | bank_connect.transfer.currency_unsupported | TRY gönderin ya da currency alanını boş bırakın. |
429 | RATE_LIMITED | Transfer istek limiti aşıldı; Retry-After kadar bekleyin. → Sınırlar |
503 | bank_connect.auth.ip_allowlist_unavailable | Geri çekilerek aynı istekle yeniden deneyin. |
Banka transferi reddederse istek yine 201 döner; transfer sonradan FAILED olur ve sebep failureCode alanındadır. → Hata Kodları › Transfer sonuç kodları