Zum Inhalt springen

Anleitungen

Anleitung: Abgleich mit anderer Software

Ein verlässlicher Abgleich hat drei Teile: eine vollständige Erstbefüllung, regelmäßige Deltas und Webhooks, die das Delta früher auslösen. Schreibzugriffe zurück nach STRYK nutzen ETags, damit du keine Änderung der Rezeption überschreibst.

1. Erstbefüllung

Lies alle Seiten jeder Liste mit dem maximalen limit von 200. Listen sind nach updatedAt, dann id sortiert; der Cursor bleibt stabil, während sich Daten ändern. Merke dir je Ressource das neueste gesehene updatedAt, deine Hochwassermarke.

import { stryk } from './stryk.js';

// Initial load: walk every page, then remember the newest updatedAt you saw.
export async function fullLoad(resource, upsert) {
  let cursor = null;
  let highWaterMark = null;
  do {
    const params = new URLSearchParams({ limit: '200', ...(cursor ? { cursor } : {}) });
    const { data: page } = await stryk(`/${resource}?${params}`);
    for (const item of page.data) {
      await upsert(item);
      if (!highWaterMark || item.updatedAt > highWaterMark) highWaterMark = item.updatedAt;
    }
    cursor = page.hasMore ? page.nextCursor : null;
  } while (cursor);
  return highWaterMark; // store it per resource, e.g. in your database
}

2. Deltas mit updated_since

updated_since liefert alles mit updatedAt größer oder gleich dem angegebenen Zeitpunkt. Durch den inklusiven Vergleich geht nichts verloren, was in derselben Millisekunde geändert wurde; dafür kommen Einträge an der Marke erneut. Dedupliziere per id und updatedAt.

// Delta run: updated_since is inclusive (>=). Items at the mark arrive again, that is intended.
export async function syncChanges(resource, store) {
  const since = await store.highWaterMark(resource);
  let cursor = null;
  let newest = since;
  do {
    const params = new URLSearchParams({ limit: '200', updated_since: since, ...(cursor ? { cursor } : {}) });
    const { data: page } = await stryk(`/${resource}?${params}`);
    for (const item of page.data) {
      const known = await store.get(resource, item.id);
      // Deduplicate by id + updatedAt; never overwrite newer local data with an older copy.
      if (!known || item.updatedAt > known.updatedAt) await store.put(resource, item);
      if (item.updatedAt > newest) newest = item.updatedAt;
    }
    cursor = page.hasMore ? page.nextCursor : null;
  } while (cursor);
  await store.setHighWaterMark(resource, newest);
}

3. Webhooks als Auslöser

Webhooks verkürzen die Verzögerung: Bei booking.updated liest du diese Buchung oder startest das Delta für Buchungen. Baue keinen Zustand allein aus Ereignissen auf, Zustellungen können verspätet, doppelt oder nach Ende der Wiederholungen ganz ausbleiben.

EreignisNeu lesen
booking.created, booking.updated, booking.cancelledGET /bookings/{id}
guest.updatedGET /guests/{id}
availability.changedGET /availability für roomTypeIds, from, to
rate.changedGET /rates für denselben Zeitraum
payment.updatedGET /bookings/{bookingId}/payments mit data.bookingId

4. Regelmäßiger Abgleich

Lass das Delta zeitgesteuert laufen, auch wenn Webhooks funktionieren, für Buchungen typischerweise alle 5 bis 15 Minuten, für Kategorien und Raten stündlich. Eine nächtliche Erstbefüllung der gespiegelten Ressourcen fängt den Rest ab, etwa Löschungen in deinem eigenen System.

Bleibe unter dem Limit deines Schlüssels (standardmäßig 120 Anfragen je Minute, GET /me → rateLimit). Mit 200 Einträgen je Seite braucht ein Delta über Tausende Änderungen nur wenige Anfragen; eine Erstbefüllung verteilst du über einige Minuten. Siehe Rate-Limits.

5. Schreiben mit ETag und If-Match

Einzelressourcen liefern einen ETag-Header und denselben Wert als version. PATCH verlangt If-Match mit dem gelesenen ETag. Hat sich die Ressource inzwischen in STRYK geändert, wird der Schreibzugriff mit 412 precondition_failed abgelehnt; ohne If-Match erhältst du 428 precondition_required. If-None-Match bei einem GET liefert 304, wenn sich nichts geändert hat.

HTTP
GET /api/partner/v1/guests/5e4d3c2b-1a09-4f8e-9d7c-6b5a4f3e2d1c
→ 200 OK
  ETag: "7e5d3c1b"
  { "id": "5e4d3c2b-…", "phone": "+43 660 1234567", "version": "7e5d3c1b", … }

PATCH /api/partner/v1/guests/5e4d3c2b-1a09-4f8e-9d7c-6b5a4f3e2d1c
If-Match: "7e5d3c1b"
{ "phone": "+43 660 7654321" }
→ 200 OK      ETag: "9f8e7d6c"
→ 412 precondition_failed   (changed in STRYK meanwhile: read again)
→ 428 precondition_required (If-Match missing)
// Optimistic concurrency: read, change, write with If-Match.
export async function updateGuestPhone(guestId, phone) {
  for (let attempt = 1; attempt <= 3; attempt += 1) {
    const { data: guest, etag } = await stryk(`/guests/${guestId}`);
    try {
      const { data: updated } = await stryk(`/guests/${guestId}`, {
        method: 'PATCH',
        headers: { 'If-Match': etag },
        body: { phone },
      });
      return updated;
    } catch (error) {
      // 412: someone changed the guest in STRYK since your read. Read again and re-apply.
      if (error.code !== 'precondition_failed') throw error;
    }
  }
  throw new Error('Guest keeps changing; resolve manually.');
}

6. Konflikte lösen

  • STRYK ist führend für Buchungen, Preise, Verfügbarkeit und Zahlungen. Spiegle diese Daten; korrigiere sie nicht von außen.
  • Für Gastkontaktdaten legst du je Feld fest, welches System gewinnt. Bei 412 liest du neu, übernimmst nur deine Felder und schreibst mit dem neuen ETag.
  • Brich nach wenigen Versuchen ab und markiere den Datensatz für eine Person, statt endlos zu wiederholen.
  • Wiederhole 409 booking_not_modifiable nie: Die Buchung ist eingecheckt, ausgecheckt oder storniert.