Cum vă aduceți ofertele pe site

Publicați la o adresă de internet un fișier JSON sau XML cu ofertele agenției, iar noi îl citim singuri, de câteva ori pe zi. Sau programul agenției ne trimite ofertele prin API-ul de import, în același format. Publicarea ofertelor e gratuită.

Prin fișier

  • Ne trimiteți adresa fișierului, de exemplu https://agentia-ta.ro/oferte.json. Fișierul poate fi făcut de programul de rezervări al agenției.
  • Citim fișierul o dată la 4 ore. Ofertele noi apar pe site, cele schimbate se schimbă și la noi.
  • O ofertă care nu mai e în fișier dispare de pe site. Dacă revine în fișier, apare din nou, la aceeași adresă.
  • Dacă fișierul nu poate fi citit, nu schimbăm nimic: ofertele rămân cum erau, iar noi vă spunem ce e greșit. O ofertă cu o greșeală rămâne și ea cum era, iar celelalte se actualizează.
  • Descărcăm pozele la noi, o singură dată. Folosiți poze JPEG, PNG sau WebP, de cel mult 10 MB.
  • Turistul vă scrie prin formularul de pe site. De aceea scoatem din descriere numerele de telefon și adresele de email.

Câmpurile unei oferte

Fișierul are o listă offers. În XML, fiecare câmp e un element cu același nume, ca în exemplele de mai jos.

CâmpObligatoriuCe conține
iddaCodul ofertei la agenție, de cel mult 100 de caractere. Nu se schimbă de la o zi la alta: după el recunoaștem oferta.
typedaTipul ofertei, vezi valorile de mai jos.
titledaTitlul ofertei, de cel mult 255 de caractere.
destinationdacountry (țara), apoi, dacă se poate, region (regiunea) și resort (stațiunea sau orașul). La circuit ajunge țara.
accommodationda, în afară de circuitname (numele cazării) și, dacă le aveți, stars (de la 1 la 5), latitude și longitude. Cu ele găsim mai sigur cazarea, dacă o vând și alte agenții.
transportdaCum ajunge turistul, vezi valorile de mai jos.
boarddaCe mese sunt incluse, vezi valorile de mai jos.
nightsdaNumărul de nopți, de la 1 la 365.
descriptiondaDescrierea ofertei, ca text. Poate avea HTML simplu, pe care îl transformăm în text.
photosnuAdresele pozelor (cel mult 30), care încep cu http:// sau https://. Prima e poza principală.
departuresdaPlecările, cel puțin una. Fiecare are date (data, de forma 2027-06-12), price (prețul pe persoană în cameră dublă, cu punct pentru zecimale) și currency (moneda).

Valorile posibile

type

  • stay: sejur
  • tour: circuit
  • accommodation_only: doar cazare

transport

  • plane: avion
  • coach: autocar
  • train: tren
  • own: transport propriu

board

  • none: fără masă
  • breakfast: mic dejun
  • half_board: demipensiune
  • full_board: pensiune completă
  • all_inclusive: all inclusive

currency

  • RON: lei
  • EUR: €

Prin API, din programul agenției

Dacă programul agenției poate trimite cereri HTTP, ne trimite fiecare ofertă imediat ce o schimbați. Oferta are aceleași câmpuri ca în fișier, în JSON. Verificăm, potrivim cazările și descărcăm pozele la fel ca la fișier.

  • Vă dăm o cheie API. O vedeți o singură dată, pentru că noi păstrăm doar amprenta ei. Dacă o pierdeți, vă facem una nouă, iar cea veche nu mai merge.
  • Trimiteți cheia la fiecare cerere, în antetul Authorization: Bearer <cheia>.
  • Codul ofertei (id) e în adresa cererii. În corpul cererii, id poate lipsi.
  • Cu cheia agenției vedeți și schimbați doar ofertele trimise de ea prin API.
  • Spre deosebire de fișier, o ofertă pe care nu o mai trimiteți nu dispare singură. O scoateți de pe site cu DELETE.
CerereCe faceRăspuns
PUT https://turism-test.omidulus.duckdns.org/api/import/offers/{id}Adaugă oferta sau o schimbă. Corpul cererii e oferta, în JSON. O ofertă scoasă de pe site revine, la aceeași adresă.201 pentru o ofertă nouă, 200 pentru una schimbată, cu oferta și cu warnings, pozele care nu s-au descărcat.
DELETE https://turism-test.omidulus.duckdns.org/api/import/offers/{id}Scoate oferta de pe site. O păstrăm, ca să revină la aceeași adresă dacă o trimiteți din nou.204, fără corp.
GET https://turism-test.omidulus.duckdns.org/api/import/offersLista ofertelor trimise prin API.200, cu offers. active spune dacă oferta apare pe site: nu e scoasă și are plecări de azi încolo.

Exemplu

