Kargo Entegrasyonu: Yurtiçi, Aras, MNG ve Sürat API
Kargo Entegrasyonu Nedir ve Neden Önemlidir?
E-ticaret dünyasında sipariş yönetiminin en kritik halkalarından biri kargo entegrasyonudur. Müşteri siparişi verdikten sonra, kargo sürecinin otomatik olarak başlatılması, takip numarasının oluşturulması ve durum güncellemelerinin anlık olarak alınması hem operasyonel verimliliği artırır hem de müşteri memnuniyetini doğrudan etkiler. Türkiye'deki dört büyük kargo firması — Yurtiçi Kargo, Aras Kargo, MNG Kargo ve Sürat Kargo — geliştiricilere RESTful API'ler sunarak bu süreci programatik olarak yönetmeye olanak tanır.
Bu yazıda her bir kargo firmasının API yapısını, kimlik doğrulama yöntemlerini, gönderi oluşturma ve sorgulama adımlarını detaylı şekilde inceleyeceğiz.
Genel Mimari Yaklaşım
Kargo entegrasyonlarını tek bir yapı altında toplamak için Adapter (veya Strategy) tasarım deseni kullanmanız önerilir. Bu sayede her kargo firması için ayrı bir sınıf yazarken, ortak bir arayüz üzerinden çalışabilirsiniz:
from abc import ABC, abstractmethod
class CargoProvider(ABC):
@abstractmethod
def create_shipment(self, order: dict) -> dict:
"""Gönderi oluşturur, takip numarası döner."""
pass
@abstractmethod
def get_tracking(self, tracking_number: str) -> dict:
"""Gönderi durumunu sorgular."""
pass
@abstractmethod
def cancel_shipment(self, tracking_number: str) -> bool:
"""Gönderiyi iptal eder."""
pass
Bu soyut sınıfı her kargo firması için implemente ettiğinizde, iş mantığınız kargo firmasından bağımsız hale gelir. Yeni bir firma eklemek istediğinizde sadece yeni bir adapter yazmanız yeterlidir.
Yurtiçi Kargo API Entegrasyonu
Yurtiçi Kargo, Türkiye'nin en yaygın kargo ağlarından birine sahiptir ve SOAP tabanlı bir web servisi sunar. Ancak güncel sürümlerinde REST desteği de mevcuttur. Entegrasyon için size bir kullanıcı adı, şifre ve müşteri kodu (firma kodu) verilir.
Kimlik Doğrulama
Yurtiçi Kargo API'sine her istekte kullanıcı bilgileri gönderilir. OAuth veya token tabanlı bir yapı yerine, istek gövdesinde kimlik bilgileri taşınır:
import requests
YURTICI_BASE_URL = "https://webservices.yurticikargo.com/api"
auth_payload = {
"userName": "FIRMA_KULLANICI_ADI",
"password": "FIRMA_SIFRE",
"custCode": "MUSTERI_KODU"
}
Gönderi Oluşturma
def create_yurtici_shipment(order):
payload = {
**auth_payload,
"shipments": [{
"receiverName": order["customer_name"],
"receiverPhone": order["phone"],
"receiverAddress": order["address"],
"cityCode": order["city_code"],
"townCode": order["district_code"],
"desi": order["desi"],
"cargoCount": order["package_count"],
"productType": "D", # Dosya/Paket
"description": f"Siparis #{order['order_id']}"
}]
}
response = requests.post(
f"{YURTICI_BASE_URL}/shipment/create",
json=payload
)
result = response.json()
if result.get("status") == "OK":
return {
"tracking_number": result["data"]["trackingNumber"],
"barcode_url": result["data"]["barcodeUrl"]
}
raise Exception(f"Yurtici hata: {result.get('message')}")
Önemli: Yurtiçi Kargo'da şehir ve ilçe kodları numerik değerlerdir. Bu kodları /address/cities ve /address/towns endpoint'lerinden çekerek kendi veritabanınızda cache'lemeniz önerilir.
Aras Kargo API Entegrasyonu
Aras Kargo, RESTful yapıda modern bir API sunar. Entegrasyon için API Key ve müşteri kodu kullanılır. Aras'ın API'si genellikle daha temiz bir JSON yapısına sahiptir.
Kimlik Doğrulama ve Gönderi Oluşturma
ARAS_BASE_URL = "https://customerservices.araskargo.com.tr/api"
ARAS_HEADERS = {
"Content-Type": "application/json",
"ApiKey": "ARAS_API_KEY"
}
def create_aras_shipment(order):
payload = {
"CustomerCode": "MUSTERI_KODU",
"InvoiceNumber": order["order_id"],
"ReceiverName": order["customer_name"],
"ReceiverPhone": order["phone"],
"ReceiverCity": order["city"],
"ReceiverDistrict": order["district"],
"ReceiverAddress": order["address"],
"PieceCount": order["package_count"],
"Weight": order["weight"],
"VolumetricWeight": order["desi"],
"PaymentType": 1, # 1: Gönderici Ödemeli
"IsCOD": False
}
response = requests.post(
f"{ARAS_BASE_URL}/shipment",
json=payload,
headers=ARAS_HEADERS
)
result = response.json()
return {
"tracking_number": result["TrackingNumber"],
"cargo_key": result["CargoKey"]
}
Kargo Takibi
def track_aras_shipment(tracking_number):
response = requests.get(
f"{ARAS_BASE_URL}/tracking/{tracking_number}",
headers=ARAS_HEADERS
)
data = response.json()
return {
"status": data["StatusDescription"],
"last_update": data["LastTransactionDate"],
"movements": [
{
"date": m["Date"],
"location": m["UnitName"],
"description": m["Description"]
}
for m in data.get("Movements", [])
]
}
MNG Kargo API Entegrasyonu
MNG Kargo, token bazlı kimlik doğrulama kullanan bir REST API sunar. Önce bir token alırsınız, ardından bu token ile işlemlerinizi gerçekleştirirsiniz.
Token Alma
MNG_BASE_URL = "https://api.mngkargo.com.tr/api/v2"
def get_mng_token():
response = requests.post(
f"{MNG_BASE_URL}/auth/token",
json={
"customerNumber": "MNG_MUSTERI_NO",
"password": "MNG_SIFRE",
"identityType": 1
}
)
data = response.json()
return data["jwt"]
# Token'ı önbelleğe alın, her istekte yeniden almayın
mng_token = get_mng_token()
mng_headers = {
"Authorization": f"Bearer {mng_token}",
"Content-Type": "application/json"
}
Gönderi Oluşturma
def create_mng_shipment(order):
payload = {
"referenceId": str(order["order_id"]),
"receiver": {
"fullName": order["customer_name"],
"phone": order["phone"],
"cityCode": order["city_code"],
"districtName": order["district"],
"address": order["address"]
},
"pieces": [{
"weight": order["weight"],
"desi": order["desi"],
"content": order.get("description", "E-ticaret gonderisi")
}],
"paymentType": "SENDER",
"shipmentServiceType": "STANDART"
}
response = requests.post(
f"{MNG_BASE_URL}/shipment",
json=payload,
headers=mng_headers
)
result = response.json()
return {
"tracking_number": result["trackingNumber"],
"barcode_zpl": result.get("barcodeZpl")
}
Dikkat: MNG Kargo token'larının belirli bir süresi (genellikle 24 saat) vardır. Token'ın süresini takip ederek otomatik yenileme mekanizması kurmanız önerilir. Bunu basit bir dekoratör ile çözebilirsiniz:
import time
class MNGTokenManager:
def __init__(self):
self.token = None
self.expires_at = 0
def get_headers(self):
if time.time() >= self.expires_at:
self.token = get_mng_token()
self.expires_at = time.time() + 82800 # 23 saat
return {
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json"
}
Sürat Kargo API Entegrasyonu
Sürat Kargo, SOAP ve REST olmak üzere iki farklı API seçeneği sunar. Yeni projelerde REST API tercih edilmelidir. Kimlik doğrulaması için kullanıcı adı ve şifre çifti ile Basic Auth veya token tabanlı erişim sağlanır.
Gönderi Oluşturma
SURAT_BASE_URL = "https://api.suratkargo.com.tr/api"
SURAT_AUTH = ("KULLANICI_ADI", "SIFRE")
def create_surat_shipment(order):
payload = {
"customerReferenceCode": str(order["order_id"]),
"receiverName": order["customer_name"],
"receiverPhoneNumber": order["phone"],
"receiverProvince": order["city"],
"receiverCounty": order["district"],
"receiverAddress": order["address"],
"packageCount": order["package_count"],
"weight": order["weight"],
"desi": order["desi"],
"paymentType": "G", # G: Gönderici Ödemeli
"collectionType": "N" # N: Normal, K: Kapıda Ödeme
}
response = requests.post(
f"{SURAT_BASE_URL}/shipment/create",
json=payload,
auth=SURAT_AUTH
)
result = response.json()
return {
"tracking_number": result["trackingNumber"],
"label_pdf": result.get("labelUrl")
}
Webhook ve Durum Güncellemeleri
Kargo firmalarının çoğu, gönderi durumu değiştiğinde sizin belirlediğiniz bir URL'e bildirim gönderebilir. Bu webhook mekanizması, sürekli polling yapmak yerine olay tabanlı mimari kurmanıza olanak tanır:
from flask import Flask, request
app = Flask(__name__)
@app.route("/webhooks/cargo", methods=["POST"])
def cargo_webhook():
data = request.json
provider = data.get("provider")
tracking = data.get("trackingNumber")
status = data.get("status")
# Sipariş durumunu güncelle
update_order_status(tracking, status)
# Müşteriye bildirim gönder
if status in ("DELIVERED", "TESLIM_EDILDI"):
send_customer_notification(tracking, "Kargonuz teslim edildi!")
elif status in ("IN_TRANSIT", "TRANSFER"):
send_customer_notification(tracking, "Kargonuz yolda!")
return {"status": "ok"}, 200
Webhook desteği olmayan firmalarda, bir cron job veya Celery task ile belirli aralıklarla (örneğin her 30 dakikada bir) teslim edilmemiş gönderilerin durumunu sorgulayabilirsiniz.
Hata Yönetimi ve En İyi Pratikler
- Retry mekanizması kurun: Kargo API'leri zaman zaman geçici hatalar verebilir. Exponential backoff ile 3 denemeye kadar tekrar edin.
tenacityveyarequests.adapters.HTTPAdapterbu iş için idealdir. - Timeout belirleyin: Her API çağrısında mutlaka
timeout=(5, 30)parametresi kullanın. İlk değer bağlantı, ikinci değer okuma zaman aşımıdır. - Loglama yapın: Her istek ve yanıtı loglayın. Kargo firmasıyla yaşanan anlaşmazlıklarda bu loglar kritik öneme sahiptir.
- Idempotency sağlayın: Aynı sipariş için birden fazla gönderi oluşturulmasını engellemek adına, sipariş ID'sini referans kodu olarak gönderin ve mükerrer kontrol mekanizması kurun.
- Sandbox ortamlarını kullanın: Dört firmanın da test ortamları mevcuttur. Canlıya geçmeden önce tüm akışları sandbox üzerinde doğrulayın.
- Rate limiting'e dikkat edin: Özellikle toplu gönderi oluşturma ve sorgulama işlemlerinde API limitlerini aşmamaya özen gösterin. Toplu işlemleri batch endpoint'leri üzerinden yapın.
Birleşik Kargo Servisi Örneği
Tüm kargo firmalarını tek bir servis altında toplamak için adapter desenini kullanarak şu şekilde bir yapı kurabilirsiniz:
class CargoService:
_providers = {
"yurtici": YurticiProvider,
"aras": ArasProvider,
"mng": MNGProvider,
"surat": SuratProvider,
}
def __init__(self, provider_name: str):
provider_class = self._providers.get(provider_name)
if not provider_class:
raise ValueError(f"Desteklenmeyen kargo: {provider_name}")
self.provider = provider_class()
def ship(self, order: dict) -> dict:
result = self.provider.create_shipment(order)
save_tracking_to_db(order["order_id"], result["tracking_number"])
return result
def track(self, tracking_number: str) -> dict:
return self.provider.get_tracking(tracking_number)
# Kullanım
service = CargoService("aras")
result = service.ship(order_data)
print(f"Takip No: {result['tracking_number']}")
Sonuç
Yurtiçi, Aras, MNG ve Sürat Kargo API entegrasyonları farklı kimlik doğrulama yöntemleri ve veri yapıları kullansa da, ortak bir soyutlama katmanı ile yönetilebilir hale gelir. Adapter deseni sayesinde yeni kargo firmaları eklemek veya mevcut firmaları değiştirmek iş mantığınızı etkilemez. Hata yönetimi, loglama ve idempotency gibi konulara özen göstererek güvenilir ve sürdürülebilir bir kargo altyapısı kurabilirsiniz. Sandbox ortamlarında kapsamlı testler yaptıktan sonra canlıya geçmeniz, olası sorunları en aza indirecektir.