API-integraatio

Käytännön opas kopioitavine esimerkkeineen Gibbondin GraphQL-API:n integrointiin — taustajärjestelmästä, mobiili- tai verkkosovelluksesta tai toisesta myyntikanavasta. Koko skeema löytyy API-viitteestä.

Tällä sivulla
⚡

Pro-tilaus. Luo API-avain Gibbondin hallinnan API-avaimet-sivulla ja säilytä se palvelimen ympäristömuuttujassa. Alla olevissa esimerkeissä oletetaan, että GIBBOND_API_KEY sisältää WRITE-avaimen.

Näin osat liittyvät toisiinsa

Tunnistetietoja on kahdenlaisia, ja oikean valitseminen on tärkeää:

  • API-avain (glk_live_…) — tunnistaa taustajärjestelmäsi. Sillä voi lukea ja muuttaa kaikkien jäsenten tietoja, joten sitä säilytetään vain palvelimellasi. Käytä sitä palvelinten väliseen viestintään ja asiakastokenien luomiseen.
  • Asiakastoken — lyhytikäinen tunnistetieto, joka on rajattu yhteen asiakkaaseen. Taustajärjestelmäsi luo sen API-avaimella ja välittää sen asiakasohjelmalle (selaimelle tai sovellukselle). Asiakasohjelma voi tarkastella tietoja ja tehdä toimintoja vain kyseiselle asiakkaalle, joten tokenin voi välittää turvallisesti laitteelle.

Valitse integraatiotapa

Rakennat…KäytäSiirry kohtaan
Taustajärjestelmän tehtävää, ERP-synkronointia tai headless-taustapalveluaAPI-avainta suoraanTaustajärjestelmästäsi
Mobiili- tai yhden sivun sovellusta, joka näyttää jäsenelle hänen pisteensäAPI-avain palvelimella → asiakastoken laitteellaSovelluksessa
Kassajärjestelmää, markkinapaikkaa tai headless-kassaa, jossa asiakkaat ansaitsevat pisteitäAPI-avainta, awardPointsForOrderToisesta kanavasta

Pyyntöjen apufunktio

Kaikilla kutsuilla on sama muoto, joten kokoa ne yhteen apufunktioon. Tämä Node-esimerkki käyttää sisäänrakennettua fetch-funktiota (Node 18+):

// gibbond.js
const ENDPOINT = "https://app.gibbond.com/api/v1/graphql";

export async function gibbond(query, variables, token) {
  const res = await fetch(ENDPOINT, {
    method: "POST",
    headers: {
      "Authorization": "Bearer " + token,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query, variables }),
  });

  if (res.status === 429) {
    const retryAfter = Number(res.headers.get("Retry-After") || 1);
    const err = new Error("Rate limited");
    err.retryAfter = retryAfter;
    throw err;
  }

  const json = await res.json();
  if (json.errors && json.errors.length) {
    const code = json.errors[0].extensions && json.errors[0].extensions.code;
    throw new Error(code + ": " + json.errors[0].message);
  }
  return json.data;
}

Tapa 1 — taustajärjestelmästäsi (palvelinten välillä)

WRITE-avaimella voit tehdä toimintoja kaikille kauppasi jäsenille. Liitä asiakas ohjelmaan, lue hänen tietonsa ja myönnä pisteitä:

import { gibbond } from "./gibbond.js";
const KEY = process.env.GIBBOND_API_KEY;
const id = "gid://shopify/Customer/123456789";

// Enroll a customer (idempotent — safe to call again).
await gibbond(
  "mutation($id: ID!, $email: String) { enrollCustomer(customerId: $id, email: $email) { ok customer { enrolled } } }",
  { id, email: "ada@example.com" },
  KEY,
);

// Read their balance and tier.
const { customer } = await gibbond(
  "query($id: ID!) { customer(id: $id) { balance pending lifetime tier { name multiplier } } }",
  { id },
  KEY,
);

// Grant points. idempotencyKey makes retries safe — the same key never double-applies.
await gibbond(
  "mutation($id: ID!, $pts: Int!, $k: String) { awardPoints(customerId: $id, points: $pts, reason: \"Newsletter signup\", idempotencyKey: $k) { ok pointsApplied balance { balance } } }",
  { id, pts: 100, k: "newsletter-123456789" },
  KEY,
);
ℹ️

Negatiivinen määrä kutsussa awardPoints ja kutsut clawbackPoints vaativat ADMIN-avaimen — käytä näihin työnkulkuihin erillisiä, tarkasti suojattuja tunnistetietoja.

Tapa 2 — mobiili- tai verkkosovelluksessa

Älä koskaan sijoita API-avainta sovellukseen. Luo sen sijaan omalle palvelimellesi pieni päätepiste, joka luo asiakastokenin kirjautuneelle käyttäjälle, ja anna sovelluksen kutsua Gibbondia suoraan tällä tokenilla.