curl -X PUT 'https://turism-test.omidulus.duckdns.org/api/import/offers/LYRA-7' \
  -H 'Authorization: Bearer <cheia>' \
  -H 'Content-Type: application/json' \
  -d @oferta.json

HTTP/1.1 201 Created
{
  "id": "LYRA-7",
  "title": "Sejur în Creta, Hotel Lyra Beach",
  "active": true,
  "url": "https://turism-test.omidulus.duckdns.org/oferta/sejur-in-creta-hotel-lyra-beach-agentia-ta",
  "warnings": []
}

Erorile

O cerere greșită nu schimbă nimic. Răspunsul spune ce e greșit, în română, și câmpul, dacă greșeala ține de un câmp:

HTTP/1.1 422 Unprocessable Content
{
  "errors": [
    {
      "field": "departures[0].price",
      "message": "Plecarea 1 nu are un preț mai mare decât zero în câmpul „price”, cu punct pentru zecimale (ex. 449.50)."
    }
  ]
}
  • 400: corpul cererii nu e o ofertă în JSON.
  • 401: cheia lipsește sau nu e bună.
  • 404: nu aveți o ofertă cu acest cod (la DELETE).
  • 409: aceeași ofertă nouă a venit în două cereri deodată. Trimiteți-o din nou.
  • 422: oferta are o greșeală. Vă spunem prima greșeală găsită.

Exemplu în JSON

Descarcă exemplul JSON

{
  "offers": [
    {
      "id": "CRT-2027-118",
      "type": "stay",
      "title": "Sejur în Creta, Hotel Lyra Beach",
      "destination": {
        "country": "Grecia",
        "region": "Creta",
        "resort": "Hersonissos"
      },
      "accommodation": {
        "name": "Hotel Lyra Beach",
        "stars": 4,
        "latitude": 35.3189,
        "longitude": 25.3901
      },
      "transport": "plane",
      "board": "all_inclusive",
      "nights": 7,
      "description": "Hotel pe plajă, cu piscină și grădină.\nPrețul include zborul, transferul și 7 nopți de cazare.",
      "photos": [
        "https://agentia-ta.ro/poze/lyra-beach-1.jpg",
        "https://agentia-ta.ro/poze/lyra-beach-2.jpg"
      ],
      "departures": [
        { "date": "2027-06-12", "price": 589, "currency": "EUR" },
        { "date": "2027-06-19", "price": 629.50, "currency": "EUR" }
      ]
    },
    {
      "id": "CIRC-GR-07",
      "type": "tour",
      "title": "Circuit Grecia clasică",
      "destination": {
        "country": "Grecia"
      },
      "transport": "coach",
      "board": "half_board",
      "nights": 8,
      "description": "Atena, Delfi, Meteora și Olimpia, cu ghid în limba română.",
      "photos": [
        "https://agentia-ta.ro/poze/meteora.jpg"
      ],
      "departures": [
        { "date": "2027-05-20", "price": 2990, "currency": "RON" }
      ]
    }
  ]
}

Exemplu în XML

Descarcă exemplul XML

<?xml version="1.0" encoding="UTF-8"?>
<offers>
  <offer>
    <id>CRT-2027-118</id>
    <type>stay</type>
    <title>Sejur în Creta, Hotel Lyra Beach</title>
    <destination>
      <country>Grecia</country>
      <region>Creta</region>
      <resort>Hersonissos</resort>
    </destination>
    <accommodation>
      <name>Hotel Lyra Beach</name>
      <stars>4</stars>
      <latitude>35.3189</latitude>
      <longitude>25.3901</longitude>
    </accommodation>
    <transport>plane</transport>
    <board>all_inclusive</board>
    <nights>7</nights>
    <description><![CDATA[Hotel pe plajă, cu piscină și grădină.
Prețul include zborul, transferul și 7 nopți de cazare.]]></description>
    <photos>
      <photo>https://agentia-ta.ro/poze/lyra-beach-1.jpg</photo>
      <photo>https://agentia-ta.ro/poze/lyra-beach-2.jpg</photo>
    </photos>
    <departures>
      <departure>
        <date>2027-06-12</date>
        <price>589</price>
        <currency>EUR</currency>
      </departure>
      <departure>
        <date>2027-06-19</date>
        <price>629.50</price>
        <currency>EUR</currency>
      </departure>
    </departures>
  </offer>
  <offer>
    <id>CIRC-GR-07</id>
    <type>tour</type>
    <title>Circuit Grecia clasică</title>
    <destination>
      <country>Grecia</country>
    </destination>
    <transport>coach</transport>
    <board>half_board</board>
    <nights>8</nights>
    <description>Atena, Delfi, Meteora și Olimpia, cu ghid în limba română.</description>
    <photos>
      <photo>https://agentia-ta.ro/poze/meteora.jpg</photo>
    </photos>
    <departures>
      <departure>
        <date>2027-05-20</date>
        <price>2990</price>
        <currency>RON</currency>
      </departure>
    </departures>
  </offer>
</offers>