{
  "info": {
    "_postman_id": "2ba593b5-28cf-5f78-be5f-08257195149d",
    "name": "Mydema Plugin API",
    "description": "Requests used by the Mydema e-shop plugins: authentication, location and package reference data, suppliers, checkout quotes, order → voucher, tracking and the credit wallet.\n\n1. Import an environment (staging or production) and set `apiKey` and `domain`.\n2. Requests log in automatically. Folders 4 and 5 are meant to be run top to bottom; their scripts carry `basketId`, the chosen route and the shipment id forward.\n\nFull guide: docs/api/plugin-api-en.md",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{accessToken}}",
        "type": "string"
      }
    ]
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Logs in first when there is no token yet.",
          "// Public requests (login, site-request) are skipped. The token is valid for 12 hours, sliding;",
          "// after a 401 the collection test clears it, so sending the request again logs in again.",
          "const path = pm.request.url.getPath();",
          "const isPublic = [\"/api/auth/plugin-login\", \"/api/plugin/site-request\"].some(p => path.startsWith(p));",
          "if (!isPublic && !pm.collectionVariables.get('accessToken')) {",
          "  const baseUrl = pm.variables.get('baseUrl');",
          "  pm.sendRequest({",
          "    url: baseUrl + '/api/auth/plugin-login',",
          "    method: 'POST',",
          "    header: {'Content-Type': 'application/json', 'Accept': 'application/json'},",
          "    body: {mode: 'raw', raw: JSON.stringify({apiKey: pm.variables.get('apiKey'), apiSecret: '', domain: pm.variables.get('domain')})}",
          "  }, (err, res) => {",
          "    if (err || res.code !== 200) { console.error('Mydema login failed', err || res.text()); return; }",
          "    const d = res.json().data;",
          "    pm.collectionVariables.set('accessToken', d.accessToken);",
          "    pm.collectionVariables.set('integrationId', d.tokenData.integrationId);",
          "    pm.collectionVariables.set('customerId', d.tokenData.pluginCustomerId);",
          "  });",
          "}"
        ]
      }
    },
    {
      "listen": "test",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Invalid token: clear it so the next request logs in again.",
          "if (pm.response.code === 401) {",
          "  try { const b = pm.response.json(); if (['TOKEN_EXPIRED','TOKEN_CHANGED','MISSING_TOKEN'].includes(b.code)) {",
          "    pm.collectionVariables.unset('accessToken'); console.warn('Token cleared (' + b.code + '). Send the request again.'); } } catch (e) {}",
          "}",
          "// Print the readable error message (error.message is often a JSON string itself; guide 4.3).",
          "if (pm.response.code >= 400) {",
          "  try {",
          "    const b = pm.response.json();",
          "    let m = b.error ? b.error.message : (b.detail || b.message);",
          "    try { const inner = JSON.parse(m); m = inner.message + ' (code ' + inner.code + ')'; } catch (e) {}",
          "    console.warn('Mydema error: ' + m);",
          "  } catch (e) {}",
          "}"
        ]
      }
    }
  ],
  "variable": [
    {
      "key": "accessToken",
      "value": "",
      "description": "Set by login"
    },
    {
      "key": "integrationId",
      "value": "",
      "description": "Set by login"
    },
    {
      "key": "customerId",
      "value": "",
      "description": "Wallet owner, set by login"
    },
    {
      "key": "senderDistrictId",
      "value": "1",
      "description": "Origin district (warehouse). 1 = Κομοτηνή"
    },
    {
      "key": "receiverDistrictId",
      "value": "158",
      "description": "Destination district. 158 = Φλώρινα"
    },
    {
      "key": "basketId",
      "value": ""
    },
    {
      "key": "routesPayload",
      "value": "[]",
      "description": "Chosen route's segments, set by Search routes"
    },
    {
      "key": "routeTotal",
      "value": "0",
      "description": "Chosen route's price, set by Search routes"
    },
    {
      "key": "transportationId",
      "value": ""
    },
    {
      "key": "supplierId",
      "value": ""
    },
    {
      "key": "topupBasketId",
      "value": ""
    },
    {
      "key": "voucherPaymentId",
      "value": ""
    },
    {
      "key": "claimSecret",
      "value": "",
      "description": "Generated by Submit site request; keep it"
    },
    {
      "key": "claimSecretHash",
      "value": ""
    },
    {
      "key": "requestId",
      "value": ""
    }
  ],
  "item": [
    {
      "name": "0. Connect (public)",
      "description": "Self-service connection: request → staff approval → claim the API key. These requests need no token. See guide section 2.1.",
      "item": [
        {
          "name": "Submit site request",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/plugin/site-request",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "plugin",
                "site-request"
              ]
            },
            "description": "Creates a connection request. The pre-request script generates `claimSecret` once and sends its SHA-256 as `claimSecretHash`. Keep `claimSecret`; status and claim need it.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"domain\": \"{{domain}}\",\n  \"siteName\": \"My Shop\",\n  \"platform\": \"woocommerce\",\n  \"pluginVersion\": \"1.0.0\",\n  \"companyName\": \"My Shop IKE\",\n  \"taxId\": \"\",\n  \"taxOffice\": \"\",\n  \"address\": \"\",\n  \"postalCode\": \"\",\n  \"city\": \"\",\n  \"contactName\": \"Developer Name\",\n  \"contactEmail\": \"dev@example.com\",\n  \"contactPhone\": \"\",\n  \"claimSecretHash\": \"{{claimSecretHash}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (!pm.collectionVariables.get('claimSecret')) {",
                  "  pm.collectionVariables.set('claimSecret', CryptoJS.lib.WordArray.random(32).toString());",
                  "}",
                  "pm.collectionVariables.set('claimSecretHash', CryptoJS.SHA256(pm.collectionVariables.get('claimSecret')).toString());"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "if (json.data.requestId !== undefined && json.data.requestId !== null) pm.collectionVariables.set('requestId', json.data.requestId);"
                ]
              }
            }
          ]
        },
        {
          "name": "Request status",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/plugin/site-request/status",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "plugin",
                "site-request",
                "status"
              ]
            },
            "description": "Poll until `status` is `approved` (or `rejected`).",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"{{requestId}}\",\n  \"claimSecret\": \"{{claimSecret}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Claim API key",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/plugin/site-request/claim",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "plugin",
                "site-request",
                "claim"
              ]
            },
            "description": "Returns `apiKey`, `apiEndpoint`, `integrationId`. Every call issues a NEW key and invalidates the previous one. The test copies the key into the active environment.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"{{requestId}}\",\n  \"claimSecret\": \"{{claimSecret}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const d = pm.response.json().data;",
                  "if (d && d.apiKey) { pm.environment.set('apiKey', d.apiKey); pm.collectionVariables.unset('accessToken'); }"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "1. Authentication",
      "description": "See guide section 3.",
      "item": [
        {
          "name": "Plugin login",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/auth/plugin-login",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "auth",
                "plugin-login"
              ]
            },
            "description": "Exchanges the `mk_live_` key for a bearer token (12 h, sliding). Other requests log in automatically when `accessToken` is empty.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"apiKey\": \"{{apiKey}}\",\n  \"apiSecret\": \"\",\n  \"domain\": \"{{domain}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "if (json.data.accessToken !== undefined && json.data.accessToken !== null) pm.collectionVariables.set('accessToken', json.data.accessToken);",
                  "if (json.data.tokenData.integrationId !== undefined && json.data.tokenData.integrationId !== null) pm.collectionVariables.set('integrationId', json.data.tokenData.integrationId);",
                  "if (json.data.tokenData.pluginCustomerId !== undefined && json.data.tokenData.pluginCustomerId !== null) pm.collectionVariables.set('customerId', json.data.tokenData.pluginCustomerId);"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "2. Reference data",
      "description": "Package catalogue and location lookups (guide sections 6.1, 6.2, 7.3, 7.4). All location lookups are POST.",
      "item": [
        {
          "name": "Package templates",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/shipment/templates",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "shipment",
                "templates"
              ]
            },
            "description": "Box (M3) and pallet (PL) templates. Cache for a day."
          },
          "response": []
        },
        {
          "name": "District autocomplete (served)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/location/district/avail-autocomplate",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "location",
                "district",
                "avail-autocomplate"
              ]
            },
            "description": "Served districts matching a name. Use `locations[].id` as `address.district`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"term\": \"Κομοτην\",\n  \"limit\": 10\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Served districts (list)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/location/district/avail-list",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "location",
                "district",
                "avail-list"
              ]
            },
            "description": "All served districts, optionally filtered by `params.state` or `params.city`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"params\": {\n    \"state\": 65\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Postal code lookup",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/autocomplate/location/postal-code",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "autocomplate",
                "location",
                "postal-code"
              ]
            },
            "description": "Postal code prefix → settlement and prefecture NAMES (not ids).",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"term\": \"69131\",\n  \"limit\": 50\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "States (prefectures)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/location/state/list",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "location",
                "state",
                "list"
              ]
            },
            "description": "ListRequest.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"orderBy\": {\n    \"name\": \"ASC\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Cities of a state",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/location/city/avail-list",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "location",
                "city",
                "avail-list"
              ]
            },
            "description": "`params.state` is required.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"params\": {\n    \"state\": 65\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "3. Suppliers (warehouses)",
      "description": "Pickup addresses your goods ship from (guide section 7.5).",
      "item": [
        {
          "name": "Create supplier",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/supplier",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "supplier"
              ]
            },
            "description": "",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Αποθήκη Κομοτηνής\",\n  \"pickupAddress\": \"Οδός Δοκιμής 1\",\n  \"districtId\": 1,\n  \"postalCode\": \"69100\",\n  \"contactName\": \"Γιώργος\",\n  \"contactPhone\": \"2531000000\",\n  \"contactEmail\": \"warehouse@example.com\",\n  \"pickupHoursStart\": \"09:00\",\n  \"pickupHoursEnd\": \"17:00\",\n  \"externalCode\": \"WH-1\",\n  \"notes\": null\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "if (json.data.supplier.id !== undefined && json.data.supplier.id !== null) pm.collectionVariables.set('supplierId', json.data.supplier.id);"
                ]
              }
            }
          ]
        },
        {
          "name": "List suppliers",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/supplier/list",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "supplier",
                "list"
              ]
            },
            "description": "",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get supplier",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/supplier/{{supplierId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "supplier",
                "{{supplierId}}"
              ]
            },
            "description": ""
          },
          "response": []
        },
        {
          "name": "Update supplier",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/supplier/{{supplierId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "supplier",
                "{{supplierId}}"
              ]
            },
            "description": "Send only the fields to change. Do not send `integrationId`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"contactName\": \"Νίκος\",\n  \"pickupHoursEnd\": \"18:00\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Suppliers of this site",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/integration/{{integrationId}}/supplier",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "integration",
                "{{integrationId}}",
                "supplier"
              ]
            },
            "description": ""
          },
          "response": []
        },
        {
          "name": "Bulk sync suppliers",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/supplier/sync",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "supplier",
                "sync"
              ]
            },
            "description": "Upsert by `externalCode`, max 200 rows. `cityId` is required on each row.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"integrationId\": {{integrationId}},\n  \"rows\": [\n    {\"externalCode\": \"WH-2\", \"name\": \"Αποθήκη Θεσσαλονίκης\", \"pickupAddress\": \"Οδός 2\", \"cityId\": 67, \"districtId\": 1}\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Delete supplier",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/supplier/{{supplierId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "supplier",
                "{{supplierId}}"
              ]
            },
            "description": ""
          },
          "response": []
        }
      ]
    },
    {
      "name": "4. Checkout quote",
      "description": "Price a cart: one basket per origin warehouse (guide sections 5, 7.6).",
      "item": [
        {
          "name": "Create basket",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transportation/create",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transportation",
                "create"
              ]
            },
            "description": "Packages in cm/kg. `address.district` = district ids from section 2. Omit shipmentType/shipmentSize for boxes and pallets.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"packages\": [\n    {\n      \"weightType\": \"M3\",\n      \"weight\": 25,\n      \"width\": 58.48,\n      \"height\": 58.48,\n      \"depth\": 58.48,\n      \"isSensitive\": false\n    }\n  ],\n  \"sender\": {\n    \"firstName\": \"Αποθήκη\",\n    \"lastName\": \"Κομοτηνής\",\n    \"email\": \"warehouse@example.com\",\n    \"phone\": \"2531000000\",\n    \"customerType\": \"S\",\n    \"address\": {\n      \"address\": \"Οδός Δοκιμής 1\",\n      \"postalCode\": \"69100\",\n      \"district\": {{senderDistrictId}},\n      \"city\": null\n    }\n  },\n  \"receiver\": {\n    \"firstName\": \"Μαρία\",\n    \"lastName\": \"Παπαδοπούλου\",\n    \"email\": \"maria@example.com\",\n    \"phone\": \"6900000000\",\n    \"customerType\": \"I\",\n    \"address\": {\n      \"address\": \"Οδός Παραλήπτη 5\",\n      \"postalCode\": \"\",\n      \"district\": {{receiverDistrictId}},\n      \"city\": null\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "if (json.data.basketId !== undefined && json.data.basketId !== null) pm.collectionVariables.set('basketId', json.data.basketId);"
                ]
              }
            }
          ]
        },
        {
          "name": "Search routes",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transportation/search-routes",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transportation",
                "search-routes"
              ]
            },
            "description": "Each `routes[i]` is one shipping option (a list of legs). Price = Σ segment.price. The test prints all options and keeps the cheapest for the order folder.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"basketId\": \"{{basketId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const res = pm.response.json().data;",
                  "// Route price = sum of segment 'price' (VAT and service fee included). Do not use serviceFees.",
                  "const routes = (res.routes || []).map((segs, i) => ({",
                  "  index: i,",
                  "  total: Math.round(segs.reduce((s, g) => s + g.price, 0) * 100) / 100,",
                  "  vat: Math.round(segs.reduce((s, g) => s + g.tax, 0) * 100) / 100,",
                  "  carriers: segs.map(g => g.data.company).join(' → '),",
                  "  delivery: segs[segs.length - 1].hasAddressDelivery ? 'door' : 'branch: ' + segs[segs.length - 1].data.endPointName,",
                  "  segs",
                  "}));",
                  "routes.forEach(r => console.log('Route ' + r.index + ': ' + r.total + ' EUR (VAT ' + r.vat + ') ' + r.carriers + ' / ' + r.delivery));",
                  "pm.test('At least one route', () => pm.expect(routes.length).to.be.above(0));",
                  "if (routes.length) {",
                  "  // Example: take the cheapest route. In a real integration take the route the customer chose (same routeIds).",
                  "  const best = routes.reduce((a, b) => (b.total < a.total ? b : a));",
                  "  const payload = best.segs.map(g => ({",
                  "    company: g.company, startPoint: g.startPoint, endPoint: g.endPoint, routeId: g.routeId,",
                  "    price: g.price, tax: g.tax, serviceFee: g.serviceFee, packagePrices: g.packagePrices,",
                  "    deliveryDoor: !!g.deliveryDoor, deliveryDoorPrice: g.deliveryDoorPrice || 0,",
                  "    carrier: !!g.carrier, carrierPrice: g.carrierPrice || 0, hasAddressDelivery: !!g.hasAddressDelivery",
                  "  }));",
                  "  pm.collectionVariables.set('routesPayload', JSON.stringify(payload));",
                  "  pm.collectionVariables.set('routeTotal', best.total.toFixed(2));",
                  "  console.log('Chosen route ' + best.index + ': ' + best.total + ' EUR (' + best.carriers + ')');",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Service scenarios (why no route?)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transportation/scenarios",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transportation",
                "scenarios"
              ]
            },
            "description": "",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"receiverDistrictId\": {{receiverDistrictId}},\n  \"isB2B\": false,\n  \"hasAfm\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Quote several districts",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transportation/quote-districts",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transportation",
                "quote-districts"
              ]
            },
            "description": "Same body as create plus `districtIds` (max 15). No basket is created.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"packages\": [\n    {\n      \"weightType\": \"M3\",\n      \"weight\": 25,\n      \"width\": 58.48,\n      \"height\": 58.48,\n      \"depth\": 58.48,\n      \"isSensitive\": false\n    }\n  ],\n  \"sender\": {\n    \"firstName\": \"Αποθήκη\",\n    \"lastName\": \"Κομοτηνής\",\n    \"email\": \"warehouse@example.com\",\n    \"phone\": \"2531000000\",\n    \"customerType\": \"S\",\n    \"address\": {\n      \"address\": \"Οδός Δοκιμής 1\",\n      \"postalCode\": \"69100\",\n      \"district\": {{senderDistrictId}},\n      \"city\": null\n    }\n  },\n  \"receiver\": {\n    \"firstName\": \"Μαρία\",\n    \"lastName\": \"Παπαδοπούλου\",\n    \"email\": \"maria@example.com\",\n    \"phone\": \"6900000000\",\n    \"customerType\": \"I\",\n    \"address\": {\n      \"address\": \"Οδός Παραλήπτη 5\",\n      \"postalCode\": \"\",\n      \"district\": {{receiverDistrictId}},\n      \"city\": null\n    }\n  },\n  \"districtIds\": [\n    {{receiverDistrictId}},\n    1\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "5. Order → voucher",
      "description": "Turn a paid order into a Mydema shipment (guide section 7.7). Run in this order. `Complete` creates a real shipment and charges the wallet: run it ONCE per basket.",
      "item": [
        {
          "name": "Update basket (real contact + comment)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transportation/update-basket",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transportation",
                "update-basket"
              ]
            },
            "description": "Always resend packages (omitting them empties the basket).",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"packages\": [\n    {\n      \"weightType\": \"M3\",\n      \"weight\": 25,\n      \"width\": 58.48,\n      \"height\": 58.48,\n      \"depth\": 58.48,\n      \"isSensitive\": false\n    }\n  ],\n  \"sender\": {\n    \"firstName\": \"Αποθήκη\",\n    \"lastName\": \"Κομοτηνής\",\n    \"email\": \"warehouse@example.com\",\n    \"phone\": \"2531000000\",\n    \"customerType\": \"S\",\n    \"address\": {\n      \"address\": \"Οδός Δοκιμής 1\",\n      \"postalCode\": \"69100\",\n      \"district\": {{senderDistrictId}},\n      \"city\": null\n    }\n  },\n  \"receiver\": {\n    \"firstName\": \"Μαρία\",\n    \"lastName\": \"Παπαδοπούλου\",\n    \"email\": \"maria@example.com\",\n    \"phone\": \"6900000000\",\n    \"customerType\": \"I\",\n    \"address\": {\n      \"address\": \"Οδός Παραλήπτη 5\",\n      \"postalCode\": \"\",\n      \"district\": {{receiverDistrictId}},\n      \"city\": null\n    }\n  },\n  \"basketId\": \"{{basketId}}\",\n  \"receiverComment\": \"Παρακαλώ τηλεφωνήστε πριν την παράδοση\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Search routes (fresh prices)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transportation/search-routes",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transportation",
                "search-routes"
              ]
            },
            "description": "Re-price at order time and pick the route the customer chose.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"basketId\": \"{{basketId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const res = pm.response.json().data;",
                  "// Route price = sum of segment 'price' (VAT and service fee included). Do not use serviceFees.",
                  "const routes = (res.routes || []).map((segs, i) => ({",
                  "  index: i,",
                  "  total: Math.round(segs.reduce((s, g) => s + g.price, 0) * 100) / 100,",
                  "  vat: Math.round(segs.reduce((s, g) => s + g.tax, 0) * 100) / 100,",
                  "  carriers: segs.map(g => g.data.company).join(' → '),",
                  "  delivery: segs[segs.length - 1].hasAddressDelivery ? 'door' : 'branch: ' + segs[segs.length - 1].data.endPointName,",
                  "  segs",
                  "}));",
                  "routes.forEach(r => console.log('Route ' + r.index + ': ' + r.total + ' EUR (VAT ' + r.vat + ') ' + r.carriers + ' / ' + r.delivery));",
                  "pm.test('At least one route', () => pm.expect(routes.length).to.be.above(0));",
                  "if (routes.length) {",
                  "  // Example: take the cheapest route. In a real integration take the route the customer chose (same routeIds).",
                  "  const best = routes.reduce((a, b) => (b.total < a.total ? b : a));",
                  "  const payload = best.segs.map(g => ({",
                  "    company: g.company, startPoint: g.startPoint, endPoint: g.endPoint, routeId: g.routeId,",
                  "    price: g.price, tax: g.tax, serviceFee: g.serviceFee, packagePrices: g.packagePrices,",
                  "    deliveryDoor: !!g.deliveryDoor, deliveryDoorPrice: g.deliveryDoorPrice || 0,",
                  "    carrier: !!g.carrier, carrierPrice: g.carrierPrice || 0, hasAddressDelivery: !!g.hasAddressDelivery",
                  "  }));",
                  "  pm.collectionVariables.set('routesPayload', JSON.stringify(payload));",
                  "  pm.collectionVariables.set('routeTotal', best.total.toFixed(2));",
                  "  console.log('Chosen route ' + best.index + ': ' + best.total + ' EUR (' + best.carriers + ')');",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Credit balance",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/customer/credit/balance",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "customer",
                "credit",
                "balance"
              ]
            },
            "description": "Must be ≥ `routeTotal` before Complete."
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const bal = pm.response.json().data.balance;",
                  "const need = parseFloat(pm.collectionVariables.get('routeTotal') || '0');",
                  "pm.test('Balance covers the route (' + bal + ' ≥ ' + need + ')', () => pm.expect(bal).to.be.at.least(need));"
                ]
              }
            }
          ]
        },
        {
          "name": "Set routes",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transportation/set-routes",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transportation",
                "set-routes"
              ]
            },
            "description": "`routes` = the segments of the chosen route, exactly as search-routes returned them (filled in by the search-routes test).",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"basketId\": \"{{basketId}}\",\n  \"routes\": {{routesPayload}}\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Payment complete",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transportation/payment-complete",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transportation",
                "payment-complete"
              ]
            },
            "description": "`creditAmount` = amount taken from the wallet at Complete. `status` must be `success`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"basketId\": \"{{basketId}}\",\n  \"totalPrice\": {{routeTotal}},\n  \"creditAmount\": {{routeTotal}},\n  \"status\": \"success\",\n  \"conversationId\": \"order-1001\",\n  \"paymentType\": \"CREDIT\",\n  \"currency\": \"EUR\",\n  \"bank\": \"MyShop\",\n  \"card\": \"MyShop\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Complete (creates the shipment)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transportation/complete",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transportation",
                "complete"
              ]
            },
            "description": "Creates the transportation order and charges the wallet. NOT idempotent: a second call creates a second shipment. The id is at data.transportations.id.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"basketId\": \"{{basketId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "if (json.data.transportations.id !== undefined && json.data.transportations.id !== null) pm.collectionVariables.set('transportationId', json.data.transportations.id);"
                ]
              }
            }
          ]
        },
        {
          "name": "Voucher PDF",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transportation/{{transportationId}}/voucher-pdf?status=confirmed",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transportation",
                "{{transportationId}}",
                "voucher-pdf"
              ],
              "query": [
                {
                  "key": "status",
                  "value": "confirmed"
                }
              ]
            },
            "description": "Returns application/pdf. Use Send and Download."
          },
          "response": []
        },
        {
          "name": "Tracking",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transportation/{{transportationId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transportation",
                "{{transportationId}}"
              ]
            },
            "description": "Order status (`transferStatus`) and per-leg status/history (`shipments[].shipmentStatus`, `actions`)."
          },
          "response": []
        }
      ]
    },
    {
      "name": "6. Credit wallet",
      "description": "Prepaid balance that pays for shipments (guide section 7.9).",
      "item": [
        {
          "name": "Balance",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/customer/credit/balance",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "customer",
                "credit",
                "balance"
              ]
            },
            "description": ""
          },
          "response": []
        },
        {
          "name": "Transactions",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/customer/credit/transactions?page=1&limit=50",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "customer",
                "credit",
                "transactions"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1"
                },
                {
                  "key": "limit",
                  "value": "50"
                }
              ]
            },
            "description": ""
          },
          "response": []
        },
        {
          "name": "Top-up: create payment basket",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/customer/credit/topup",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "customer",
                "credit",
                "topup"
              ]
            },
            "description": "Net amount 1–1000 EUR; 24% VAT is added.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"amount\": 50\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "if (json.data.basketId !== undefined && json.data.basketId !== null) pm.collectionVariables.set('topupBasketId', json.data.basketId);"
                ]
              }
            }
          ]
        },
        {
          "name": "Top-up: payment page link",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/auth/plugin-autologin",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "auth",
                "plugin-autologin"
              ]
            },
            "description": "Returns a 2-minute token. The test prints the URL to open in the merchant's browser.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"redirect\": \"/customer/{{customerId}}/credit/payment/{{topupBasketId}}/init\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const t = pm.response.json().data.token;",
                  "console.log('Open in a browser: ' + pm.variables.get('baseUrl') + '/plugin/auto-login?t=' + t);"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "7. Optional",
      "description": "VAT-number lookup, site settings, weekly voucher invoices (guide section 7.10).",
      "item": [
        {
          "name": "VAT number (ΑΦΜ) lookup",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/afm",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "afm"
              ]
            },
            "description": "",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"afmNumber\": \"099999999\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Settings: all",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/integration/{{integrationId}}/settings",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "integration",
                "{{integrationId}}",
                "settings"
              ]
            },
            "description": ""
          },
          "response": []
        },
        {
          "name": "Settings: write",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/integration/{{integrationId}}/settings",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "integration",
                "{{integrationId}}",
                "settings"
              ]
            },
            "description": "Bulk upsert of allowed keys.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"settings\": [\n    {\n      \"key\": \"currency\",\n      \"value\": \"EUR\"\n    },\n    {\n      \"key\": \"free_shipping_threshold\",\n      \"value\": 100\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Settings: one",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/integration/{{integrationId}}/settings/currency",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "integration",
                "{{integrationId}}",
                "settings",
                "currency"
              ]
            },
            "description": ""
          },
          "response": []
        },
        {
          "name": "Settings: delete",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/integration/{{integrationId}}/settings/free_shipping_threshold",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "integration",
                "{{integrationId}}",
                "settings",
                "free_shipping_threshold"
              ]
            },
            "description": ""
          },
          "response": []
        },
        {
          "name": "Voucher invoices: summary",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/voucher-payment/summary",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "voucher-payment",
                "summary"
              ]
            },
            "description": "",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Voucher invoices: list",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/voucher-payment/list",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "voucher-payment",
                "list"
              ]
            },
            "description": "",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"status\": \"unpaid\",\n  \"page\": 1,\n  \"perPage\": 50\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Voucher invoices: get",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/voucher-payment/{{voucherPaymentId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "voucher-payment",
                "{{voucherPaymentId}}"
              ]
            },
            "description": ""
          },
          "response": []
        },
        {
          "name": "Voucher invoices: invoice PDF",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/voucher-payment/{{voucherPaymentId}}/invoice-pdf",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "voucher-payment",
                "{{voucherPaymentId}}",
                "invoice-pdf"
              ]
            },
            "description": ""
          },
          "response": []
        }
      ]
    }
  ]
}
