Mydema Plugin API — Entegrasyon Kılavuzu
Bu kılavuz, bir e-mağazayı veya ERP'yi Mydema'ya resmi OpenCart / WooCommerce eklentilerinin yaptığı şekilde bağlayan geliştiriciler içindir: ödeme adımında canlı kargo fiyatlarını göstermek, ödenmiş siparişleri Mydema gönderilerine (voucher'lara) dönüştürmek ve bunları takip etmek.
Bu klasörde çalıştırmaya hazır bir Postman koleksiyonu bulunur:
| Dosya | Nedir |
|---|---|
mydema-plugin-api.postman_collection.json | Bu kılavuzdaki tüm istekler, çağrı sırasıyla; token'ı, sepet id'sini ve seçilen rotayı bir istekten diğerine taşıyan script'lerle birlikte |
mydema-staging.postman_environment.json | Staging temel URL'si ve anahtarınız için yer tutucular |
mydema-production.postman_environment.json | Canlı ortam (production) temel URL'si ve anahtarınız için yer tutucular |
#1. Ortamlar
| Ortam | Temel URL |
|---|---|
| Canlı (production) | https://app.mydema.gr |
| Test (staging) | https://stage.mydema.gr |
Bu kılavuzdaki her yol (path) temel URL'ye (base URL) göredir. Anahtarı self-servis bağlantı akışıyla aldığınızda (bölüm 2.1), claim yanıtı kullanmanız gereken temel URL'yi bildirir (apiEndpoint).
Staging kendi verilerini ve kendi anahtarlarını kullanır; canlı ortam anahtarı staging'de çalışmaz.
#2. API anahtarı edinme
Bir API anahtarı (API key; mk_live_…, 48 karakter) tek bir siteye (Mydema'daki adıyla bir "integration") aittir. Her sitenin şunları vardır:
- API'nin adına işlem yaptığı bir servis kullanıcısı (service user),
- gönderilerinin bedelini ödeyen bir ön ödemeli kredi cüzdanı (credit wallet) (bölüm 7.9),
- isteğe bağlı olarak, her girişin kontrol edildiği bir alan adı (domain) ve bir IP izin listesi (IP allowlist).
Anahtar edinmenin iki yolu vardır.
#2.1 Self-servis bağlantı (eklentiler için önerilir)
Mağaza bağlanmak için başvurur, Mydema personeli başvuruyu panelden onaylar ve mağaza anahtarını teslim alır. Bu üç endpoint herkese açıktır (token gerekmez).
- Rastgele bir gizli değer (secret) üretin (örneğin hex olarak 32 rastgele bayt) ve bunu kendi tarafınızda saklayın.
POST/api/plugin/site-requestisteğini mağaza bilgileriyle veclaimSecretHash= bu gizli değerin SHA-256 hex karşılığı ile gönderin. BirrequestIdalırsınız.statusdeğeriapproved(veyarejectReasonile birlikterejected) olana kadarPOST/api/plugin/site-request/statusendpoint'inirequestId+ hamclaimSecretile yoklayın (polling). Birkaç dakikada bir yoklamak yeterlidir; onay elle verilir.POST/api/plugin/site-request/claimisteğini aynı iki değerle gönderin.apiKey,apiEndpointveintegrationIdalırsınız. Her teslim alma (claim) işlemi yeni bir anahtar üretir ve öncekini geçersiz kılar, bu yüzden anahtarı bir kez teslim alın ve saklayın.
#2.2 Mydema tarafından verilen anahtar
Mydema personeli siteyi panelde oluşturup anahtarı size doğrudan gönderebilir. Bu anahtar, teslim alınmış bir anahtarla tamamen aynı şekilde çalışır.
Anahtarı bir parola gibi saklayın. Mydema yalnızca hash'ini tutar ve anahtarı bir daha gösteremez; kaybolan bir anahtar, yeni bir teslim alma işlemi ya da Mydema'dan yeni bir anahtar demektir.
#3. Kimlik doğrulama
#3.1 Giriş yapma
POST /api/auth/plugin-login
Content-Type: application/json
{"apiKey": "mk_live_…", "apiSecret": "", "domain": "myshop.gr"}| Alan | Zorunlu | Notlar |
|---|---|---|
apiKey | evet | mk_live_… anahtarı |
apiSecret | evet, "" olarak | mk_live_ anahtarları için boş bir string olmalıdır. (Boş olmayan bir değer, eski anahtar + secret düzenine geçirir.) |
domain | sitenin bir alan adı varsa | Mağazanızın host adı. Şema (scheme), yol, port ve baştaki www. her iki tarafta da yok sayılır. |
Sitenin bir IP izin listesi varsa, istek listelenen adreslerden birinden gelmelidir (birebir eşleşme, CIDR aralıkları desteklenmez). Ret mesajı, Mydema'nın gördüğü adresi size bildirir.
Yanıt (kısaltılmış):
{
"data": {
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9…",
"expiresAt": "2026-10-31T07:59:12+00:00",
"expiresIn": 43200,
"integration": {"id": 8, "name": "My Shop", "platform": null, "domain": "myshop.gr"},
"tokenData": {
"user": {"id": 260, "userRole": "PLUGIN", "customer": 273},
"company": {"id": 4, "name": "Customers Company", "type": "CUS"},
"plugin": true,
"integrationId": 8,
"pluginCustomerId": 273,
"pluginCustomerName": "Customers Company"
}
}
}integrationId ve pluginCustomerId (cüzdan sahibi) değerlerini saklayın; birkaç endpoint bunları path içinde ister.
#3.2 Token'ı kullanma
Token'ı diğer tüm isteklerde gönderin:
Authorization: Bearer <accessToken>Tam olarak Bearer + bir boşluk; kontrol büyük/küçük harfe duyarlıdır.
#3.3 Geçerlilik süresi ve yenileme
- Token 12 saat, kayan süreyle geçerlidir: her başarılı istek, son geçerlilik zamanını o andan itibaren 12 saat sonraya taşır. Düzenli trafiği olan bir mağaza aynı token'ı süresiz olarak kullanmaya devam eder.
- Token hâlâ geçerliyken tekrar giriş yapmak aynı token'ı döndürür; böylece aynı mağazanın birden fazla worker'ı bu token'ı güvenle paylaşabilir.
Token artık geçerli olmadığında, her endpoint şu gövdeyle HTTP 401 döndürür (not:
data/errorsarmalayıcısı yoktur):JSON{"message": "User timed out.", "code": "TOKEN_EXPIRED"}codeAnlamı MISSING_TOKENAuthorizationheader'ı yok / hatalı biçimde ya da token bilinmiyorTOKEN_EXPIRED12 saatten uzun süre hiç istek yapılmamış TOKEN_CHANGEDBu site için daha yeni bir token üretilmiş Bunlardan herhangi birinde: tekrar giriş yapın ve isteği bir kez tekrarlayın.
Giriş hataları, standart hata zarfı (error envelope) içinde HTTP 403 olarak döner (bölüm 4.3); örneğin Invalid credentials., Domain not allowed for this key., IP not allowed for this key. Detected IP: ….
#4. Ortak kurallar: istekler, yanıtlar, hatalar
#4.1 Gönderilen istekler
- JSON gövdeler, UTF-8,
Content-Type: application/json.Accept: application/jsongönderin. - Listeleme ve arama endpoint'leri
POST'tur, yalnızca okuma yapsalar bile. BunlarıGETile çağırmak ya 405 döndürür ya da farklı bir HTTP route'una düşüp başarısız olur. - Para birimi EUR'dur. Ağırlıklar kg cinsindendir. Paket ölçüleri isteklerde cm cinsindendir (Mydema bunları metre olarak saklar ve döndürür).
#4.2 Başarılı yanıtlar
HTTP 200 ve bir data nesnesi:
{"data": {"basketId": "695bca64-c645-4398-8b67-c16adcc89de4"}}#4.3 Hata yanıtları
Mydema birden fazla hata biçimi kullanır. Hepsini ele alın.
A. Standart zarf (çoğu endpoint):
{"error": {"message": "{\"message\":\"District is not valid.\",\"code\":400}", "code": 500, "file": null, "line": null}}error.message çoğunlukla kendisi de bir JSON string'idir. Okunabilir message değerini ve kastedilen code değerini (400 hatalı girdi, 403 size ait değil, 404 bulunamadı, 409 çakışma) almak için onu ikinci kez decode edin. Geçerli bir JSON değilse düz metin olarak kullanın.
B. Framework hatası (bazı endpoint'ler; bölüm 7'de "B" ile işaretlenmiştir):
{"type": "https://tools.ietf.org/html/rfc2616#section-10", "title": "An error occurred", "status": 500, "detail": "Internal Server Error"}Canlı ortamda detail geneldir, dolayısıyla nedeni göremezsiniz. Göndermeden önce girdinizi doğrulayın (bölüm 6) ve gönderdiğiniz isteği loglayın.
C. Token hataları: HTTP 401, {"message": …, "code": "TOKEN_EXPIRED"} (bölüm 3.3).
D. Bağlantı endpoint'leri (/api/plugin/site-request*), gerçek HTTP durum koduyla birlikte temiz bir zarf kullanır: {"error": {"message": "Request is not approved.", "code": 409}}.
E. Geçici veritabanı kilidi: Retry-After header'ı ile HTTP 503 ve {"error": {"message": "…temporary database lock…", "code": "DB_RETRY"}}. Bekleyip tekrar deneyin.
#4.4 HTTP durum kodları
| Durum | Anlamı | Tekrar denenmeli mi? |
|---|---|---|
| 200 | Başarılı | — |
| 401 | Token sorunu (bölüm 3.3) | Tekrar giriş yapın, ardından bir kez yeniden deneyin |
| 403 | Giriş reddedildi | Hayır; anahtarı / alan adını / IP'yi düzeltin |
| 404, 405 | Yanlış yol veya yanlış HTTP metodu | Hayır |
| 500 | İş kuralı veya doğrulama hatası (hatalı girdi, bulunamadı, yetersiz kredi…) — asıl neden gövdededir | Hayır: aynı istek yine başarısız olur |
| 503 | Geçici veritabanı kilidi | Evet, Retry-After sonrasında |
| Ağ hatası / zaman aşımı | — | Okuma istekleri ve create: evet. complete: evet, sepet başına güvenlidir (kural 8.2) |
Doğrulama ve iş kuralı hatalarının çoğu 4xx değil, HTTP 500 olarak döner. 500'ü sunucu kesintisi olarak değerlendirmeyin ve körü körüne tekrar denemeyin.
#4.5 Liste isteği biçimi
"ListRequest" olarak belirtilen endpoint'ler şu isteğe bağlı gövdeyi kabul eder:
| Anahtar | Tür | Notlar |
|---|---|---|
params | object | Endpoint'e özgü filtreler, ör. {"state": 65} |
limit | int | Sayfa boyutu. Çoğu listede varsayılan değer yoktur: gönderilmezse her şey döner |
offset | int | Atlanacak satır sayısı |
orderBy | object | ör. {"name": "ASC"} |
{} geçerli bir gövdedir. Sayılar string değil, sayı olmalıdır.
Sonuçlar {"data": {"<items>": [...], "meta": {"total": 17}}} biçimindedir.
#5. Entegrasyon akışına genel bakış
tek seferlik kurulum ödeme adımı (alışveriş sepeti başına) sipariş (ödenmiş sipariş başına)
──────────────────── ───────────────────────────────────── ────────────────────────────────
plugin-login create (sepet) ──► search-routes update-basket (gerçek iletişim, not)
shipment/templates (çıkış deposu başına bir sepet) search-routes (güncel)
suppliers (depolar) rota başına Σ segment.price göster set-routes (seçilen rota)
konum sorguları rota yok mu? ──► scenarios kredi bakiyesi kontrolü
payment-complete (creditAmount)
complete ──► transportation id
voucher-pdf, takip- Kurulum. Giriş yapın. Paket şablonlarını yükleyin. Malların gönderildiği her depoyu bir depo / alım noktası (supplier) olarak kaydedin (ya da her şey için tek bir çıkış ilçesi (district) kullanın). Müşterilerinizin adreslerini Mydema ilçe id'lerine eşleyin (bölüm 6.1).
- Ödeme adımında fiyat teklifi. Alışveriş sepetindeki her çıkış deposu için paketler, gönderici (depo) ve alıcı ile bir sepet (basket) oluşturun, ardından rotaları (route) arayın. Her rota bir kargo seçeneğidir; fiyatı, rota ayaklarının (segment)
pricedeğerlerinin toplamıdır (bölüm 7.6).basketIdve rotanın kimliğini (segmentlerinrouteIddeğerleri) müşterinin seçimiyle birlikte saklayın. - Sipariş → gönderi. Sipariş ödendiğinde sepeti müşterinin gerçek iletişim bilgileri ve notuyla güncelleyin, rotaları yeniden arayın (fiyatlar o anda günceldir), seçilen rotayı
set-routesile gönderin, ödemeyipayment-completeile kaydedin, ardındancompleteendpoint'ini bir kez çağırın. Mydema taşıma siparişi (transportation order) id'sini alırsınız. - Sonrası. Voucher PDF'ini indirin ve durum için takip endpoint'ini yoklayın.
Birden fazla depodan mal içeren bir alışveriş sepeti birden fazla sepet demektir: her depo (gönderici) için bir tane; her biri ayrı ayrı fiyatlandırılır ve tamamlanır. Resmi eklentiler bunların toplamını tek bir kargo fiyatı olarak gösterir.
#6. Referans verileri: konumlar, paketler, kişiler
#6.1 Konumlar
Mydema'nın konum ağacı ülke → il (state) → belediye (city) → ilçe (district) şeklindedir. Canlı ortamda state bir Yunan iline (νομός, prefecture), city ise bir belediyeye (municipality) karşılık gelir.
Gönderi endpoint'leri göndericinin ve alıcının ilçe id'sine ihtiyaç duyar. Rotalar ilçeler arasında işler ve yalnızca hizmet verilen ilçelerin rotası vardır.
| Elinizdeki | Kullanın |
|---|---|
| Müşterinin yazdığı bir yer adı | POST/api/location/district/avail-autocomplate → locations[].id (yalnızca hizmet verilen ilçeler) |
| Bir il; hizmet verilen ilçelerini istiyorsunuz | params.state ile POST/api/location/district/avail-list → locations[].districtId |
| Bir posta kodu | POST/api/autocomplate/location/postal-code → yer adları (id değil); ardından adı yukarıdaki iki çağrıyla eşleştirin |
| İl adı → state id | POST/api/location/state/list |
Hizmet verilen tam liste (filtresiz avail-list) birkaç saat önbelleğe alınabilecek kadar küçüktür.
#6.2 Paketler
Paket kataloğunu günde bir kez GET/api/shipment/templates üzerinden yükleyin. Bu yazının yazıldığı tarihteki değerler (Ekim 2026):
| Kod | Tür | Hacim aralığı (m³) | Faturalanan (m³) | Ağırlık (kg) | Taban ölçüsü (cm) |
|---|---|---|---|---|---|
| S | M3 (koli) | 0 – 0.20 | 0.20 | 0 – 80 | — |
| M | M3 (koli) | 0.2001 – 0.50 | 0.50 | 0 – 80 | — |
| L | M3 (koli) | 0.5001 – 1.00 | 1.00 | 0 – 80 | — |
| XL | M3 (koli) | 1.0001 – 1.60 | 1.60 | 0 – 80 | — |
| PEUR1 | PL (palet) | — | 1.92 | 80 – 1000 | 80 × 120, yükseklik 200 |
| PEUR2 | PL (palet) | — | 2.40 | 80 – 1000 | 100 × 120, yükseklik 200 |
| PMNI6 | PL (palet) | — | 0.48 | 80 – 300 | 60 × 80, yükseklik 100 |
Mydema bu değerleri değiştirebilir; bunları koda gömmek yerine API'den okuyun.
Paket nesnesi (create, update-basket, quote-districts tarafından kullanılır):
| Alan | Tür | Zorunlu | Notlar |
|---|---|---|---|
weightType | string | evet | M3 = koli / paket, PL = palet, KG = özel ürünler |
weight | number | evet | kg, tek bir koli |
width, height, depth | number | evet | cm |
isSensitive | bool | hayır | Kırılacak eşya; varsayılan false |
shipmentType, shipmentSize | string | aşağıya bakın | Şablon / özel ürün kodları |
productWidth, productHeight, productDepth | number | hayır | Ürünün kendi ölçüsü (cm). Yalnızca voucher üzerine basılır; fiyatlandırmada kullanılmaz |
Fiyat nasıl belirlenir:
- M3 (koli): hacme (
width × height × depth, m³) ve ağırlığa göre. Tek bir koli en fazla 220 kg olabilir. Her fiziksel koli için bir paket gönderin.shipmentTypeveshipmentSizealanlarını göndermeyin — bilinmeyen bir kod tüm isteğinSize is not valid/Type is not validhatasıyla başarısız olmasına yol açar. - PL (palet): taban ölçüsüne göre:
widthvedepth, yukarıdaki palet taban ölçülerinden biri olmalıdır (iki yönde de olabilir, ör. PEUR1 için 80 ve 120).heightyük yüksekliğidir; fiyatı etkilemez.shipmentType/shipmentSizegöndermeyin. - KG (özel ürünler): hem
shipmentTypehemshipmentSizezorunludur ve Mydema'nın o ürün için size verdiği kodlar olmalıdır.
Birden fazla aynı koli = birden fazla paket nesnesi. weight değeri 0 olan ve shipmentType içermeyen bir paket sessizce yok sayılır.
#6.3 Gönderici ve alıcı
{
"firstName": "Μαρία",
"lastName": "Παπαδοπούλου",
"email": "[email protected]",
"phone": "6900000000",
"customerType": "I",
"taxId": "",
"address": {
"address": "Οδός Παραλήπτη 5",
"postalCode": "53100",
"district": 158,
"city": null,
"officeName": "",
"taxOffice": "",
"location": ""
}
}| Alan | Notlar |
|---|---|
customerType | I bireysel, S kurumsal |
taxId | Kurumsal müşteriler için Yunan vergi numarası (ΑΦΜ), en fazla 20 karakter |
address.district | Zorunlu. İlçe id'si (bölüm 6.1). Eksik veya bilinmeyen bir ilçe isteğin başarısız olmasına yol açar |
address.city | Belediye id'si ya da ilçeden alınması için null (önerilir) |
email, phone | Sipariş tamamlarken gerçek değerler kullanın: Mydema gönderi oluşturulduğunda alıcıya e-posta gönderir ve kurye telefon numarasını görür. Fiyat teklifi aşamasında yer tutucu değerler sorun değildir |
Gönderici sizin deponuzdur. customerType olarak S ve deponun ilçesini kullanın.
#7. Endpoint referansı
7.1'dekiler dışındaki tüm endpoint'ler Authorization: Bearer <token> gerektirir. "Hatalar: A/B" ifadesi bölüm 4.3'teki hata biçimlerini belirtir.
#7.1 Bağlantı (herkese açık)
#POST/api/plugin/site-request
| Alan | Zorunlu | Notlar |
|---|---|---|
domain | evet | Mağaza host adı |
companyName | evet | en fazla 191 |
contactName | evet | en fazla 100 |
contactEmail | evet | Onay e-postası bu adrese gider |
claimSecretHash | evet | Gizli değerinizin SHA-256 hex karşılığı (64 karakter) |
platform | hayır | opencart3, opencart4, woocommerce, magento2; diğer değerler boş olarak saklanır |
siteName, pluginVersion, taxId, taxOffice, address, postalCode, city, contactPhone | hayır | Fatura / iletişim bilgileri |
{"data": {"requestId": "9b2f8f8e-3c1a-4f7e-9d1e-1a2b3c4d5e6f", "status": "pending"}}Aynı alan adı ve gizli değer tekrar gönderilirse aynı başvuru döner. Hatalar (D biçimi): 400 geçersiz alan, 409 bu alan adı için açık başka bir başvuru var, 429 tek bir IP'den saatte 5'ten fazla başvuru.
#POST/api/plugin/site-request/status
Gövde: {"requestId": "…", "claimSecret": "<raw secret>"}
{"data": {"status": "approved", "siteName": "My Shop", "domain": "myshop.gr", "rejectReason": null}}status: pending, approved, rejected, claimed. Bilinmeyen id veya yanlış gizli değer → 404.
#POST/api/plugin/site-request/claim
Gövde: {"requestId": "…", "claimSecret": "<raw secret>"}
{"data": {"apiKey": "mk_live_…", "apiEndpoint": "https://app.mydema.gr", "integrationId": 57}}Her çağrı anahtarı yeniler (rotate). Hatalar: 404, 409 onaylanmamış, 410 site artık aktif değil.
#7.2 Kimlik doğrulama
| Endpoint | Notlar |
|---|---|
POST/api/auth/plugin-login | bölüm 3.1 |
POST/api/auth/plugin-autologin | Satıcının tarayıcısında parolasız olarak bir Mydema panel sayfası (kredi yükleme ödemesi) açar (bölüm 7.9) |
#7.3 Konumlar
Hepsi POST'tur. Hatalar: B.
#POST/api/location/district/avail-autocomplate
Bir adla eşleşen hizmet verilen ilçeler (Yunanca veya Latin harfleriyle, aksanlar isteğe bağlı; eşleşme yoksa bulanık eşleşmeye (fuzzy match) geçer).
Gövde: {"term": "Κομοτην", "limit": 10} (limit varsayılanı 100)
{"data": {"locations": [{
"id": 1, "name": "Κομοτηνή", "nameLatin": "Komotini", "cityName": "Κομοτηνή", "CountryName": "Ελλάδα",
"city": {"id": 67, "name": "Κομοτηνή", "state": {"id": 65, "name": "Ροδόπης"}, "country": {"code": "GR"}},
"country": {"code": "GR", "name": "Ελλάδα"}
}]}}(CountryName gerçekten büyük C ile başlar.)
#POST/api/location/district/avail-list — ListRequest
Hizmet verilen tüm ilçeler, düz liste halinde. Filtreler: params.state (il id'si), params.city (belediye id'si). limit olmadan hepsini alırsınız.
Gövde: {"params": {"state": 65}}
{"data": {
"locations": [{"districtId": 1, "name": "Κομοτηνή", "cityId": 67, "cityName": "Κομοτηνή", "stateId": 65, "stateName": "Ροδόπης"}],
"locationDistricts": ["…same districts in the long format of avail-autocomplate…"],
"meta": {"total": 4, "limit": null, "offset": 0}
}}locations kullanın; locationDistricts eski istemciler için korunmaktadır.
#POST/api/autocomplate/location/postal-code
(autocomplate yazımı path'in bir parçasıdır.) Gövde: {"term": "69131", "limit": 50} (önek eşleşmesi; limit varsayılanı 10).
{"data": {"locationPostalCodes": [
{"id": 12752, "code": "69131", "district": "ΑΓΙΑ ΒΑΡΒΑΡΑ", "city": "ΡΟΔΟΠΗΣ"},
{"id": 12754, "code": "69131", "district": "ΚΟΜΟΤΗΝΗ", "city": "ΡΟΔΟΠΗΣ"}
]}}district yerleşim yerinin adı, city ise ilin adıdır — id değil, metin. Bir posta kodu birden fazla yerleşim yerini kapsayabilir.
#POST/api/location/state/list — ListRequest
Gövde: {"orderBy": {"name": "ASC"}}
{"data": {"locationStates": [{"id": 65, "name": "Ροδόπης", "nameLatin": null, "originalName": "Ροδόπης", "country": {"code": "GR"}}], "meta": {"total": 59}}}#POST/api/location/city/avail-list — ListRequest
Bir ile ait tüm belediyeler. params.state zorunludur (olmadan liste boş döner).
Gövde: {"params": {"state": 65}} → {"data": {"locationCities": [{"id": 542, "name": "Αίγειρος", "state": {"id": 65, "name": "Ροδόπης"}}], "meta": {"total": 14}}}
#7.4 Paket şablonları
#GET/api/shipment/templates
{"data": {"templates": [
{"code": "S", "name": "Small", "shipmentType": "M3", "lengthCm": 0, "widthCm": 0, "heightCm": 0,
"minWeightKg": 0, "maxWeightKg": 80, "minVolumeM3": 0, "maxVolumeM3": 0.2, "volumeM3": 0.2,
"billingVolumeM3": 0.2, "isPallet": false, "displayOrder": 1},
{"code": "PEUR1", "name": "Standard EUR 1 80x120x200", "shipmentType": "PL", "lengthCm": 80, "widthCm": 120, "heightCm": 200,
"minWeightKg": 80, "maxWeightKg": 1000, "minVolumeM3": null, "maxVolumeM3": null, "volumeM3": 1.92,
"billingVolumeM3": 1.92, "isPallet": true, "displayOrder": 5}
]}}Nasıl kullanılacakları için bölüm 6.2'ye bakın. Hatalar: A.
#7.5 Depolar / alım noktaları (suppliers)
Bir depo / alım noktası (supplier), mallarınızın teslim alındığı bir yerdir. Onun ilçesi, o mallara ait sepetlerin gönderici ilçesidir. Her site yalnızca kendi alım noktalarını görür ve düzenler. Hatalar: A (yetki sorunları: B).
Alım noktası (supplier) nesnesi
{
"id": 3, "integrationId": 8, "companyId": 4, "companyUserId": 260,
"name": "Αποθήκη Κομοτηνής", "pickupAddress": "Οδός Δοκιμής 1",
"districtId": 1, "districtName": "Κομοτηνή", "cityId": 67, "cityName": "Κομοτηνή", "postalCode": "69100",
"latitude": null, "longitude": null, "googlePlaceId": null,
"contactName": "Γιώργος", "contactPhone": "2531000000", "contactEmail": null,
"pickupHoursStart": "09:00:00", "pickupHoursEnd": "17:00:00",
"notes": null, "externalCode": "WH-1", "isActive": true,
"createdAt": "2026-10-30T19:58:41+00:00", "updatedAt": "2026-10-30T19:58:41+00:00"
}Yazılabilir alanlar: name (zorunlu, en fazla 200), pickupAddress (zorunlu, en fazla 500), districtId (belediye bundan türetilir), postalCode, latitude, longitude, googlePlaceId, contactName, contactPhone, contactEmail, pickupHoursStart, pickupHoursEnd ("09:00"), notes, externalCode (sizin kendi id'niz, site başına benzersiz).
| Endpoint | Gövde | Yanıt |
|---|---|---|
POST/api/supplier | alım noktası alanları | {"data": {"supplier": {…}}} |
GET/api/supplier/{id} | — | {"data": {"supplier": {…}}} |
PUT/api/supplier/{id} | yalnızca değişecek alanlar | {"data": {"supplier": {…}}} |
DELETE/api/supplier/{id} | — | {"data": {"deleted": true}} |
POST/api/supplier/list | {} | {"data": {"suppliers": […], "total": 1}} |
GET/api/integration/{integrationId}/supplier | — | Liste ile aynı |
POST/api/supplier/sync | {"integrationId": 8, "rows": [{…, "externalCode": "WH-1", "cityId": 67}]} | {"data": {"created": 1, "updated": 0, "errors": []}} |
Notlar:
PUTisteğindeintegrationIdgöndermeyin (ya da "koru" anlamına gelen0gönderin)."districtId": nullgöndermek ilçeyi temizler.sync,externalCodedeğerine göre upsert yapar (çağrı başına en fazla 200 satır) ve her satırdacityIdister. Satır hataları, satırınindexdeğeriyle birlikteerrorsiçinde listelenir; geri kalanlar kaydedilir.- Silinmiş bir alım noktasının
externalCodedeğeri yeniden kullanılamaz. - Sık görülen hatalar:
District is not valid.,Please select a district.,Supplier not found.,Supplier with this externalCode already exists for this integration.(409).
#7.6 Gönderiler: fiyat teklifi
#POST/api/transportation/create — sepet oluşturma
{
"packages": [{"weightType": "M3", "weight": 25, "width": 58.48, "height": 58.48, "depth": 58.48, "isSensitive": false}],
"sender": {"firstName": "Αποθήκη", "lastName": "Κομοτηνής", "email": "[email protected]", "phone": "2531000000", "customerType": "S",
"address": {"address": "Οδός Δοκιμής 1", "postalCode": "69100", "district": 1, "city": null}},
"receiver": {"firstName": "Μαρία", "lastName": "Παπαδοπούλου", "email": "[email protected]", "phone": "6900000000", "customerType": "I",
"address": {"address": "Οδός Παραλήπτη 5", "postalCode": "", "district": 158, "city": null}}
}{"data": {"basketId": "695bca64-c645-4398-8b67-c16adcc89de4"}}Sepet, geçici bir fiyat teklifi / sipariş taslağıdır. Henüz hiçbir şey rezerve edilmez. Hatalar: B. Sık görülen nedenler: bilinmeyen bir district, Size is not valid / Type is not valid (bkz. 6.2), eksik sender veya receiver. Sipariş notunu daha sonra update-basket ile gönderin (buradaki comments alanı 10 karakterle sınırlıdır).
#POST/api/transportation/search-routes
Gövde: {"basketId": "…"}
{"data": {
"basketId": "695bca64-c645-4398-8b67-c16adcc89de4",
"serviceFee": 10,
"serviceFees": [{"total": 10.07, "serviceFee": 0, "tax": 0.22}],
"routes": [[
{"startPoint": 3, "endPoint": 698, "company": 15, "onlyB2B": false, "routeId": 78,
"price": 5.26, "tax": 1.01, "currency": "EUR", "hasAddressDelivery": true,
"deliveryDoor": false, "carrier": false, "deliveryDoorPrice": 0, "deliveryDoorTax": 0, "carrierPrice": 0, "carrierTax": 0,
"data": {"route": "Ροδόπης, Κομοτηνή-Θεσσαλονίκη, Κέντρο Διαλογής", "company": "ΚΟΚΟΒΙΔΗΣ ΦΡΙΓΚΟΤΡΑΝΣ",
"endPointName": "Θεσσαλονίκη, Κέντρο Διαλογής", "endPointAddress": "Π.Σ.Ο. Θεσ/νίκης-Ωραιοκάστρου\nΤηλέφωνα : 2310 785 050",
"companyPrices": [7095], "price": 4.78, "currency": "EUR"},
"packagePrices": [{"weightType": "M3", "weight": 25, "width": 0.5848, "height": 0.5848, "depth": 0.5848,
"shipmentSize": null, "shipmentType": null, "isSensitive": false, "productDimsCm": null,
"price": 4.78, "tax": 0.92, "basePrice": 3.86}],
"basePrice": 4.25, "serviceFee": 0},
{"startPoint": 698, "endPoint": 179, "company": 18, "onlyB2B": false, "routeId": 385,
"price": 5.84, "tax": 1.13, "currency": "EUR", "hasAddressDelivery": false,
"deliveryDoor": false, "carrier": false, "deliveryDoorPrice": 0, "deliveryDoorTax": 0, "carrierPrice": 0, "carrierTax": 0,
"data": {"route": "Θεσσαλονίκη, Κέντρο Διαλογής-Φλώρινας, Φλώρινα", "company": "ΣΑΛΒΑΡΙΝΑΣ ΑΛ. ΙΩΑΝΝΗΣ",
"endPointName": "Φλώρινας, Φλώρινα", "endPointAddress": "Λεωφόρος Αβέρωφ 47, Φλώρινα\nΤηλέφωνο: 2385023995",
"companyPrices": [953], "price": 5.31, "currency": "EUR"},
"packagePrices": ["…"], "basePrice": 4.71, "serviceFee": 0}
]]
}}Nasıl okunur:
routesbir rotalar listesidir; her rota, taşıyıcı başına bir tane olmak üzere rota ayaklarından (segment, leg) oluşan bir listedir. Örnekte iki ayaklı tek bir rota vardır: Komotini → Thessaloniki aktarma merkezi → Florina.- Müşteriye gösterilen fiyat = rotanın segmentlerinin Σ
pricedeğeri; KDV ve Mydema hizmet bedeli dahildir (burada 5.26 + 5.84 = 11.10 €). İçindeki KDV Σtaxdeğeridir (2.14 €). serviceFeesveyadata.pricedeğerlerini müşteri fiyatı olarak kullanmayın; bunlar dahili rakamlardır.- Teslimat şekli son segmentten anlaşılır:
hasAddressDelivery: true= alıcının adresine teslim edilir;false= alıcı gönderiyi taşıyıcınındata.endPointName/data.endPointAddressadresindeki şubesinden teslim alır. - Gösterim için taşıyıcı adları: her segmentin
data.companydeğeri. - Rotalar sıralı değildir. Herhangi bir doğrudan (tek ayaklı) rota varsa, yalnızca doğrudan rotalar döndürülür.
- HTTP 200 ile
routes: [], bu kombinasyona hiçbir taşıyıcının hizmet vermediği anlamına gelir (bkz.scenarios).
Search-routes sepeti değiştirmez; gerektiği kadar sık çağırabilirsiniz.
#POST/api/transportation/scenarios
Boş bir sonucu açıklar.
Gövde: {"receiverDistrictId": 158, "isB2B": false, "hasAfm": false}
{"data": {"inService": true, "scenarios": [{"type": "IN_SERVICE"}, {"type": "B2C_SMS_NOTIFY"}]}}İlçeye hizmet verilmiyorsa: {"inService": false, "scenarios": [{"type": "HUB_PICKUP", "hub": {"transferLocationId": 48, "name": "…", "address": "…", "cityId": 5, "districtId": 101}}]} (hub null olabilir). Hatalar: A.
#POST/api/transportation/quote-districts — birden fazla varış noktasını tek seferde fiyatlandırma
Müşterinin bölgesini bildiğiniz ama tam ilçeyi bilmediğiniz durumlar için: aynı alışveriş sepetini tek çağrıda, sepet oluşturmadan, en fazla 15 ilçeye göre fiyatlandırır.
Gövde: create gövdesi artı "districtIds": [158, 1]. Alıcının address.district değeri sırayla her bir id ile değiştirilir.
{"data": {"districts": [
{"districtId": 158, "found": true, "name": "Φλώρινα", "nameLatin": "Florina", "cityId": 192, "cityName": "Φλώρινα",
"serviceFee": 10, "serviceFees": ["…"], "routes": [["…segments as in search-routes…"]]},
{"districtId": 999999, "found": false, "routes": [], "serviceFees": []}
]}}Müşteri birini seçtiğinde, o ilçe için create ile bir sepet oluşturun ve normal şekilde devam edin. Hatalar: B.
#7.7 Gönderiler: sipariş → voucher
#POST/api/transportation/update-basket
Sepetin paketlerini, göndericisini ve alıcısını değiştirir ve sipariş notunu ayarlar.
{"basketId": "…", "packages": ["…same as create…"], "sender": {"…": "…"}, "receiver": {"…": "…"},
"receiverComment": "Παρακαλώ τηλεφωνήστε πριν την παράδοση"}Yanıt: {"data": {"basketId": "…"}}. Hatalar: B.
packagesalanını her zaman yeniden gönderin: gönderilmezse sepet boş kalır.- Sipariş anında alıcıya müşterinin gerçek adını, telefonunu ve e-postasını yazmak için kullanın.
receiverComment, gönderide alıcının talimatları olarak görünür.
#POST/api/transportation/set-routes
Seçilen rotayı kaydeder. routes, tek bir rotanın segmentlerini içerir (rotalar listesini değil); bunlar aynı sepetin search-routes yanıtından kopyalanır:
{"basketId": "…", "routes": [
{"company": 15, "startPoint": 3, "endPoint": 698, "routeId": 78, "price": 5.26, "tax": 1.01, "serviceFee": 0,
"packagePrices": ["…as returned…"], "deliveryDoor": false, "deliveryDoorPrice": 0, "carrier": false, "carrierPrice": 0,
"hasAddressDelivery": true},
{"company": 18, "startPoint": 698, "endPoint": 179, "routeId": 385, "price": 5.84, "tax": 1.13, "serviceFee": 0,
"packagePrices": ["…"], "deliveryDoor": false, "deliveryDoorPrice": 0, "carrier": false, "carrierPrice": 0,
"hasAddressDelivery": false}
]}hasAddressDelivery dışında yukarıdaki her alan zorunludur; packagePrices boş olmamalıdır. Değerleri tam olarak search-routes endpoint'inin döndürdüğü şekilde gönderin. Yanıt: {"data": {"basketId": "…"}}. Hatalar: B.
#POST/api/transportation/payment-complete
Gönderinin nasıl ödendiğini kaydeder. Ön ödemeli cüzdan kullanan siteler için (tüm eklenti siteleri), creditAmount değerini rota toplamına ayarlayın; böylece bu tutar complete sırasında cüzdandan düşülür.
{"basketId": "…", "totalPrice": 11.10, "creditAmount": 11.10, "status": "success",
"conversationId": "order-1001", "paymentType": "CREDIT", "currency": "EUR", "bank": "MyShop", "card": "MyShop"}| Alan | Notlar |
|---|---|
totalPrice | Seçilen rotanın Σ segment price değeri |
creditAmount | Cüzdandan düşülecek tutar. totalPrice ile aynı değeri kullanın |
status | success olmalıdır, aksi halde complete sepeti reddeder |
conversationId | Sizin sipariş referansınız, en fazla 50 |
paymentType, currency, bank, card | Zorunlu, boş olmayan string'ler; CREDIT, EUR ve mağaza adınız uygundur |
Yanıt: {"data": {"basketId": "…"}}. Henüz hiçbir ücret alınmaz. Hatalar: B.
#POST/api/transportation/complete
Sepetten Mydema taşıma siparişini (voucher'ı) oluşturur. Gövde: {"basketId": "…"}.
{"data": {"transportations": {"id": 5123, "orderNumber": "MY-1042", "transferStatus": {"code": "CR", "name": "Created"},
"totalPrice": 11.1, "shipments": ["…"], "details": ["…"]}}}Tam nesne GET/api/transportation/{id} (7.8) ile aynı yapıdadır. data.transportations.id değerini saklayın. Hatalar: B.
Ne olur:
creditAmountsitenin cüzdanından düşülür. Kredi yetersizse istek başarısız olur (Insufficient credit.) ve hiçbir şey oluşturulmaz. Önce bakiyeyi kontrol edin (7.9).- Rotadaki taşıyıcılar bilgilendirilir, alıcı bir e-posta alır (alıcı e-postası gerçekse) ve gönderici adresine bir onay e-postası gider.
- Aynı sepet için tekrarlamak güvenlidir. Bir sepet gönderiye dönüştükten sonra
completeaynı gönderiyi tekrar döndürür ve hiçbir ücret almaz; o sepet içinset-routesvepayment-completeyok sayılır (kural 8.2).
Ön koşullar, sırasıyla: sepet mevcut; ödeme status: success ile kaydedilmiş (aksi halde Payment is not valid); yeterli kredi.
#7.8 Gönderiler: voucher ve takip
#GET/api/transportation/{id}/voucher-pdf?status=confirmed
Voucher'ı application/pdf olarak döndürür (Content-Disposition: attachment; filename="MY-1042.pdf"). status, confirmed veya temporary olabilir; yalnızca X-Mydema-Voucher-Status yanıt header'ını belirler. Hatalar: A.
#GET/api/transportation/{id}
{"data": {"transportationOrder": {
"id": 5123, "orderNumber": "MY-1042", "totalPrice": 11.1, "taxTotal": "2.14", "serviceFee": 0,
"transferStatus": {"code": "CR", "name": "Created"},
"senderComment": "", "deliverComment": " Παρακαλώ τηλεφωνήστε πριν την παράδοση",
"sendAddress": {"…": "…"}, "deliverAddress": {"…": "…"},
"shipments": [{
"id": 9001, "orderNumber": "MY-1042",
"route": {"id": 78, "route": "Κομοτηνή - Θεσσαλονίκη", "company": {"id": 15, "name": "…", "phone": "…"}},
"shipmentStatus": {"code": "TR", "name": "In transit"},
"actions": [{"actionDate": "2026-10-31", "shipmentStatus": {"code": "TR", "name": "In transit"}}],
"packages": ["…"]
}],
"details": ["…one per package…"],
"createdAt": "2026-10-30 10:00:00", "updatedAt": "2026-10-31 09:12:00"
}}}shipments her ayak için bir kayıt içerir; actions o ayağın geçmişidir. Hatalar: A (Company not found. sipariş id'sinin mevcut olmadığı anlamına gelir).
Sipariş transferStatus.code | Anlamı |
|---|---|
| CR | Oluşturuldu |
| PP | Ödeme bekliyor |
| PA | Ödendi |
| TR | Taşımada |
| CM | Tamamlandı |
| CL | İptal edildi |
Gönderi shipmentStatus.code | Anlamı |
|---|---|
| DO | Sipariş oluşturuldu |
| PE | Beklemede |
| RE | Taşıyıcı tarafından teslim alındı |
| TR | Yolda |
| DT | Dağıtımda |
| DE | Teslim edildi |
Webhook yoktur; bu endpoint'i yoklayın (örneğin açık gönderiler için saatte bir).
#7.9 Kredi cüzdanı
Gönderiler sitenin ön ödemeli cüzdanından ödenir. Üç endpoint'in tümü giriş yapmış sitenin cüzdanı üzerinde çalışır. Hatalar: B.
#GET/api/customer/credit/balance
{"data": {"balance": 42.5, "currency": "EUR"}}#GET/api/customer/credit/transactions?page=1&limit=50
limit 1–200, varsayılan 50.
{"data": {"transactions": [
{"id": 9, "type": "shipment", "amount": -11.1, "balanceAfter": 31.4, "currency": "EUR",
"refType": "basket", "refId": "695bca64-…", "description": "Shipment payment", "createdAt": "2026-10-30 14:02:11"}
], "total": 1, "page": 1, "limit": 50}}type: topup, shipment, refund. createdAt, saat dilimi eki olmadan sunucunun yerel saatidir.
#POST/api/customer/credit/topup + ödeme sayfası
Kredi yüklemeleri Mydema'nın kendi ödeme sayfasında kartla ödenir.
{"amount": 50}ilePOST/api/customer/credit/topup(net tutar, 1–1000 €). Yanıt:{"data": {"basketId": "…", "customerId": 273, "amount": 50, "tax": 12, "chargeTotal": 62, "currency": "EUR"}}(%24 KDV eklenir).{"redirect": "/customer/<customerId>/credit/payment/<basketId>/init"}ilePOST/api/auth/plugin-autologin→{"data": {"token": "…"}}(2 dakika geçerli).<base URL>/plugin/auto-login?t=<token>adresini satıcının tarayıcısında açın. Mydema satıcının oturumunu açar ve kartla ödeme sayfasını gösterir. Ödeme başarılı olduğunda bakiye artar.
#7.10 Opsiyonel
#POST/api/afm — Yunan vergi numarası sorgulama
Gövde: {"afmNumber": "099999999"} (9–12 karakter). Hatalar: B.
{"data": {"taxData": {"taxId": "099999999", "taxOffice": "ΦΑΕ ΑΘΗΝΩΝ", "name": "ACME ΑΕ", "address": "ΟΔΟΣ 1", "zip": "10431", "location": "ΑΘΗΝΑ"}}}Bilinmeyen bir numara aynı yapıyı boş string'lerle döndürür.
#Site ayarları
Siteniz için Mydema'da saklanan serbest anahtar/değer ayarları.
| Endpoint | Notlar |
|---|---|
GET/api/integration/{integrationId}/settings | {"data": {"settings": {"currency": {"key": "currency", "value": "EUR", "valueType": "string"}}}} (boşken []) |
GET/api/integration/{integrationId}/settings/{key} | {"data": {"setting": {…}}}; bilinmeyen anahtar → Setting not found. |
PUT/api/integration/{integrationId}/settings | {"settings": [{"key": "currency", "value": "EUR"}]} → {"data": {"written": 1, "errors": []}} |
DELETE/api/integration/{integrationId}/settings/{key} | {"data": {"deleted": true}} |
İzin verilen anahtarlar: free_shipping_threshold, hide_shipping_cost, currency, language, default_pickup_supplier_id, default_pickup_branch_id, buffer_ratio, risk_premium_ratio, auto_voucher_email_to, voucher_email_subject, pickup_warning_html, emergency_mode_enabled, fallback_template_under_35kg, fallback_template_over_35kg. Diğer anahtarlar errors içinde bildirilir.
#Voucher ödemeleri (haftalık faturalar)
Mydema'nın sitenin voucher'ları için haftalık partiler halinde düzenlediği, banka havalesiyle ödenen faturalar. Yalnızca Mydema sitenizi bu şekilde faturalandırıyorsa geçerlidir. İki GET dışında hepsi POST'tur.
| Endpoint | Gövde | Yanıt |
|---|---|---|
POST/api/voucher-payment/summary | {} | {"data": {"count": 0, "totalAmount": 0, "oldestDueDate": null}} |
POST/api/voucher-payment/list | {"status": "unpaid", "page": 1, "perPage": 50} (ayrıca weekBatchId, fromDate, toDate) | {"data": {"payments": [], "page": 1, "perPage": 50}} |
GET/api/voucher-payment/{id} | — | {"data": {"payment": {…}}} |
GET/api/voucher-payment/{id}/invoice-pdf | — | application/pdf |
#8. Entegrasyon kuralları ve tuzaklar
8.1 İş kuralı hataları HTTP 500'dür. Durum kodu yerine gövdeyi okuyun (bölüm 4.3) ve 500 yanıtlarını otomatik olarak tekrar denemeyin. Yalnızca 503 ve ağ hataları tekrar denemeye değer.
8.2 complete her sepet için idempotent'tir. Bir sepet en fazla bir gönderiye dönüşür: aynı sepet için complete tekrar çağrıldığında mevcut gönderi (aynı id) döner ve cüzdandan tekrar ücret alınmaz; bu yüzden zaman aşımından sonra endpoint'i yeniden çağırmanız yeterlidir. İlk çağrı hâlâ sürerken ikinci çağrı This basket is already being completed. Please try again in a moment. hatasıyla başarısız olur — birkaç saniye sonra tekrar deneyin. complete birkaç saniye sürebilir (yanıt vermeden önce e-postaları gönderir); en az 30 sn'lik bir zaman aşımı kullanın. Yine de dönen id'yi siparişinizle ilişkilendirerek saklayın: birden fazla depodan mal içeren bir siparişin her depo için bir sepeti ve bir gönderisi olur.
8.3 Müşterinin gördüğü rotayı sipariş anında yeniden fiyatlandırın. Sipariş anında search-routes çağırın, aynı routeId değerlerine sahip rotayı seçin ve tam olarak bu segmentleri set-routes endpoint'ine gönderin. Fiyatlar teklif ile sipariş arasında değişebilir.
8.4 complete öncesinde cüzdanı kontrol edin. GET/api/customer/credit/balance ≥ rota toplamı olmalıdır; aksi halde satıcıdan kredi yüklemesini isteyin (7.9) ve daha sonra tekrar deneyin.
8.5 Depo başına bir sepet. Farklı ilçelerden çıkan mallar farklı gönderilerdir. Her gönderici için ayrı bir sepet oluşturun, fiyatlandırın ve tamamlayın.
8.6 Adlar değil, ilçeler. Her fiyat teklifi iki taraf için de bir ilçe id'si gerektirir. create çağırmadan önce müşterinin adresini hizmet verilen bir ilçeye çözümleyin (6.1); adresi yalnızca bir ile kadar daraltabiliyorsanız quote-districts kullanın ve seçimi müşteriye bırakın.
8.7 Koli paketleri: shipmentType / shipmentSize göndermeyin. Bilinmeyen bir kod tüm isteği başarısız kılar (6.2).
8.8 update-basket isteğinde paketleri yeniden gönderin. Gönderilmezlerse sepet boşalır.
8.9 Sipariş anında alıcının gerçek iletişim bilgileri. Mydema alıcıya e-posta gönderir ve kurye alıcıyı arar; teklif aşamasındaki yer tutucular complete öncesinde update-basket ile değiştirilmelidir.
8.10 Token'ı saklayın. Bir kez giriş yapın, token'ı yeniden kullanın ve yalnızca 401 aldıktan sonra tekrar giriş yapın (3.3). Her istekte giriş yapmak gereksizdir.