Mistä asiakkaan GID saadaan?

Asiakastoken sidotaan siihen GID-tunnukseen, jonka välität kutsulle mintCustomerToken — vastaat siis siitä, että luot tokenin oikealle asiakkaalle. Gibbond todentaa taustajärjestelmäsi API-avaimella, mutta ei todenna loppuasiakasta. Ennen tokenin luomista palvelimesi on jo tiedettävä luotettavasti, mikä Shopify-asiakas on kirjautunut sisään. Tämä voidaan selvittää kolmella tavalla, suositusjärjestyksessä:

Miten sovelluksesi kirjaa asiakkaan sisäänMistä GID saadaan
Shopify Customer Account API (suositeltu)Kirjaa asiakas sisään Shopifyn OAuth-pohjaisella Customer Account API -rajapinnalla. Taustajärjestelmäsi käyttää saatua tokenia varmennetun asiakkaan hakemiseen ja lukee hänen GID-tunnuksensa. Shopify allekirjoittaa henkilöllisyyden koko ketjun läpi.
Shopifyyn sisäänrakennettu käyttöliittymäJos toiminto suoritetaan Shopifyssa (teeman sovellusupotuksessa tai asiakastilin käyttöliittymälaajennuksessa), Shopify antaa jo allekirjoitetun henkilöllisyyden — sovellusvälityspalvelimen logged_in_customer_id-arvon tai asiakastilin istuntotokenin sub-kentän. Lue se palvelimella ja käytä kyseistä GID-tunnusta.
Oma kirjautumisesiOmia käyttäjätilejä käyttävä kanava (mukautettu sovellus tai kassajärjestelmä) yhdistää käyttäjän Shopify-asiakkaan GID-tunnukseen asiakkaan luonnin yhteydessä tallentamasi vastaavuuden avulla. Vastaat yhteyden oikeellisuudesta, joten suojaa tämä reitti huolellisesti.
✅

Suositus: Customer Account API. Todenna headless-kaupan tai mobiilisovelluksen asiakkaat Shopifyn Customer Account API -rajapinnalla ja hae GID Shopifyn varmennetusta tokenista. Näin koko ketju — asiakas → Shopifyn varmentama henkilöllisyys → taustajärjestelmäsi → luotu asiakastoken — on lähtökohtaisesti luotettava, eikä sinun tarvitse suojata erillistä GID-vastaavuutta. Tämä tarkistus kuuluu alla olevaan requireAuth-väliohjelmistoon.

ℹ️

Jos käyttöliittymäsi toimii jo Shopifyn istunnon sisällä (teema-, kassa- tai asiakastililohkona), et yleensä tarvitse tätä API:a — verkkokaupan sovelluslohkot ja asiakastili-integraatio varmentavat Shopify-henkilöllisyyden puolestasi. Käytä asiakastokeneita tämän yhteyden ulkopuolella.

Palvelimellasi

Oma todennuksesi kertoo, mikä Shopify-asiakas on kirjautunut sisään (hänen GID-tunnuksensa). Luo hänelle token:

import express from "express";
import { gibbond } from "./gibbond.js";

const app = express();

// requireAuth is YOUR session middleware; it sets req.customerGid.
app.post("/loyalty/session", requireAuth, async (req, res) => {
  const data = await gibbond(
    "mutation($id: ID!) { mintCustomerToken(customerId: $id, ttlSeconds: 900) { token expiresAt } }",
    { id: req.customerGid },
    process.env.GIBBOND_API_KEY, // WRITE key, server-side only
  );
  // Hand the app the token, when it expires, and the id it is bound to.
  res.json({
    token: data.mintCustomerToken.token,
    expiresAt: data.mintCustomerToken.expiresAt,
    customerId: req.customerGid,
  });
});

Sovelluksessa

Hae token palvelimeltasi ja kutsu sitten Gibbondia suoraan. Kysely me kohdistuu tokenin asiakkaaseen — id-tunnusta ei tarvita:

// 1. Get a short-lived token from YOUR backend.
const session = await fetch("/loyalty/session", { method: "POST" }).then(r => r.json());

// 2. Call Gibbond with the customer token.
async function loyalty(query, variables) {
  const res = await fetch("https://app.gibbond.com/api/v1/graphql", {
    method: "POST",
    headers: {
      "Authorization": "Bearer " + session.token,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query, variables }),
  });
  return (await res.json()).data;
}

// Show the member their points, tier, and reward codes.
const { me } = await loyalty("{ me { name balance lifetime tier { name multiplier } } }");
const { activeCodes } = await loyalty(
  "query($id: ID!) { activeCodes(customerId: $id) { code rewardName status expiresAt } }",
  { id: session.customerId },
);

