Hopp til innhold
skribbskribb

Utviklere

Hold lageret i sync

Et annet system eier beholdningen. skribb følger etter uten å overskrive noe eller selge noe du ikke har.
Alle sidene

Nøkkelen trenger

products:readproducts:write

Huk av for disse når du lager nøkkelen. Mangler én, svarer ruta 403 og sier hvilken. Lag en nøkkel →

Oppsettet

Utgangspunktet er at et annet system er fasiten. Kassa, lagersystemet eller regnearket vet hva som står på hylla, og skribb skal følge etter. Da er jobben å flytte endringer én vei, og å oppdage når de to har kommet i utakt.

To ting må stemme før noe av dette virker. Produktet må være fysisk: digitale produkter har ikke lager, og et forsøk svarer 409 not-a-physical-product. Og produktet må ha lagerstyring i det hele tatt. Et produkt med inventory: null selges uten å telle ned, og et forsøk på å justere det svarer 409 inventory-not-tracked. Du slår det på ved å sette et tall.

Slå på lagerstyring for et produkt
curl -X PATCH https://api.skribb.no/v1/products/prod_8kQ2vX/stock \
  -H "Authorization: Bearer skribb_sk_…" \
  -H "Content-Type: application/json" \
  -d '{"set": 42}'

Bruk delta, ikke set

set skriver et absolutt tall. delta flytter beholdningen relativt til det den er nå.

Selger butikken tre krus i samme øyeblikk som lagersystemet ditt melder at det kom inn ti, kommer to oppdateringer inn nesten samtidig. Med delta blir begge med, og beholdningen ender riktig uansett hvilken rekkefølge de lander i. Med set vinner den siste, og den andre forsvinner uten et ord. Det er den samme feilen som å lese et tall, regne på det og skrive det tilbake.

Bruk set bare når du mener «uansett hva som står der nå skal det være dette»: ved en opptelling, eller når du kobler et produkt til for første gang. {"set": null} slår lagerstyringen av igjen.

Avvik i lageret

Et trekk som ville gjort beholdningen negativ, blir avvist med 409. Det er ikke en feil du skal prøve igjen på, for den kan aldri gå. Det er de to systemene som er uenige om hva som står på hylla.

Svaret bærer inventory med beholdningen slik den faktisk er hos oss, så du kan avstemme uten et ekstra kall. Som regel er det riktige å skrive ditt eget tall med set og logge at de var i utakt.

Svaret du får
{
  "error": "would-go-negative",
  "message": "Trekket ville gjort beholdningen negativ. Svaret sier hva den faktisk er, så du kan avstemme.",
  "docs": "https://skribb.no/utviklere/referanse/produkter",
  "inventory": 2
}

Hent bare det som er endret

Den andre veien, altså å oppdage at noe er solgt i butikken, gjør du ved å hente produktene som har endret seg siden sist. Du trenger ikke hente alle hver gang: updated_since gir deg bare de endrede, og updated_at på hvert produkt er verdien du tar vare på til neste runde.

Lista er paginert. Er has_more sann, sender du next_cursor som ?cursor= for å få neste side. Cursoren peker på raden du sist så, så produkter som endres mens du blar, verken hopper over eller gjentar seg.

En full runde
const KEY = process.env.SKRIBB_KEY;
const base = "https://api.skribb.no/v1";

async function api(path, init) {
  const res = await fetch(base + path, {
    ...init,
    headers: {
      Authorization: `Bearer ${KEY}`,
      ...(init?.body ? { "Content-Type": "application/json" } : {}),
      ...init?.headers,
    },
  });
  const body = await res.json();
  if (!res.ok) throw Object.assign(new Error(body.message), { body, status: res.status });
  return body;
}

// 1. Hva har endret seg hos oss siden sist?
async function* changedSince(iso) {
  let cursor = null;
  do {
    const qs = new URLSearchParams({ updated_since: iso, limit: "200" });
    if (cursor) qs.set("cursor", cursor);
    const page = await api(`/products?${qs}`);
    yield* page.products;
    cursor = page.has_more ? page.next_cursor : null;
  } while (cursor);
}

// 2. Flytt en endring fra ditt system til skribb.
async function move(productId, delta) {
  try {
    return await api(`/products/${productId}/stock`, {
      method: "PATCH",
      body: JSON.stringify({ delta }),
    });
  } catch (err) {
    if (err.body?.error !== "would-go-negative") throw err;
    // Vi er i utakt. Vårt tall er fasiten, så skriv det og logg avviket.
    console.warn("i utakt", { productId, skribb: err.body.inventory });
    return await api(`/products/${productId}/stock`, {
      method: "PATCH",
      body: JSON.stringify({ set: yourStockFor(productId) }),
    });
  }
}

Verdt å vite

Et produkt du har tatt ut av butikken er fortsatt i lista, med is_active: false. Vi lar raden stå, for tidligere bestillinger skal fortsatt vise hva som ble kjøpt. Filtrer det bort hvis synkroniseringen din bare bryr seg om det som er til salgs.

Grensa er rundt 120 forespørsler i minuttet per nøkkel. En runde som henter 200 av gangen og bare skriver det som er endret, kommer ingen steder i nærheten. En runde som skriver hvert produkt hver gang, gjør det fort.

Skal du reagere med det samme et salg skjer, i stedet for å vente på neste runde, er order.paid som webhook den riktige veien. Da får du varelinjene i det de blir betalt.

Videre

Referansen for det denne guiden bruker: