Adres Doğrulama Entegrasyonu: Siparişten Önce Kontrol
Adres doğrulama entegrasyonu ile sipariş adresini kaydetmeden önce kontrol edin: istek gövdesi, öneri gösterimi ve stableId ile saklama.
Kullanıcının yazdığı adres sipariş kaydına girdiği anda kargo etiketine, faturaya ve müşteri hizmetleri ekranına taşınır. Hatayı bundan sonra düzeltmek zahmetlidir. En uygun an, adres veritabanına yazılmadan hemen önceki andır. Bu yazı, API Türkiye'nin POST /v1/addresses/validate ucuyla bir adres doğrulama entegrasyonunun nasıl kurulacağını adım adım anlatıyor. İstek gövdesini, sonuca göre kullanıcıya öneri sunmayı ve doğrulanmış adresi kalıcı kimliklerle saklamayı ele alıyor.
Adres doğrulama entegrasyonu akışın neresinde durmalı?
Doğrulama, formun gönderildiği an ile siparişin kaydedildiği an arasına yerleşir. Tipik akış şöyledir:
- Kullanıcı adres formunu doldurur. Otomatik tamamlama veya il/ilçe/mahalle seçim kutularıyla girişi kolaylaştırmak, doğrulamaya daha temiz veri ulaştırır.
- Tarayıcı, adres bileşenlerini kendi backend'inize gönderir. API anahtarı tarayıcı koduna konmaz. Çağrıyı her zaman sunucunuz yapar.
- Backend,
/v1/addresses/validateucunu çağırır. - Sonuç
valid: trueise normalize edilmiş adres ve kimlikler siparişe yazılır. - Sonuç
valid: falseise sipariş kaydı bekletilir. Kullanıcıya hangi alanın sorunlu olduğu ve varsa öneriler gösterilir.
Doğrulamayı her tuş vuruşunda değil, kullanıcı "Devam" ya da "Siparişi tamamla" dediğinde çalıştırın. Aynı girdi için alınan sonucu kısa süreliğine önbelleğe almak gereksiz tekrar çağrıları da önler.
Otomatik tamamlamadan seçilmiş bir adres de bu adımdan geçmelidir. Autocomplete yanıtındaki score yalnız sıralama içindir. Adresin geçerli olduğunu ya da teslimat yapılabileceğini kanıtlamaz. Öneri deneyiminin ayrıntıları için adres otomatik tamamlama rehberine bakabilirsiniz.
Adres doğrulama Pro ve üzeri paketlerde sunulur ve anahtarın addresses:validate yetkisine sahip olmasını gerektirir. Bu yetki yoksa yanıt 403 insufficient_scope olur.
İstek gövdesi: beş zorunlu alan ve doorId
Uç, serbest metin yerine ayrı adres bileşenleri bekler. Katalogdaki il → ilçe → mahalle → cadde/sokak → dış kapı zincirini sırayla kontrol eder.
| Alan | Zorunlu | Üst sınır | Not |
|---|---|---|---|
province | Evet | 100 karakter | İl adı |
district | Evet | 100 karakter | İlçe adı |
neighborhood | Evet | 150 karakter | Mahalle adı |
street | Evet | 200 karakter | Cadde/sokak adı |
doorNumber | Evet | 80 karakter | Dış kapı numarası |
doorId | Hayır | — | Dış kapı listesindeki id değeri, sayısal metin |
doorId, /v1/streets/{code}/buildings yanıtındaki id alanıdır. Kapının code veya stableId değeri değildir. Aynı kapı numarasına sahip birden fazla kaydı ayırt etmek için kullanılır.
curl "https://api.apiturkiye.com/v1/addresses/validate" \
-H "X-API-Key: apt_live_..." \
-H "Content-Type: application/json" \
--data '{"province":"İstanbul","district":"Kadıköy","neighborhood":"Caferağa Mahallesi","street":"Moda Caddesi","doorNumber":"12"}'
Göndermeden önce şu temizlikleri yapın:
- Alanların başındaki ve sonundaki boşlukları kırpın. Uzunluk sınırlarını formda da uygulayın.
doorNumberalanına yalnız dış kapı numarasını yazın. Kat, daire ve tarif bilgisini ayrı bir alanda tutun.- Kullanıcı seçim kutusundan bir kayıt seçtiyse elle yazılmış metni değil, seçilen kaydın
namedeğerini gönderin.
Backend'de doğrulama çağrısı
Aşağıdaki Node.js (18 ve üzeri) fonksiyonu yalnız sunucuda çalışır. Yanıtı arayüzün kullanabileceği sade bir biçime çevirir.
const VALIDATE_URL = "https://api.apiturkiye.com/v1/addresses/validate";
export async function validateAddress(input) {
const body = {
province: input.province.trim(),
district: input.district.trim(),
neighborhood: input.neighborhood.trim(),
street: input.street.trim(),
doorNumber: input.doorNumber.trim(),
};
if (input.doorId) body.doorId = String(input.doorId);
const res = await fetch(VALIDATE_URL, {
method: "POST",
headers: {
"X-API-Key": process.env.APITURKIYE_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (res.status === 429) {
const err = await res.json().catch(() => ({}));
return {
status: "retry_later",
code: err.error?.code,
retryAfter: res.headers.get("Retry-After"),
resetAt: err.error?.resetAt,
};
}
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(`Doğrulama hatası: ${res.status} ${err.error?.code ?? ""}`);
}
const { data, meta } = await res.json();
if (data.valid) {
return {
status: "valid",
normalizedAddress: data.normalizedAddress,
components: data.components,
datasetVersion: meta.datasetVersion,
};
}
return {
status: "invalid",
failedAt: data.failedAt,
matched: data.matched ?? {},
suggestions: data.suggestions ?? [],
};
}
Bu API entegrasyonunda iki durumu birbirinden ayırmak önemlidir. Eşleşmeyen ama geçerli gövdeli bir adres de HTTP 200 döner ve data.valid: false taşır. Hatalı veya eksik JSON gövdesi ise 400 döner. Bu bir kullanıcı hatası değil, kodunuzdaki bir sorundur.
Diğer hata durumlarını şöyle ele alın:
401ve403: Anahtar, paket veya yetki düzeltilmeden otomatik tekrar denemeyin.429 rate_limit_exceeded:Retry-Afterbaşlığındaki süre kadar bekleyin.429 quota_exceeded:resetAtzamanına kadar aynı çağrıyı tekrarlamayın.503 catalog_unavailable: Siparişi engellemek yerine adresi "doğrulanmadı" olarak işaretleyip daha sonra yeniden kontrol edebilirsiniz.
Kararlarınızı error.message metnine değil, HTTP durumuna ve error.code değerine göre verin.
Sonuca göre kullanıcıya öneri sunmak
Başarısız yanıttaki failedAt alanı, zincirin hangi halkada koptuğunu söyler. matched alanı o noktaya kadar eşleşen bileşenleri, suggestions ise aynı üst kapsamdaki en fazla beş adayı içerir. Öneri listesi boş olabilir.
failedAt | Arayüzde yapılacak |
|---|---|
province | İl alanını işaretleyin, varsa önerileri gösterin. |
district | İli koruyun, ilçe için önerileri listeleyin. |
neighborhood | İl ve ilçeyi koruyun, mahalle önerilerini sunun. |
street | Mahalleye kadar olan kısmı koruyun, sokak önerilerini gösterin. |
doorNumber | Kapı numarasını sokağın kapı listesinden seçtirin. |
Kullanıcı bir öneriye tıkladığında ilgili alanı önerinin name değeriyle değiştirin ve doğrulamayı yeniden çalıştırın. matched içindeki bileşenleri dolu bırakmak, kullanıcının formu baştan doldurmasını önler.
const LABELS = {
province: "İl", district: "İlçe", neighborhood: "Mahalle",
street: "Cadde/sokak", doorNumber: "Dış kapı numarası",
};
export function buildFeedback(result) {
if (result.status === "valid") {
return { ok: true, confirmText: result.normalizedAddress };
}
const field = LABELS[result.failedAt];
if (result.failedAt === "doorNumber") {
return { ok: false, field: "doorNumber", message: `${field} bulunamadı. Lütfen listeden seçin.` };
}
return {
ok: false,
field: result.failedAt,
message: result.suggestions.length
? `${field} bulunamadı. Şunlardan birini mi kastettiniz?`
: `${field} bulunamadı. Lütfen bu alanı kontrol edin.`,
options: result.suggestions.map((s) => s.name),
};
}
Başarılı sonuçta normalizedAddress değerini kullanıcıya "Adresiniz bu şekilde kaydedilecek" diyerek göstermek, son bir göz kontrolü sağlar.
Aynı adı taşıyan mahalleler bir istisna oluşturur. Bu durumda uç, adaylardan birini kendiliğinden seçmez. Kullanıcıyı ilçeye bağlı mahalle listesinden seçime yönlendirin ve etikete yerleşim adını ekleyin. Hiyerarşik seçim formunun kurulumu için il, ilçe ve mahalle seçimli adres formu yazısına bakabilirsiniz. Belirsizlik yine de sürerse adresi manuel incelemeye işaretlemek, kullanıcıyı bir döngüde bırakmaktan iyidir.
Aynı kapı numarasında birden fazla kayıt
failedAt: "doorNumber" döndüğünde eşleşen sokağın kodunu kullanarak kapı adaylarını listeleyebilirsiniz. Kullanıcının seçtiği kaydın id değerini doorId olarak gönderip yeniden doğrularsınız. Aşağıdaki örnek Python ile yazılmıştır:
import os
import requests
BASE = "https://api.apiturkiye.com"
HEADERS = {"X-API-Key": os.environ["APITURKIYE_KEY"]}
def validate(address, door_id=None):
body = dict(address)
if door_id:
body["doorId"] = door_id
res = requests.post(f"{BASE}/v1/addresses/validate",
headers=HEADERS, json=body, timeout=10)
res.raise_for_status()
return res.json()
def door_candidates(street_code, door_number):
res = requests.get(f"{BASE}/v1/streets/{street_code}/buildings",
headers=HEADERS,
params={"q": door_number, "limit": 50},
timeout=10)
res.raise_for_status()
return [d for d in res.json()["data"] if d["doorNumber"] == door_number]
address = {"province": "İstanbul", "district": "Kadıköy",
"neighborhood": "Caferağa Mahallesi",
"street": "Moda Caddesi", "doorNumber": "12"}
data = validate(address)["data"]
street = data.get("matched", {}).get("street")
if not data["valid"] and data["failedAt"] == "doorNumber" and street:
candidates = door_candidates(street["code"], address["doorNumber"])
if candidates:
chosen = candidates[0] # Gerçek uygulamada seçimi kullanıcı yapar
data = validate(address, door_id=chosen["id"])["data"]
Kapı kayıtlarındaki label alanı boş olabilir. Seçim listesinde bu alana tek başına güvenmeyin. Aday listesi boş dönüyorsa kullanıcıdan numarayı kontrol etmesini isteyin.
Doğrulanmış adresi stableId ile saklamak
Doğrulanmış bir sipariş adresi için yalnız metni saklamak yetmez. Her bileşenin stableId değeri sürümden bağımsız, kalıcı bir referanstır. Raporlama, bölgeye göre gruplama ve sonraki eşleştirmeler bu kimlik üzerinden güvenle yapılır.
Kapıdaki id alanı ise aktif katalog sürümüne aittir ve sürümler arasında değişebilir. doorId olarak anlık doğrulamada kullanılır, kalıcı referans olarak kullanılmaz. Adlar da kimlik değildir, çünkü aynı ad farklı yerleşimlerde bulunabilir.
export function toAddressRecord(result, rawInput, unitDetail) {
const c = result.components;
return {
verified: true,
normalizedAddress: result.normalizedAddress,
provinceStableId: c.province.stableId,
districtStableId: c.district.stableId,
neighborhoodStableId: c.neighborhood.stableId,
streetStableId: c.street.stableId,
doorStableId: c.door.stableId,
provinceCode: c.province.code,
districtCode: c.district.code,
datasetVersion: result.datasetVersion,
validatedAt: new Date().toISOString(),
rawInput, // Kullanıcının yazdığı haliyle
unitDetail, // Kat, daire, tarif
};
}
datasetVersion, adresin hangi katalog sürümüne göre doğrulandığını gösterir. Destek ekibi bir uyuşmazlığı incelerken işe yarar. Kullanıcının ham girdisini saklamak da sonradan "ne yazmıştı, ne kaydettik" sorusunu yanıtlar. Kişisel veri saklama süreleri ve kapsamı için hukuk uzmanınızın görüşünü alın.
Doğrulanamayan adresleri tamamen reddetmek yerine verified: false ile kaydedip manuel incelemeye almak da bir seçenektir. Yeni yapılmış bir binada sipariş vermek isteyen kullanıcıyı kaybetmemek için bu kararı iş ekibinizle birlikte verin.
Kısa kontrol listesi
- Doğrulama yalnız sunucuda çalışıyor, anahtar
X-API-Keybaşlığında ve ortam değişkeninde duruyor. 400hatası ilevalid: falsesonucu ayrı ele alınıyor.failedAt,matchedvesuggestionsarayüzde alan bazında gösteriliyor.- Kapı belirsizliğinde
doorIdakışı kullanılıyor. - Siparişte
stableIddeğerleri,normalizedAddressvedatasetVersionsaklanıyor.
Adres doğrulama entegrasyonunu kendi akışınıza uyarlamak için API dokümantasyonu üzerinden uç ayrıntılarını inceleyebilirsiniz. Denemeye başlamak için ücretsiz kayıt olup API anahtarınızı alabilirsiniz. Doğrulama özelliğini içeren paketleri paket karşılaştırmasında görebilirsiniz.