MydemaPlugin API
app.mydema.gr · stage.mydema.gr

Mydema Plugin API — Οδηγός ενσωμάτωσης

Ο οδηγός αυτός απευθύνεται σε developers που συνδέουν ένα e-shop ή ERP με το Mydema με τον ίδιο τρόπο που το κάνουν τα επίσημα plugins για OpenCart / WooCommerce: εμφάνιση τιμών αποστολής σε πραγματικό χρόνο στο ταμείο, μετατροπή των πληρωμένων παραγγελιών σε αποστολές Mydema (vouchers) και παρακολούθησή τους.

Σε αυτόν τον φάκελο υπάρχει μια έτοιμη προς χρήση συλλογή Postman:

ΑρχείοΤι είναι
mydema-plugin-api.postman_collection.jsonΌλα τα αιτήματα αυτού του οδηγού, με τη σειρά κλήσης, με scripts που μεταφέρουν το token, το αναγνωριστικό καλαθιού (basket id) και την επιλεγμένη διαδρομή (route) από το ένα αίτημα στο επόμενο
mydema-staging.postman_environment.jsonΒασικό URL του staging και placeholders για το κλειδί σας
mydema-production.postman_environment.jsonΒασικό URL της παραγωγής και placeholders για το κλειδί σας

#1. Περιβάλλοντα

ΠεριβάλλονΒασικό URL
Παραγωγή (production)https://app.mydema.gr
Staginghttps://stage.mydema.gr

Κάθε path σε αυτόν τον οδηγό είναι σχετική ως προς το βασικό URL. Όταν παίρνετε κλειδί μέσω της ροής αυτοεξυπηρέτησης (ενότητα 2.1), η απάντηση του claim σας δίνει το βασικό URL που πρέπει να χρησιμοποιήσετε (apiEndpoint).

Το staging έχει δικά του δεδομένα και δικά του κλειδιά· ένα κλειδί παραγωγής δεν λειτουργεί στο staging.

#2. Απόκτηση κλειδιού API

Ένα κλειδί API (API key, mk_live_…, 48 χαρακτήρες) ανήκει σε έναν ιστότοπο (site — μια «integration» στο Mydema). Κάθε ιστότοπος έχει:

  • έναν χρήστη υπηρεσίας (service user), ως τον οποίο ενεργεί το API,
  • ένα πορτοφόλι προπληρωμένης πίστωσης (credit wallet) που πληρώνει τις αποστολές του (ενότητα 7.9),
  • προαιρετικά ένα domain και μια λίστα επιτρεπόμενων IP (IP allowlist), με τα οποία ελέγχεται κάθε σύνδεση.

Υπάρχουν δύο τρόποι για να πάρετε κλειδί.

#2.1 Σύνδεση αυτοεξυπηρέτησης (προτείνεται για plugins)

Το κατάστημα ζητά σύνδεση, το προσωπικό του Mydema την εγκρίνει στο panel και το κατάστημα παραλαμβάνει το κλειδί του. Αυτά τα τρία endpoints είναι δημόσια (χωρίς token).

  1. Δημιουργήστε ένα τυχαίο μυστικό (για παράδειγμα 32 τυχαία bytes σε hex) και κρατήστε το στη μεριά σας.
  2. POST/api/plugin/site-request με τα στοιχεία του καταστήματος και claimSecretHash = το SHA-256 σε hex αυτού του μυστικού. Παίρνετε ένα requestId.
  3. Κάνετε polling στο POST/api/plugin/site-request/status με requestId + το αυτούσιο claimSecret μέχρι το status να γίνει approved (ή rejected, με rejectReason). Αρκεί polling κάθε λίγα λεπτά· η έγκριση γίνεται χειροκίνητα.
  4. POST/api/plugin/site-request/claim με τις ίδιες δύο τιμές. Παίρνετε apiKey, apiEndpoint και integrationId. Κάθε claim εκδίδει νέο κλειδί και ακυρώνει το προηγούμενο, οπότε κάντε claim μία φορά και αποθηκεύστε το κλειδί.

#2.2 Κλειδί που εκδίδεται από το Mydema

Το προσωπικό του Mydema μπορεί να δημιουργήσει τον ιστότοπο στο panel και να σας στείλει απευθείας το κλειδί. Λειτουργεί ακριβώς όπως ένα κλειδί που αποκτήθηκε με claim.

Αποθηκεύστε το κλειδί όπως έναν κωδικό πρόσβασης. Το Mydema κρατά μόνο το hash του και δεν μπορεί να το εμφανίσει ξανά· αν χαθεί το κλειδί, χρειάζεται νέο claim ή νέο κλειδί από το Mydema.

#3. Αυθεντικοποίηση

#3.1 Σύνδεση

HTTP
POST /api/auth/plugin-login
Content-Type: application/json

{"apiKey": "mk_live_…", "apiSecret": "", "domain": "myshop.gr"}
ΠεδίοΥποχρεωτικόΣημειώσεις
apiKeyναιΤο κλειδί mk_live_…
apiSecretναι, ως ""Πρέπει να είναι κενή συμβολοσειρά για κλειδιά mk_live_. (Μια μη κενή τιμή ενεργοποιεί ένα παλαιότερο σχήμα κλειδιού + μυστικού.)
domainαν ο ιστότοπος έχει domainΤο host name του καταστήματός σας. Το scheme, το path, η θύρα και ένα αρχικό www. αγνοούνται και από τις δύο πλευρές.

Αν ο ιστότοπος έχει λίστα επιτρεπόμενων IP, το αίτημα πρέπει να προέρχεται από μία από τις καταχωρισμένες διευθύνσεις (ακριβής αντιστοίχιση, χωρίς εύρη CIDR). Το μήνυμα απόρριψης αναφέρει τη διεύθυνση που είδε το Mydema.

Απάντηση (συντομευμένη):

JSON
{
  "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 και pluginCustomerId (ο κάτοχος του πορτοφολιού)· ορισμένα endpoints τα χρειάζονται στο path.

#3.2 Χρήση του token

Στείλτε το σε κάθε άλλο αίτημα:

HTTP
Authorization: Bearer <accessToken>

Ακριβώς Bearer + ένα κενό· ο έλεγχος κάνει διάκριση πεζών-κεφαλαίων.

#3.3 Διάρκεια ισχύος και ανανέωση

  • Το token ισχύει για 12 ώρες, κυλιόμενα: κάθε επιτυχημένο αίτημα μεταθέτει τη λήξη σε 12 ώρες από εκείνη τη στιγμή. Ένα κατάστημα με τακτική κίνηση κρατά το ίδιο token επ' αόριστον.
  • Νέα σύνδεση ενώ το token είναι ακόμη έγκυρο επιστρέφει το ίδιο token, οπότε πολλοί workers του ίδιου καταστήματος μπορούν να το μοιράζονται με ασφάλεια.
  • Όταν το token δεν είναι πλέον έγκυρο, οποιοδήποτε endpoint επιστρέφει HTTP 401 με αυτό το σώμα (προσοχή: χωρίς περίβλημα data / error):

    JSON
    {"message": "User timed out.", "code": "TOKEN_EXPIRED"}
    codeΣημασία
    MISSING_TOKENΛείπει ή είναι κακοσχηματισμένη η κεφαλίδα Authorization, ή άγνωστο token
    TOKEN_EXPIREDΠάνω από 12 ώρες χωρίς αίτημα
    TOKEN_CHANGEDΕκδόθηκε νεότερο token για αυτόν τον ιστότοπο

    Σε οποιαδήποτε από αυτές τις περιπτώσεις: συνδεθείτε ξανά και επαναλάβετε το αίτημα μία φορά.

Οι αποτυχίες σύνδεσης επιστρέφουν HTTP 403 με τον τυπικό φάκελο σφάλματος (ενότητα 4.3), για παράδειγμα Invalid credentials., Domain not allowed for this key., IP not allowed for this key. Detected IP: ….

#4. Συμβάσεις: αιτήματα, απαντήσεις, σφάλματα

#4.1 Αιτήματα

  • Σώματα JSON, UTF-8, Content-Type: application/json. Στείλτε Accept: application/json.
  • Τα endpoints λίστας και αναζήτησης είναι POST, ακόμη κι όταν μόνο διαβάζουν. Αν τα καλέσετε με GET είτε επιστρέφουν 405 είτε πέφτουν σε άλλο route του server και αποτυγχάνουν.
  • Τα χρηματικά ποσά είναι σε EUR. Τα βάρη σε kg. Οι διαστάσεις των δεμάτων είναι σε cm στα αιτήματα (το Mydema τις αποθηκεύει και τις επιστρέφει σε μέτρα).

#4.2 Επιτυχημένες απαντήσεις

HTTP 200 και ένα αντικείμενο data:

JSON
{"data": {"basketId": "695bca64-c645-4398-8b67-c16adcc89de4"}}

#4.3 Απαντήσεις σφάλματος

Το Mydema χρησιμοποιεί περισσότερες από μία μορφές σφάλματος. Χειριστείτε όλες.

A. Τυπικός φάκελος (τα περισσότερα endpoints):

JSON
{"error": {"message": "{\"message\":\"District is not valid.\",\"code\":400}", "code": 500, "file": null, "line": null}}

Το error.message είναι συνήθως το ίδιο μια συμβολοσειρά JSON. Αποκωδικοποιήστε το δεύτερη φορά για να πάρετε το αναγνώσιμο message και τον πραγματικό code (400 λανθασμένα δεδομένα, 403 δεν σας ανήκει, 404 δεν βρέθηκε, 409 σύγκρουση). Αν δεν είναι έγκυρο JSON, χρησιμοποιήστε το ως απλό κείμενο.

B. Σφάλμα framework (ορισμένα endpoints, σημειωμένα με «B» στην ενότητα 7):

JSON
{"type": "https://tools.ietf.org/html/rfc2616#section-10", "title": "An error occurred", "status": 500, "detail": "Internal Server Error"}

Στην παραγωγή το detail είναι γενικό, οπότε δεν θα δείτε την αιτία. Επικυρώστε τα δεδομένα σας πριν τα στείλετε (ενότητα 6) και καταγράψτε το αίτημα που στείλατε.

C. Σφάλματα token: HTTP 401, {"message": …, "code": "TOKEN_EXPIRED"} (ενότητα 3.3).

D. Endpoints σύνδεσης (/api/plugin/site-request*) χρησιμοποιούν καθαρό φάκελο με την πραγματική κατάσταση HTTP: {"error": {"message": "Request is not approved.", "code": 409}}.

E. Προσωρινό κλείδωμα βάσης δεδομένων: HTTP 503 με κεφαλίδα Retry-After και {"error": {"message": "…temporary database lock…", "code": "DB_RETRY"}}. Περιμένετε και δοκιμάστε ξανά.

#4.4 Κωδικοί κατάστασης HTTP

ΚατάστασηΣημασίαΕπανάληψη;
200Επιτυχία—
401Πρόβλημα token (ενότητα 3.3)Νέα σύνδεση, μετά μία επανάληψη
403Η σύνδεση απορρίφθηκεΌχι· διορθώστε κλειδί / domain / IP
404, 405Λάθος path ή λάθος μέθοδος HTTPΌχι
500Επιχειρησιακό σφάλμα ή σφάλμα επικύρωσης (λανθασμένα δεδομένα, δεν βρέθηκε, ανεπαρκής πίστωση…) — η πραγματική αιτία βρίσκεται στο σώμαΌχι: το ίδιο αίτημα θα αποτύχει ξανά
503Προσωρινό κλείδωμα βάσης δεδομένωνΝαι, μετά το Retry-After
Σφάλμα δικτύου / timeout—Αναγνώσεις και create: ναι. complete: ναι, είναι ασφαλές ανά καλάθι (κανόνας 8.2)

Τα περισσότερα σφάλματα επικύρωσης και επιχειρησιακά σφάλματα επιστρέφονται ως HTTP 500, όχι 4xx. Μην αντιμετωπίζετε το 500 ως διακοπή λειτουργίας του server και μην το επαναλαμβάνετε στα τυφλά.

#4.5 Μορφή αιτήματος λίστας

Τα endpoints που περιγράφονται ως «ListRequest» δέχονται αυτό το προαιρετικό σώμα:

ΚλειδίΤύποςΣημειώσεις
paramsobjectΦίλτρα ειδικά για το endpoint, π.χ. {"state": 65}
limitintΜέγεθος σελίδας. Στις περισσότερες λίστες δεν υπάρχει προεπιλογή: αν το παραλείψετε, παίρνετε τα πάντα
offsetintΓραμμές προς παράλειψη
orderByobjectπ.χ. {"name": "ASC"}

Το {} είναι έγκυρο σώμα. Οι αριθμοί πρέπει να είναι αριθμοί, όχι συμβολοσειρές.

Τα αποτελέσματα έχουν τη μορφή {"data": {"<items>": [...], "meta": {"total": 17}}}.

#5. Η ροή ενσωμάτωσης με μια ματιά

 εφάπαξ ρύθμιση            ταμείο (ανά καλάθι αγορών)               παραγγελία (ανά πληρωμένη παραγγελία)
 ──────────────            ──────────────────────────               ─────────────────────────────────────
 plugin-login              δημιουργία καλαθιού ──► search-routes    update-basket (πραγματικά στοιχεία, σχόλιο)
 shipment/templates        (ένα καλάθι ανά αποθήκη αποστολής)       search-routes (εκ νέου)
 suppliers (αποθήκες)      εμφάνιση Σ segment.price ανά διαδρομή    set-routes (επιλεγμένη διαδρομή)
 αναζητήσεις τοποθεσιών    χωρίς διαδρομή; ──► scenarios            έλεγχος υπολοίπου πίστωσης
                                                                    payment-complete (creditAmount)
                                                                    complete  ──► id μεταφοράς
                                                                    voucher-pdf, παρακολούθηση
  1. Ρύθμιση. Συνδεθείτε. Φορτώστε τα πρότυπα δεμάτων. Καταχωρίστε κάθε αποθήκη από την οποία αποστέλλονται τα εμπορεύματα ως αποθήκη / σημείο παραλαβής (supplier) (ή χρησιμοποιήστε μία περιοχή αφετηρίας για όλα). Αντιστοιχίστε τις διευθύνσεις των πελατών σας σε αναγνωριστικά περιοχών (district ids) του Mydema (ενότητα 6.1).
  2. Προσφορά στο ταμείο. Για κάθε αποθήκη αφετηρίας στο καλάθι αγορών, δημιουργήστε ένα καλάθι (basket) με τα δέματα, τον αποστολέα (αποθήκη) και τον παραλήπτη, και μετά αναζητήστε διαδρομές. Κάθε διαδρομή είναι μία επιλογή αποστολής· η τιμή της είναι το άθροισμα του price των τμημάτων της (segments) (ενότητα 7.6). Κρατήστε το basketId και την ταυτότητα της διαδρομής (routeId των τμημάτων) μαζί με την επιλογή του πελάτη.
  3. Παραγγελία → αποστολή. Όταν η παραγγελία πληρωθεί, ενημερώστε το καλάθι με τα πραγματικά στοιχεία επικοινωνίας και το σχόλιο του πελάτη, αναζητήστε ξανά διαδρομές (οι τιμές είναι επίκαιρες εκείνη τη στιγμή), στείλτε την επιλεγμένη διαδρομή με set-routes, καταγράψτε την πληρωμή με payment-complete και μετά καλέστε το complete μία φορά. Παίρνετε το αναγνωριστικό της εντολής μεταφοράς του Mydema.
  4. Μετά. Κατεβάστε το PDF του voucher και κάνετε polling στο endpoint παρακολούθησης για την κατάσταση.

Ένα καλάθι αγορών με εμπορεύματα από πολλές αποθήκες αντιστοιχεί σε πολλά καλάθια: ένα ανά αποθήκη (αποστολέα), καθένα τιμολογείται και ολοκληρώνεται χωριστά. Τα επίσημα plugins εμφανίζουν το άθροισμά τους ως μία τιμή αποστολής.

#6. Δεδομένα αναφοράς: τοποθεσίες, δέματα, πρόσωπα

#6.1 Τοποθεσίες

Το δέντρο τοποθεσιών του Mydema είναι χώρα (country) → νομός (state) → δήμος (city) → περιοχή (district). Στην παραγωγή, ένα state είναι ελληνικός νομός (prefecture) και ένα city είναι δήμος (municipality).

Τα endpoints αποστολών χρειάζονται το αναγνωριστικό περιοχής του αποστολέα και του παραλήπτη. Οι διαδρομές συνδέουν περιοχές μεταξύ τους, και μόνο οι εξυπηρετούμενες περιοχές έχουν διαδρομές.

ΈχετεΧρησιμοποιήστε
Ένα τοπωνύμιο που πληκτρολόγησε ο πελάτηςPOST/api/location/district/avail-autocomplate → locations[].id (μόνο εξυπηρετούμενες περιοχές)
Έναν νομό, θέλετε τις εξυπηρετούμενες περιοχές τουPOST/api/location/district/avail-list με params.state → locations[].districtId
Έναν ταχυδρομικό κώδικαPOST/api/autocomplate/location/postal-code → ονόματα τοποθεσιών (όχι αναγνωριστικά)· μετά αντιστοιχίστε το όνομα με τις δύο παραπάνω κλήσεις
Όνομα νομού → αναγνωριστικό statePOST/api/location/state/list

Η πλήρης λίστα εξυπηρετούμενων περιοχών (avail-list χωρίς φίλτρα) είναι αρκετά μικρή ώστε να αποθηκεύεται στην cache για μερικές ώρες.

#6.2 Δέματα

Φορτώνετε τον κατάλογο δεμάτων μία φορά την ημέρα από το GET/api/shipment/templates. Τιμές κατά τη στιγμή της σύνταξης (Οκτώβριος 2026):

ΚωδικόςΤύποςΕύρος όγκου (m³)Χρέωση ως (m³)Βάρος (kg)Αποτύπωμα (cm)
SM3 (κουτί)0 – 0.200.200 – 80—
MM3 (κουτί)0.2001 – 0.500.500 – 80—
LM3 (κουτί)0.5001 – 1.001.000 – 80—
XLM3 (κουτί)1.0001 – 1.601.600 – 80—
PEUR1PL (παλέτα)—1.9280 – 100080 × 120, ύψος 200
PEUR2PL (παλέτα)—2.4080 – 1000100 × 120, ύψος 200
PMNI6PL (παλέτα)—0.4880 – 30060 × 80, ύψος 100

Το Mydema μπορεί να αλλάξει αυτές τις τιμές· διαβάζετέ τες από το API αντί να τις ενσωματώνετε στον κώδικα.

Αντικείμενο δέματος (χρησιμοποιείται από τα create, update-basket, quote-districts):

ΠεδίοΤύποςΥποχρεωτικόΣημειώσεις
weightTypestringναιM3 = κουτί / δέμα, PL = παλέτα, KG = ειδικά αντικείμενα
weightnumberναιkg, ένα δέμα
width, height, depthnumberναιcm
isSensitiveboolόχιΕύθραυστα εμπορεύματα· προεπιλογή false
shipmentType, shipmentSizestringβλ. παρακάτωΚωδικοί προτύπου / ειδικού αντικειμένου
productWidth, productHeight, productDepthnumberόχιΟι διαστάσεις του ίδιου του προϊόντος σε cm. Τυπώνονται μόνο στο voucher· δεν χρησιμοποιούνται για την τιμολόγηση

Πώς προκύπτει η τιμή:

  • M3 (κουτί): με βάση τον όγκο (width × height × depth, m³) και το βάρος. Ένα δέμα μπορεί να ζυγίζει έως 220 kg. Στείλτε ένα δέμα ανά φυσικό δέμα. Παραλείψτε τα shipmentType και shipmentSize — ένας άγνωστος κωδικός κάνει όλο το αίτημα να αποτύχει με Size is not valid / Type is not valid.
  • PL (παλέτα): με βάση το αποτύπωμα: τα width και depth πρέπει να αντιστοιχούν σε ένα από τα παραπάνω αποτυπώματα παλέτας (σε οποιονδήποτε προσανατολισμό, π.χ. 80 και 120 για PEUR1). Το height είναι το ύψος του φορτίου· δεν επηρεάζει την τιμή. Παραλείψτε τα shipmentType / shipmentSize.
  • KG (ειδικά αντικείμενα): τα shipmentType και shipmentSize είναι και τα δύο υποχρεωτικά και πρέπει να είναι κωδικοί που σας έδωσε το Mydema για το συγκεκριμένο αντικείμενο.

Πολλά πανομοιότυπα δέματα = πολλά αντικείμενα δέματος. Ένα δέμα με weight 0 και χωρίς shipmentType αγνοείται σιωπηρά.

#6.3 Αποστολέας και παραλήπτης

JSON
{
  "firstName": "Μαρία",
  "lastName": "Παπαδοπούλου",
  "email": "[email protected]",
  "phone": "6900000000",
  "customerType": "I",
  "taxId": "",
  "address": {
    "address": "Οδός Παραλήπτη 5",
    "postalCode": "53100",
    "district": 158,
    "city": null,
    "officeName": "",
    "taxOffice": "",
    "location": ""
  }
}
ΠεδίοΣημειώσεις
customerTypeI ιδιώτης, S επιχείρηση
taxIdΑΦΜ για επιχειρήσεις, έως 20 χαρακτήρες
address.districtΥποχρεωτικό. Αναγνωριστικό περιοχής (ενότητα 6.1). Αν λείπει ή είναι άγνωστη η περιοχή, το αίτημα αποτυγχάνει
address.cityΑναγνωριστικό δήμου, ή null ώστε να προκύψει από την περιοχή (προτείνεται)
email, phoneΧρησιμοποιήστε πραγματικές τιμές όταν ολοκληρώνετε μια παραγγελία: το Mydema στέλνει e-mail στον παραλήπτη όταν δημιουργείται η αποστολή και ο courier βλέπει τον αριθμό τηλεφώνου. Κατά την προσφορά αρκούν placeholders

Ο αποστολέας είναι η αποθήκη σας. Χρησιμοποιήστε customerType S και την περιοχή της αποθήκης.

#7. Αναφορά endpoints

Όλα τα endpoints εκτός από αυτά της 7.1 χρειάζονται Authorization: Bearer <token>. Το «Σφάλματα: A/B» παραπέμπει στις μορφές σφαλμάτων της ενότητας 4.3.

#7.1 Σύνδεση (δημόσια)

#POST/api/plugin/site-request

ΠεδίοΥποχρεωτικόΣημειώσεις
domainναιHost name του καταστήματος
companyNameναιέως 191
contactNameναιέως 100
contactEmailναιΕδώ αποστέλλεται το e-mail έγκρισης
claimSecretHashναιSHA-256 σε hex (64 χαρακτήρες) του μυστικού σας
platformόχιopencart3, opencart4, woocommerce, magento2· άλλες τιμές αποθηκεύονται ως κενές
siteName, pluginVersion, taxId, taxOffice, address, postalCode, city, contactPhoneόχιΣτοιχεία τιμολόγησης / επικοινωνίας
JSON
{"data": {"requestId": "9b2f8f8e-3c1a-4f7e-9d1e-1a2b3c4d5e6f", "status": "pending"}}

Αν στείλετε ξανά το ίδιο domain και μυστικό, επιστρέφεται το ίδιο αίτημα. Σφάλματα (μορφή D): 400 μη έγκυρο πεδίο, 409 υπάρχει άλλο ανοιχτό αίτημα για αυτό το domain, 429 περισσότερα από 5 αιτήματα ανά ώρα από μία IP.

#POST/api/plugin/site-request/status

Σώμα: {"requestId": "…", "claimSecret": "<raw secret>"}

JSON
{"data": {"status": "approved", "siteName": "My Shop", "domain": "myshop.gr", "rejectReason": null}}

status: pending, approved, rejected, claimed. Άγνωστο αναγνωριστικό ή λάθος μυστικό → 404.

#POST/api/plugin/site-request/claim

Σώμα: {"requestId": "…", "claimSecret": "<raw secret>"}

JSON
{"data": {"apiKey": "mk_live_…", "apiEndpoint": "https://app.mydema.gr", "integrationId": 57}}

Κάθε κλήση ανανεώνει (rotates) το κλειδί. Σφάλματα: 404, 409 δεν έχει εγκριθεί, 410 ο ιστότοπος δεν είναι πλέον ενεργός.

#7.2 Αυθεντικοποίηση

EndpointΣημειώσεις
POST/api/auth/plugin-loginΒλ. ενότητα 3.1
POST/api/auth/plugin-autologinΑνοίγει μια σελίδα του panel του Mydema (πληρωμή αναπλήρωσης πίστωσης) στον browser του εμπόρου χωρίς κωδικό πρόσβασης. Βλ. ενότητα 7.9

#7.3 Τοποθεσίες

Όλα είναι POST. Σφάλματα: B.

#POST/api/location/district/avail-autocomplate

Εξυπηρετούμενες περιοχές που ταιριάζουν με ένα όνομα (ελληνικά ή λατινικά, οι τόνοι προαιρετικοί· αν δεν βρεθεί κάτι, γίνεται ασαφής αντιστοίχιση).

Σώμα: {"term": "Κομοτην", "limit": 10} (limit προεπιλογή 100)

JSON
{"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 όντως ξεκινά με κεφαλαίο C.)

#POST/api/location/district/avail-list — ListRequest

Όλες οι εξυπηρετούμενες περιοχές, σε επίπεδη λίστα. Φίλτρα: params.state (αναγνωριστικό νομού), params.city (αναγνωριστικό δήμου). Χωρίς limit τις παίρνετε όλες.

Σώμα: {"params": {"state": 65}}

JSON
{"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· το locationDistricts διατηρείται για παλαιότερους clients.

#POST/api/autocomplate/location/postal-code

(Η ορθογραφία autocomplate είναι μέρος του path.) Σώμα: {"term": "69131", "limit": 50} (αντιστοίχιση προθέματος· limit προεπιλογή 10).

JSON
{"data": {"locationPostalCodes": [
  {"id": 12752, "code": "69131", "district": "ΑΓΙΑ ΒΑΡΒΑΡΑ", "city": "ΡΟΔΟΠΗΣ"},
  {"id": 12754, "code": "69131", "district": "ΚΟΜΟΤΗΝΗ", "city": "ΡΟΔΟΠΗΣ"}
]}}

Το district είναι το όνομα του οικισμού και το city το όνομα του νομού — κείμενο, όχι αναγνωριστικά. Ένας ταχυδρομικός κώδικας μπορεί να καλύπτει πολλούς οικισμούς.

#POST/api/location/state/list — ListRequest

Σώμα: {"orderBy": {"name": "ASC"}}

JSON
{"data": {"locationStates": [{"id": 65, "name": "Ροδόπης", "nameLatin": null, "originalName": "Ροδόπης", "country": {"code": "GR"}}], "meta": {"total": 59}}}

#POST/api/location/city/avail-list — ListRequest

Όλοι οι δήμοι ενός νομού. Το params.state είναι υποχρεωτικό (χωρίς αυτό η λίστα είναι κενή).

Σώμα: {"params": {"state": 65}} → {"data": {"locationCities": [{"id": 542, "name": "Αίγειρος", "state": {"id": 65, "name": "Ροδόπης"}}], "meta": {"total": 14}}}

#7.4 Πρότυπα δεμάτων

#GET/api/shipment/templates

JSON
{"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}
]}}

Δείτε την ενότητα 6.2 για τον τρόπο χρήσης τους. Σφάλματα: A.

#7.5 Αποθήκες / σημεία παραλαβής (suppliers)

Μια αποθήκη / σημείο παραλαβής (supplier) είναι ένα σημείο από το οποίο παραλαμβάνονται τα εμπορεύματά σας. Η περιοχή της είναι η περιοχή του αποστολέα στα καλάθια για αυτά τα εμπορεύματα. Κάθε ιστότοπος βλέπει και επεξεργάζεται μόνο τις δικές του αποθήκες. Σφάλματα: A (προβλήματα δικαιωμάτων: B).

Αντικείμενο αποθήκης (supplier)

JSON
{
  "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"
}

Εγγράψιμα πεδία: name (υποχρεωτικό, έως 200), pickupAddress (υποχρεωτικό, έως 500), districtId (ο δήμος προκύπτει από αυτό), postalCode, latitude, longitude, googlePlaceId, contactName, contactPhone, contactEmail, pickupHoursStart, pickupHoursEnd ("09:00"), notes, externalCode (δικό σας αναγνωριστικό, μοναδικό ανά ιστότοπο).

EndpointΣώμαΑπάντηση
POST/api/supplierπεδία αποθήκης{"data": {"supplier": {…}}}
GET/api/supplier/{id}—{"data": {"supplier": {…}}}
PUT/api/supplier/{id}μόνο τα πεδία προς αλλαγή{"data": {"supplier": {…}}}
DELETE/api/supplier/{id}—{"data": {"deleted": true}}
POST/api/supplier/list{}{"data": {"suppliers": […], "total": 1}}
GET/api/integration/{integrationId}/supplier—Ίδιο με τη λίστα
POST/api/supplier/sync{"integrationId": 8, "rows": [{…, "externalCode": "WH-1", "cityId": 67}]}{"data": {"created": 1, "updated": 0, "errors": []}}

Σημειώσεις:

  • Στο PUT, μη στέλνετε integrationId (ή στείλτε 0, που σημαίνει «διατήρηση»). Αν στείλετε "districtId": null η περιοχή διαγράφεται.
  • Το sync κάνει upsert με βάση το externalCode (έως 200 γραμμές ανά κλήση) και χρειάζεται cityId σε κάθε γραμμή. Τα σφάλματα γραμμών αναφέρονται στο errors με το index της γραμμής· οι υπόλοιπες αποθηκεύονται.
  • Το externalCode μιας διαγραμμένης αποθήκης δεν μπορεί να ξαναχρησιμοποιηθεί.
  • Συνήθη σφάλματα: District is not valid., Please select a district., Supplier not found., Supplier with this externalCode already exists for this integration. (409).

#7.6 Αποστολές: προσφορά

#POST/api/transportation/create — δημιουργία καλαθιού

JSON
{
  "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}}
}
JSON
{"data": {"basketId": "695bca64-c645-4398-8b67-c16adcc89de4"}}

Ένα καλάθι είναι ένα προσωρινό προσχέδιο προσφοράς/παραγγελίας. Τίποτα δεν έχει κρατηθεί ακόμη. Σφάλματα: B. Συνήθεις αιτίες: άγνωστο district, Size is not valid / Type is not valid (βλ. 6.2), λείπει το sender ή το receiver. Στείλτε το σχόλιο της παραγγελίας αργότερα με update-basket (το πεδίο comments εδώ περιορίζεται σε 10 χαρακτήρες).

#POST/api/transportation/search-routes

Σώμα: {"basketId": "…"}

JSON
{"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}
  ]]
}}

Πώς να το διαβάσετε:

  • Το routes είναι μια λίστα διαδρομών· κάθε διαδρομή είναι μια λίστα τμημάτων (σκελών), ένα ανά μεταφορέα. Το παράδειγμα έχει μία διαδρομή με δύο σκέλη: Κομοτηνή → κέντρο διαλογής Θεσσαλονίκης → Φλώρινα.
  • Τιμή που εμφανίζεται στον πελάτη = Σ price των τμημάτων της διαδρομής, με ΦΠΑ και την προμήθεια υπηρεσίας του Mydema να περιλαμβάνονται (εδώ 5.26 + 5.84 = 11.10 €). Ο ΦΠΑ που περιέχεται είναι Σ tax (2.14 €).
  • Μη χρησιμοποιείτε τα serviceFees ή data.price ως τιμή πελάτη· είναι εσωτερικά μεγέθη.
  • Ο τρόπος παράδοσης προκύπτει από το τελευταίο τμήμα: hasAddressDelivery: true = παράδοση στη διεύθυνση του παραλήπτη· false = ο παραλήπτης παραλαμβάνει από το κατάστημα του μεταφορέα στο data.endPointName / data.endPointAddress.
  • Ονόματα μεταφορέων για εμφάνιση: το data.company κάθε τμήματος.
  • Οι διαδρομές δεν είναι ταξινομημένες. Αν υπάρχει έστω και μία απευθείας διαδρομή (ενός σκέλους), επιστρέφονται μόνο απευθείας διαδρομές.
  • routes: [] με HTTP 200 σημαίνει ότι κανένας μεταφορέας δεν εξυπηρετεί αυτόν τον συνδυασμό (βλ. scenarios).

Το search-routes δεν αλλάζει το καλάθι· καλέστε το όσο συχνά χρειάζεστε.

#POST/api/transportation/scenarios

Εξηγεί ένα κενό αποτέλεσμα.

Σώμα: {"receiverDistrictId": 158, "isB2B": false, "hasAfm": false}

JSON
{"data": {"inService": true, "scenarios": [{"type": "IN_SERVICE"}, {"type": "B2C_SMS_NOTIFY"}]}}

Όταν η περιοχή δεν εξυπηρετείται: {"inService": false, "scenarios": [{"type": "HUB_PICKUP", "hub": {"transferLocationId": 48, "name": "…", "address": "…", "cityId": 5, "districtId": 101}}]} (το hub μπορεί να είναι null). Σφάλματα: A.

#POST/api/transportation/quote-districts — τιμολόγηση πολλών προορισμών με μία κλήση

Για όταν γνωρίζετε την ευρύτερη περιοχή του πελάτη αλλά όχι την ακριβή περιοχή (district): τιμολογεί το ίδιο καλάθι αγορών για έως 15 περιοχές σε μία κλήση, χωρίς να δημιουργεί καλάθι.

Σώμα: το σώμα του create συν "districtIds": [158, 1]. Το address.district του παραλήπτη αντικαθίσταται διαδοχικά από κάθε αναγνωριστικό.

