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âmp | Obligatoriu | Ce conține |
|---|---|---|
id | da | Codul ofertei la agenție, de cel mult 100 de caractere. Nu se schimbă de la o zi la alta: după el recunoaștem oferta. |
type | da | Tipul ofertei, vezi valorile de mai jos. |
title | da | Titlul ofertei, de cel mult 255 de caractere. |
destination | da | country (țara), apoi, dacă se poate, region (regiunea) și resort (stațiunea sau orașul). La circuit ajunge țara. |
accommodation | da, în afară de circuit | name (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. |
transport | da | Cum ajunge turistul, vezi valorile de mai jos. |
board | da | Ce mese sunt incluse, vezi valorile de mai jos. |
nights | da | Numărul de nopți, de la 1 la 365. |
description | da | Descrierea ofertei, ca text. Poate avea HTML simplu, pe care îl transformăm în text. |
photos | nu | Adresele pozelor (cel mult 30), care încep cu http:// sau https://. Prima e poza principală. |
departures | da | Plecă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: sejurtour: circuitaccommodation_only: doar cazare
transport
plane: avioncoach: autocartrain: trenown: transport propriu
board
none: fără masăbreakfast: mic dejunhalf_board: demipensiunefull_board: pensiune completăall_inclusive: all inclusive
currency
RON: leiEUR: €
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,idpoate 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.
| Cerere | Ce face | Ră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/offers | Lista 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 (laDELETE).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
{
"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
<?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>