renderRewardsScreen(me, activeCodes);

Palkintojen lunastaminen sovelluksesta

Asiakastokenilla voi lunastaa palkinnon vain omalle asiakkaalleen — laitteelle ei tarvita kauppiaan avainta. Mutaatio palauttaa kertakäyttöisen alennuskoodin, jota voi käyttää kassalla:

const { redeemReward } = await loyalty(
  "mutation($id: ID!, $reward: ID!) { redeemReward(customerId: $id, rewardId: $reward) { ok code { code rewardName pointsSpent expiresAt } } }",
  { id: session.customerId, reward: "gid_or_reward_id_from_rewards_query" },
);
if (redeemReward.ok) applyDiscountCode(redeemReward.code.code);
ℹ️

Tokenit ovat oletuksena voimassa 15 minuuttia (voit asettaa ttlSeconds-arvon enintään 24 tuntiin). Jos kutsu palauttaa UNAUTHENTICATED, kutsu /loyalty/session uudelleen saadaksesi uuden tokenin ja yritä uudelleen.

Tapa 3 — ansaitse pisteitä toisesta myyntikanavasta

Kun tilaus tehdään muualla kuin Shopify-kassallasi — kassajärjestelmässä, markkinapaikassa tai headless-kaupassa — ilmoita se kutsulla awardPointsForOrder. Gibbond käyttää jäsenen tasokerrointa, aktiivisia Boosts-kampanjoita ja välitavoitteita aivan kuten Shopify-tilauksessa. Kutsu on idempotentti orderId-tunnuksen suhteen, joten sen voi toistaa turvallisesti:

await gibbond(
  `mutation($input: OrderInput!) {
    awardPointsForOrder(input: $input) {
      ok
      pointsApplied
      balance { balance lifetime }
    }
  }`,
  {
    input: {
      orderId: "pos-2026-000123",
      customerId: "gid://shopify/Customer/123456789",
      customerEmail: "ada@example.com",
      orderName: "POS #000123",
      lineItems: [
        { productId: "gid://shopify/Product/55", collectionIds: ["gid://shopify/Collection/9"], lineTotal: 42.0 },
        { lineTotal: 8.5 },
      ],
    },
  },
  process.env.GIBBOND_API_KEY,
);

Jokaisella tilausrivillä on valinnainen productId, valinnaiset collectionIds ja pakollinen lineTotal — tuote- ja kokoelmakohtaiset Boosts-kampanjat sekä poissulkemiset toimivat siis kuten Shopify-tilauksissa. Vahvista pisteet myöhemmin (esim. tilauksen käsittelyn jälkeen) kutsulla confirmPoints(customerId, orderId); peru pisteet hyvityksen yhteydessä kutsumalla clawbackPoints ADMIN-avaimella.

Virheet, uudelleenyritykset ja idempotenssi

  • Koodit. Virheet palautetaan GraphQL-taulukossa errors; lue errors[0].extensions.code — UNAUTHENTICATED (401), FORBIDDEN (403, väärä käyttöoikeusalue), RATE_LIMITED (429), BAD_USER_INPUT (400), NOT_FOUND (404).
  • Pyyntörajoitukset. 600 pyyntöä minuutissa kutakin tunnistetietoa kohden. Kun saat vastauksen 429, odota otsakkeessa Retry-After ilmoitettu sekuntimäärä ja yritä sitten uudelleen.
  • Idempotenssi. Välitä idempotencyKey kutsulle awardPoints ja pysyvä orderId kutsulle awardPointsForOrder. Samaa avainta tai tilausta ei koskaan sovelleta kahdesti — voit siis yrittää verkkovirheiden jälkeen uudelleen.
  • Tokenin vanheneminen. Jos asiakastoken palauttaa UNAUTHENTICATED, luo uusi token ja yritä uudelleen.

Turvallisuuden tarkistuslista

  • Säilytä API-avaimia vain palvelimen salaisuuksina — älä koskaan sovelluspaketeissa, selainkoodissa tai versionhallinnassa.
  • Käytä asiakastokenia kaikessa laitteella suoritettavassa toiminnassa; älä koskaan API-avainta.
  • Anna kullekin integraatiolle pienin tarvittava käyttöoikeusalue (READ-avain koontinäyttöihin, WRITE pisteiden ansaitsemiseen ja lunastamiseen, ADMIN vain hyvityksiin ja korjauksiin).
  • Vaihda avain hallinnassa heti, jos se on voinut vuotaa — mitätöinti tulee voimaan välittömästi.
  • Luo asiakastokenit mahdollisimman lyhyellä käytännöllisellä ttlSeconds-arvolla.

Koko luettelo tyypeistä, kyselyistä, mutaatioista ja kentistä löytyy API-viitteestä.