Un răspuns lent nu trebuie să producă o a doua comandă. Păstrează un ID și un corp de cerere persistente pentru fiecare achiziție și diferențiază o eroare de validare cunoscută de un rezultat incert.
Protocolul de reîncercare
503, comanda inițială poate exista deja în Retail. Un ID nou poate crea un duplicat.- Înainte de prima trimitere, salvează atomic conexiunea,
external_order_idși cererea completă într-o coadă persistentă de ieșire. - La
200sau201, salvează confirmarea primirii. Folosește GET pentru starea curentă. - La o eroare de rețea sau
503, păstrează cererea ca neconfirmată și reîncearcă fără modificări. RespectăRetry-After; gateway-ul trimite5secunde în răspunsurile503. - Folosește intervale de reîncercare exponențiale, limitate, cu variație aleatorie. Păstrează cererea după expirarea ferestrei de reîncercare și semnaleaz-o pentru reconciliere; nu o elimina tacit.
- Alte erori neașteptate ale serverului/proxy-ului pot avea și ele rezultate incerte. Păstrează identitatea inițială și investighează sau reîncearcă potrivit politicii tale de recuperare.
saved = persist_once(purchase, connection, external_order_id, full_request)
send saved.request to saved.connection
if response is 200 or 201:
mark intake confirmed
handle signed webhook updates
reconcile with GET after gaps; poll only without webhooks
else if response is 429:
retry saved.request after Retry-After plus jitter
else if timeout, network failure, or 503:
keep unconfirmed
retry saved.request after backoff
else:
apply the error-specific recovery below
never create another purchase ID just because a response was lost
Gateway-ul rezervă persistent conținutul validat înainte de trimitere. Reîncercările identice refolosesc starea inițială chiar și după modificarea meniului sau închidere. După rezervare, ID-ul nu poate fi folosit pentru alt coș, client, referință de plată sau observație. Nu există un antet de idempotență configurabil; identitatea este external_order_id.
Răspunsuri HTTP și structura corpului
| HTTP | Semnificație | Acțiune |
|---|---|---|
| 400 | connection_required | Selectează o conexiune activată din lista de conexiuni și include ID-ul în rută. Nu s-a creat nicio comandă. |
| 404 | connection_not_found | Conexiunea selectată nu este disponibilă pentru această cheie de tenant. Actualizează lista; nu muta niciodată o comandă cu rezultat incert la alt POS. |
401 | Cheie absentă, invalidă sau cu rol greșit. | Verifică credențialele și mediul. Repetarea unei chei invalide nu ajută. |
404 | Meniu nepublicat sau ID de comandă necunoscut acestei conexiuni. | Verifică resursa și conexiunea; contactează restaurantul pentru publicarea meniului. |
409 | Conflict cu regulile de business. | Citește error și folosește tabelul de mai jos. |
413 | Corpul depășește 4 MiB. | Redu dimensiunea cererii. |
422 | Validarea schemei sau a condițiilor a eșuat. | Corectează câmpurile. Nu reîncerca o cerere invalidă fără modificări. |
429 | Limita de cereri a fost depășită. | Așteaptă secundele din Retry-After, adaugă o întârziere aleatoare și repetă ID-ul și corpul inițial. Vezi Limite de cereri mai jos. |
503 | Retail sau stocarea nu sunt disponibile; rezultatul trimiterii este neconfirmat. | Respectă Retry-After. Reîncearcă folosind ID-ul și corpul inițiale. |
Citește codul din error sau detail. Autentificarea și resursele negăsite folosesc de obicei detail; conflictele de preț folosesc error. Verificările eligibilității acțiunilor pot întoarce oricare dintre forme, deci tratează-le pe ambele. Validarea include căi și tipuri de câmp fără a repeta datele clientului.
{ "error": "total_mismatch" }{ "detail": "invalid_credentials" }{
"error": "invalid_request",
"fields": [{ "path": ["body", "total"], "type": "int_type" }]
}{ "error": "retail_unavailable", "retry_same_order_id": true }storage_unavailable este tot un 503, cu {"error":"storage_unavailable"} și aceleași recomandări de reîncercare. Păstrează identitatea inițială chiar dacă retry_same_order_id lipsește. Proxy-urile inverse pot întoarce erori non-JSON; tratează starea HTTP și erorile de transport înainte de a interpreta corpul JSON.
Coduri de conflict
| Cod | Recuperare |
|---|---|
menu_not_published | Cere restaurantului să publice meniul; citirea meniului folosește 404, trimiterea comenzii folosește 409. |
menu_changed | Actualizează meniul și reconfirmă coșul cu clientul. |
restaurant_closed | Dezactivează finalizarea comenzilor noi. Nu ocoli închiderea folosind altă conexiune. |
product_unavailable | Actualizează meniul și elimină/înlocuiește produsul indisponibil cu acordul clientului. |
unknown_modifier | Folosește ID-urile de grup și opțiune din produsul selectat. |
duplicate_modifier | Trimite fiecare pereche grup/opțiune o singură dată pe linie. |
variant_unavailable | Selectează o combinație de variantă exactă, publicată și disponibilă. |
modifier_selection_invalid | Respectă numărul minim și maxim de selecții al fiecărui grup. |
modifier_unavailable | Alege o opțiune disponibilă după actualizarea meniului. |
total_mismatch | Recalculează în bani întregi prețul variantei, promoțiile publicate, cuponul, garanțiile, extra, ambalajele, bacșișul și taxa de livrare. |
order_id_already_used | Acest ID a fost rezervat cu alt conținut. Recuperează cererea inițială salvată; investighează înainte de a crea o achiziție cu adevărat nouă. |
publication_superseded | Doar pentru publicarea internă: a sosit deja o publicare mai nouă sau această generație a fost folosită cu alt conținut. Păstrează cea mai recentă publicare din coadă. |
revision_already_used | Doar pentru publicare: modificarea conținutului meniului necesită o revizie nouă. |
O cerere respinsă la validarea inițială nu este rezervată. Poți corecta cererea pentru aceeași achiziție. Dacă a existat anterior o încercare cu rezultat incert, reconciliază mai întâi corpul inițial; nu presupune că un conflict ulterior a șters comanda anterioară.
Furnizează date utile pentru diagnosticare
Pentru asistență, furnizează mediul, ID-ul conexiunii din URL, ID-ul extern al comenzii, data și ora, endpointul, starea HTTP și codul erorii. Elimină cheile, antetele de autorizare, numele, telefoanele, adresele și detaliile de plată ale clienților. Nu înregistra corpuri complete de cereri sau excepții din sistemele destinație.
scheduled_time_unavailable: Trimite un marcaj temporal valid cu offset de fus orar explicit.cancellation_not_allowed: Comanda este în afara intervalului de anulare de către client sau nu mai are o stare eligibilă. Comenzile imediate trebuie să aștepte acceptarea; cele programate acceptate trebuie să aștepte prepararea și să fie strict înaintea orei salvate de începere. Citește starea și contactează restaurantul. Existența unui bon nu blochează singură anularea unei comenzi programate altfel eligibile.cancellation_conflict: A fost confirmată o altă anulare. Reconciliază folosind starea comenzii.
Actualizarea livrării
delivery_update_not_allowed: modalitate de primire sau stare incompatibilă; citește starea curentă înainte de a reîncerca. delivery_update_conflict: ID-ul cererii aparține deja altui corp. delivery_update_limit: comanda a ajuns la 100 de actualizări distincte ale livrării. Aceste erori întorc 409.
Rapoarte de rambursare
refund_report_not_allowed: comanda inițială nu era plătită online. refund_report_conflict: ID-ul cererii sau referința procesatorului a fost reutilizată. refund_amount_exceeded: suma depășește totalul comenzii sau suma rămasă rambursabilă. refund_report_limit: există deja 100 de rapoarte. Toate întorc 409; nu efectua o nouă rambursare doar pentru a rezolva o eroare de raportare.
tips_unavailable (409): meniul publicat nu permite bacșișuri pentru personal. Elimină bacșișul doar cu acordul clientului și recalculează înainte de trimiterea unei noi achiziții.
invalid_checkout_discount: Produse eligibile necunoscute sau repetate, ori sumă peste prețurile eligibile. Corectează cuponul înainte de trimiterea unei comenzi noi.
Urmărirea cererilor
Răspunsurile gateway-ului pentru cererile /v1/ procesate includ X-Request-ID. Păstrați acest antet împreună cu ID-ul conexiunii și ID-ul extern al comenzii când raportați o problemă. Fiecare încercare HTTP primește un ID nou, inclusiv reîncercările; acesta nu este o cheie de idempotență. La reîncercare, păstrați ID-ul extern și corpul original al cererii. O eroare de rețea sau un răspuns generat de proxy poate să nu conțină un ID de cerere.
Limite de cereri
Gateway-ul aplică direct limitele, pentru toate procesele care folosesc aceeași bază de date locală. Pentru fiecare tenant, citirile website (GET/HEAD/OPTIONS) se reîncarcă la 600 de cereri/minut, cu un vârf de 100; scrierile la 120/minut, cu un vârf de 30. Toate punctele de vânzare împart aceste bugete. Publicarea din Retail are un buget separat per tenant de 120/minut, cu un vârf de 30. Înlocuirea cheii nu resetează bugetele tenantului.
Toate cererile /v1/ împart și un buget de intrare de 6.000/minut, cu un vârf de 1.000, inclusiv cererile cu autentificare invalidă sau conținut incorect. Verificarea are loc înainte de citirea corpului cererii. Bugetele se reîncarcă permanent, nu la începutul fiecărui minut. Documentația, verificările de sănătate și livrările webhook nu sunt incluse. Limitele se păstrează la repornire; bazele de date independente nu împart contoarele.
Cererile peste limită primesc 429, {"error":"rate_limit_exceeded"} și Retry-After în secunde. Așteaptă cel puțin atât, adaugă o întârziere aleatoare, apoi repetă aceeași cerere cu ID-ul original al comenzii/cererii și același corp. O încercare limitată nu este trimisă către Retail. Alt client poate consuma următorul token, deci și reîncercarea poate primi 429. Folosește webhook-uri pentru actualizări și evită interogările frecvente. Dacă stocarea limitatorului nu este disponibilă, cererile sunt blocate cu 503 și Retry-After: 5.