JSON
{"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": []}
]}}

Όταν ο πελάτης επιλέξει μία, κάντε create ένα καλάθι για αυτή την περιοχή και συνεχίστε κανονικά. Σφάλματα: B.

#7.7 Αποστολές: παραγγελία → voucher

#POST/api/transportation/update-basket

Αντικαθιστά τα δέματα, τον αποστολέα και τον παραλήπτη του καλαθιού και ορίζει το σχόλιο της παραγγελίας.

JSON
{"basketId": "…", "packages": ["…same as create…"], "sender": {"…": "…"}, "receiver": {"…": "…"},
 "receiverComment": "Παρακαλώ τηλεφωνήστε πριν την παράδοση"}

Απάντηση: {"data": {"basketId": "…"}}. Σφάλματα: B.

  • Στέλνετε πάντα ξανά τα packages: αν τα παραλείψετε, το καλάθι μένει άδειο.
  • Χρησιμοποιήστε το κατά την παραγγελία για να βάλετε στον παραλήπτη το πραγματικό όνομα, τηλέφωνο και e-mail του πελάτη.
  • Το receiverComment εμφανίζεται ως οδηγίες του παραλήπτη στην αποστολή.

#POST/api/transportation/set-routes

Αποθηκεύει την επιλεγμένη διαδρομή. Το routes περιέχει τα τμήματα μίας διαδρομής (όχι λίστα διαδρομών), αντιγραμμένα από μια απάντηση του search-routes του ίδιου καλαθιού:

JSON
{"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· το packagePrices δεν πρέπει να είναι κενό. Στείλτε τις τιμές ακριβώς όπως τις επέστρεψε το search-routes. Απάντηση: {"data": {"basketId": "…"}}. Σφάλματα: B.

#POST/api/transportation/payment-complete

Καταγράφει τον τρόπο πληρωμής της αποστολής. Για ιστότοπους με προπληρωμένο πορτοφόλι (όλοι οι ιστότοποι plugin), ορίστε το creditAmount στο σύνολο της διαδρομής ώστε να αφαιρεθεί από το πορτοφόλι κατά το complete.

JSON
{"basketId": "…", "totalPrice": 11.10, "creditAmount": 11.10, "status": "success",
 "conversationId": "order-1001", "paymentType": "CREDIT", "currency": "EUR", "bank": "MyShop", "card": "MyShop"}
ΠεδίοΣημειώσεις
totalPriceΣ price των τμημάτων της επιλεγμένης διαδρομής
creditAmountΠοσό που αφαιρείται από το πορτοφόλι. Χρησιμοποιήστε την ίδια τιμή με το totalPrice
statusΠρέπει να είναι success, διαφορετικά το complete απορρίπτει το καλάθι
conversationIdΗ αναφορά της παραγγελίας σας, έως 50
paymentType, currency, bank, cardΥποχρεωτικές μη κενές συμβολοσειρές· τα CREDIT, EUR και το όνομα του καταστήματός σας αρκούν

Απάντηση: {"data": {"basketId": "…"}}. Δεν χρεώνεται ακόμη τίποτα. Σφάλματα: B.

#POST/api/transportation/complete

Δημιουργεί την εντολή μεταφοράς του Mydema (το voucher) από το καλάθι. Σώμα: {"basketId": "…"}.

JSON
{"data": {"transportations": {"id": 5123, "orderNumber": "MY-1042", "transferStatus": {"code": "CR", "name": "Created"},
  "totalPrice": 11.1, "shipments": ["…"], "details": ["…"]}}}

Το πλήρες αντικείμενο έχει την ίδια μορφή με το GET/api/transportation/{id} (7.8). Αποθηκεύστε το data.transportations.id. Σφάλματα: B.

Τι συμβαίνει:

  • Το creditAmount αφαιρείται από το πορτοφόλι του ιστότοπου. Αν η πίστωση δεν επαρκεί, το αίτημα αποτυγχάνει (Insufficient credit.) και δεν δημιουργείται τίποτα. Ελέγξτε πρώτα το υπόλοιπο (7.9).
  • Οι μεταφορείς της διαδρομής ειδοποιούνται, ο παραλήπτης λαμβάνει e-mail (αν το e-mail του παραλήπτη είναι πραγματικό) και η διεύθυνση του αποστολέα λαμβάνει e-mail επιβεβαίωσης.
  • Μπορεί να επαναληφθεί με ασφάλεια για το ίδιο καλάθι. Από τη στιγμή που ένα καλάθι έχει γίνει αποστολή, το complete επιστρέφει ξανά την ίδια αποστολή και δεν χρεώνει τίποτα· τα set-routes και payment-complete σε αυτό το καλάθι αγνοούνται (κανόνας 8.2).

Προϋποθέσεις, με τη σειρά: το καλάθι υπάρχει· η πληρωμή έχει καταγραφεί με status: success (διαφορετικά Payment is not valid)· επαρκής πίστωση.

#7.8 Αποστολές: voucher και παρακολούθηση

#GET/api/transportation/{id}/voucher-pdf?status=confirmed

Επιστρέφει το voucher ως application/pdf (Content-Disposition: attachment; filename="MY-1042.pdf"). Το status είναι confirmed ή temporary· ορίζει μόνο την κεφαλίδα απάντησης X-Mydema-Voucher-Status. Σφάλματα: A.

#GET/api/transportation/{id}

JSON
{"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 έχει μία εγγραφή ανά σκέλος· το actions είναι το ιστορικό αυτού του σκέλους. Σφάλματα: A (το Company not found. σημαίνει ότι το αναγνωριστικό παραγγελίας δεν υπάρχει).

transferStatus.code παραγγελίαςΣημασία
CRΔημιουργήθηκε
PPΕκκρεμεί πληρωμή
PAΠληρώθηκε
TRΣε μεταφορά
CMΟλοκληρώθηκε
CLΑκυρώθηκε
shipmentStatus.code αποστολήςΣημασία
DOΗ εντολή δημιουργήθηκε
PEΣε εκκρεμότητα
REΠαραλήφθηκε από τον μεταφορέα
TRΣε διαμετακόμιση
DTΠρος παράδοση
DEΠαραδόθηκε

Δεν υπάρχει webhook· κάνετε polling σε αυτό το endpoint (για παράδειγμα ανά ώρα για τις ανοιχτές αποστολές).

#7.9 Πορτοφόλι πίστωσης

Οι αποστολές πληρώνονται από το προπληρωμένο πορτοφόλι του ιστότοπου. Και τα τρία endpoints λειτουργούν στο πορτοφόλι του συνδεδεμένου ιστότοπου. Σφάλματα: B.

#GET/api/customer/credit/balance

JSON
{"data": {"balance": 42.5, "currency": "EUR"}}

#GET/api/customer/credit/transactions?page=1&limit=50

limit 1–200, προεπιλογή 50.

JSON
{"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 είναι σε τοπική ώρα του server χωρίς ένδειξη ζώνης ώρας.

#POST/api/customer/credit/topup + σελίδα πληρωμής

Οι αναπληρώσεις πληρώνονται με κάρτα στη σελίδα πληρωμής του ίδιου του Mydema.

  1. POST/api/customer/credit/topup με {"amount": 50} (καθαρό ποσό, 1–1000 €). Απάντηση: {"data": {"basketId": "…", "customerId": 273, "amount": 50, "tax": 12, "chargeTotal": 62, "currency": "EUR"}} (προστίθεται ΦΠΑ 24%).
  2. POST/api/auth/plugin-autologin με {"redirect": "/customer/<customerId>/credit/payment/<basketId>/init"} → {"data": {"token": "…"}} (ισχύει για 2 λεπτά).
  3. Ανοίξτε το <base URL>/plugin/auto-login?t=<token> στον browser του εμπόρου. Το Mydema συνδέει τον έμπορο και εμφανίζει τη σελίδα πληρωμής με κάρτα. Το υπόλοιπο αυξάνεται όταν η πληρωμή ολοκληρωθεί επιτυχώς.

#7.10 Προαιρετικά

#POST/api/afm — αναζήτηση ελληνικού ΑΦΜ

Σώμα: {"afmNumber": "099999999"} (9–12 χαρακτήρες). Σφάλματα: B.

JSON
{"data": {"taxData": {"taxId": "099999999", "taxOffice": "ΦΑΕ ΑΘΗΝΩΝ", "name": "ACME ΑΕ", "address": "ΟΔΟΣ 1", "zip": "10431", "location": "ΑΘΗΝΑ"}}}

Ένας άγνωστος αριθμός επιστρέφει την ίδια μορφή με κενές συμβολοσειρές.

#Ρυθμίσεις ιστότοπου

Ελεύθερες ρυθμίσεις κλειδιού/τιμής που αποθηκεύονται στο Mydema για τον ιστότοπό σας.

EndpointΣημειώσεις
GET/api/integration/{integrationId}/settings{"data": {"settings": {"currency": {"key": "currency", "value": "EUR", "valueType": "string"}}}} ([] όταν είναι κενό)
GET/api/integration/{integrationId}/settings/{key}{"data": {"setting": {…}}}· άγνωστο κλειδί → 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}}

Επιτρεπόμενα κλειδιά: 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. Άλλα κλειδιά αναφέρονται στο errors.

#Πληρωμές voucher (εβδομαδιαία τιμολόγια)

Τιμολόγια που εκδίδει το Mydema για τα vouchers του ιστότοπου σε εβδομαδιαίες δέσμες, πληρωτέα με τραπεζική μεταφορά. Αφορούν μόνο αν το Mydema τιμολογεί τον ιστότοπό σας με αυτόν τον τρόπο. Όλα είναι POST εκτός από τα δύο GET.

EndpointΣώμαΑπάντηση
POST/api/voucher-payment/summary{}{"data": {"count": 0, "totalAmount": 0, "oldestDueDate": null}}
POST/api/voucher-payment/list{"status": "unpaid", "page": 1, "perPage": 50} (επίσης 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. Κανόνες ενσωμάτωσης και παγίδες

8.1 Τα επιχειρησιακά σφάλματα είναι HTTP 500. Διαβάζετε το σώμα (ενότητα 4.3) αντί για τον κωδικό κατάστασης και μην επαναλαμβάνετε αυτόματα τις απαντήσεις 500. Αξίζει να επαναλαμβάνετε μόνο τα 503 και τις αποτυχίες δικτύου.

8.2 Το complete είναι idempotent ανά καλάθι. (Idempotent: η επανάληψη της κλήσης δίνει το ίδιο αποτέλεσμα.) Ένα καλάθι γίνεται το πολύ μία αποστολή: αν καλέσετε ξανά το complete για το ίδιο καλάθι, επιστρέφεται η υπάρχουσα αποστολή (ίδιο id) και το πορτοφόλι δεν χρεώνεται ξανά, οπότε μετά από timeout μπορείτε απλώς να το καλέσετε ξανά. Όσο μια πρώτη κλήση εκτελείται ακόμη, μια δεύτερη αποτυγχάνει με This basket is already being completed. Please try again in a moment. — δοκιμάστε ξανά λίγα δευτερόλεπτα αργότερα. Το complete μπορεί να διαρκέσει αρκετά δευτερόλεπτα (στέλνει τα e-mails πριν απαντήσει)· χρησιμοποιήστε timeout τουλάχιστον 30 s. Παρ' όλα αυτά αποθηκεύστε το αναγνωριστικό που επιστρέφεται μαζί με την παραγγελία σας: μια παραγγελία με εμπορεύματα από πολλές αποθήκες έχει ένα καλάθι, και μία αποστολή, ανά αποθήκη.

8.3 Τιμολογήστε ξανά, κατά την παραγγελία, τη διαδρομή που είδε ο πελάτης. Καλέστε το search-routes τη στιγμή της παραγγελίας, επιλέξτε τη διαδρομή με τα ίδια routeId και στείλτε ακριβώς αυτά τα τμήματα στο set-routes. Οι τιμές μπορεί να αλλάξουν μεταξύ προσφοράς και παραγγελίας.

8.4 Ελέγξτε το πορτοφόλι πριν από το complete. GET/api/customer/credit/balance ≥ σύνολο διαδρομής, διαφορετικά ζητήστε από τον έμπορο να κάνει αναπλήρωση (7.9) και δοκιμάστε ξανά αργότερα.

8.5 Ένα καλάθι ανά αποθήκη. Εμπορεύματα που αναχωρούν από διαφορετικές περιοχές είναι διαφορετικές αποστολές. Δημιουργήστε, τιμολογήστε και ολοκληρώστε ένα καλάθι ανά αποστολέα.

8.6 Περιοχές, όχι ονόματα. Κάθε προσφορά χρειάζεται αναγνωριστικό περιοχής και για τις δύο πλευρές. Αντιστοιχίστε τη διεύθυνση του πελάτη σε μια εξυπηρετούμενη περιοχή (6.1) πριν καλέσετε το create· όταν μπορείτε να την περιορίσετε μόνο σε επίπεδο νομού, χρησιμοποιήστε το quote-districts και αφήστε τον πελάτη να επιλέξει.

8.7 Δέματα τύπου κουτιού: παραλείψτε τα shipmentType / shipmentSize. Ένας άγνωστος κωδικός κάνει όλο το αίτημα να αποτύχει (6.2).

8.8 Στέλνετε ξανά τα δέματα στο update-basket. Αν τα παραλείψετε, το καλάθι αδειάζει.

8.9 Πραγματικά στοιχεία επικοινωνίας παραλήπτη κατά την παραγγελία. Το Mydema στέλνει e-mail στον παραλήπτη και ο courier του τηλεφωνεί· τα placeholders της προσφοράς πρέπει να αντικατασταθούν μέσω update-basket πριν από το complete.

8.10 Κρατήστε το token. Συνδεθείτε μία φορά, ξαναχρησιμοποιήστε το token και συνδεθείτε ξανά μόνο μετά από 401 (3.3). Η σύνδεση σε κάθε αίτημα είναι περιττή.