Integração da API

Guia prático para integrar a API GraphQL do Gibbond, com exemplos para copiar e colar em serviços internos, apps móveis ou web e outros canais de venda. Consulta a referência da API para veres o esquema completo.

Nesta página
⚡

Plano Pro. Cria uma chave de API na página Chaves API do admin do Gibbond e guarda-a numa variável de ambiente do servidor. Os exemplos seguintes pressupõem que GIBBOND_API_KEY contém uma chave com âmbito WRITE.

Como as peças se encaixam

Existem dois tipos de credenciais; escolher a certa é essencial:

  • Chave de API (glk_live_…) — identifica o teu backend. Pode ler e alterar os dados de todos os membros, por isso deve ficar apenas no servidor. Usa-a nas operações entre servidores e para criar tokens de cliente.
  • Token de cliente — uma credencial de curta duração associada a um único cliente. O teu backend gera-o com a chave de API e entrega-o a um cliente (navegador ou app). Este só pode consultar os dados e agir em nome dessa pessoa, por isso o token pode ser enviado para o dispositivo em segurança.

Escolhe o padrão de integração

Estás a criar…UsaIr para
Um processo interno, uma sincronização com ERP ou um backend headlessChave de API diretamenteDo teu backend
Uma app móvel ou de página única que mostra os pontos de um membroChave de API no teu servidor → token de cliente no dispositivoNuma app
Um POS, marketplace ou checkout headless que deve atribuir pontosChave de API, awardPointsForOrderDe outro canal

Função auxiliar para pedidos

Todas as chamadas seguem a mesma estrutura, por isso encapsula-as numa função. Este exemplo de Node usa o fetch integrado (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;
}

Padrão 1 — Do teu backend (servidor a servidor)

Com uma chave WRITE, podes agir em nome de qualquer membro da tua loja. Inscreve um cliente, consulta os dados e atribui-lhe pontos:

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,
);
ℹ️

Um valor negativo em awardPoints, assim como clawbackPoints, requer uma chave ADMIN. Mantém esses processos numa credencial separada e bem protegida.

Padrão 2 — Numa app móvel ou web

Nunca coloques a chave de API numa app. Em vez disso, disponibiliza um endpoint simples no teu servidor que gere um token de cliente para o utilizador autenticado. Assim, a app pode comunicar diretamente com o Gibbond através desse token.

De onde vem o GID do cliente?

Um token de cliente fica associado ao GID indicado em mintCustomerToken — por isso, és responsável por o gerar para o cliente certo. O Gibbond autentica o teu backend através da chave de API, mas não autentica o cliente final. Antes de gerar o token, o teu servidor já tem de saber e confirmar que cliente Shopify iniciou sessão. Existem três formas de o determinar, por ordem de preferência:

Como a app autentica o clienteOrigem do GID
Shopify Customer Account API (recomendada)Autentica o cliente através da Customer Account API da Shopify, baseada em OAuth. O teu backend troca o token obtido pela identidade do cliente verificado e lê o GID. A Shopify assina a identidade de ponta a ponta.
Uma interface nativa da ShopifySe a experiência decorrer dentro da Shopify (através de um app embed do tema ou de uma extensão da interface da conta de cliente), a Shopify já fornece a identidade assinada: logged_in_customer_id do app proxy ou sub do token de sessão da conta de cliente. Lê esse valor no servidor e usa-o como GID.
O teu próprio início de sessãoUm canal com contas próprias (uma app personalizada ou um POS) associa o utilizador ao GID de um cliente Shopify através de um mapeamento guardado quando o cliente foi criado. És responsável por essa ligação, por isso protege-a cuidadosamente.
✅

Recomendação: Customer Account API. Numa loja headless ou app móvel, autentica os clientes através da Customer Account API da Shopify e obtém o GID a partir do token verificado pela Shopify. Assim, toda a cadeia — cliente → identidade verificada pela Shopify → backend → token de cliente — é fiável desde a origem, sem precisares de proteger um mapeamento de GID. O middleware requireAuth abaixo é o local certo para fazer esta verificação.

ℹ️

Se a tua interface já funciona dentro de uma sessão Shopify (bloco do tema, checkout ou conta de cliente), normalmente não precisas desta API: os blocos de app da loja e a integração da conta de cliente verificam a identidade Shopify por ti. Usa tokens de cliente quando estiveres fora desse contexto.

No teu servidor

O teu sistema de autenticação indica que cliente Shopify tem sessão iniciada (através do GID). Gera um token para esse cliente:

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,
  });
});

Na app

Obtém um token do teu servidor e chama diretamente o Gibbond. A consulta me devolve o cliente associado ao token, pelo que não é necessário indicar um ID:

// 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);

Resgatar na app

Com um token de cliente, é possível resgatar uma recompensa para si próprio, sem uma chave do comerciante no dispositivo. A mutação devolve um código de desconto de utilização única para aplicar no checkout:

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);
ℹ️

Por predefinição, os tokens duram 15 minutos (define ttlSeconds até 24h). Quando uma chamada devolve UNAUTHENTICATED, volta a chamar /loyalty/session para obter um novo token e tenta novamente.

Padrão 3 — Atribuir pontos noutro canal de venda

Quando uma encomenda for feita fora do checkout da Shopify — num POS, marketplace ou loja headless — regista-a com awardPointsForOrder. O Gibbond aplica o multiplicador de nível, os boosts ativos e os marcos tal como numa encomenda nativa. orderId torna seguro voltar a tentar:

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,
);

Cada linha pode incluir productId e collectionIds opcionais, bem como um lineTotal obrigatório. Assim, boosts e exclusões por produto e coleção também se aplicam às encomendas Shopify. Para confirmar os pontos mais tarde (por exemplo, após o processamento da encomenda), chama confirmPoints(customerId, orderId); para os reverter em caso de reembolso, chama clawbackPoints com uma chave ADMIN.

Erros, novas tentativas e idempotência

  • Códigos. Os erros são devolvidos num array GraphQL errors; consulta errors[0].extensions.code: UNAUTHENTICATED (401), FORBIDDEN (403, âmbito incorreto), RATE_LIMITED (429), BAD_USER_INPUT (400), NOT_FOUND (404).
  • Limites de pedidos. 600 pedidos por minuto por credencial. Se receberes 429, aguarda os segundos indicados no cabeçalho Retry-After e volta a tentar.
  • Idempotência. Passa uma idempotencyKey a awardPoints e um orderId estável a awardPointsForOrder. Reenviar a mesma chave ou encomenda não atribui os pontos uma segunda vez, pelo que podes voltar a tentar em caso de falha de rede.
  • Expiração do token. Se receberes UNAUTHENTICATED com um token de cliente, cria outro e volta a tentar.

Lista de verificação de segurança

  • Guarda as chaves de API apenas em segredos do servidor — nunca em bundles da app, código do navegador ou controlo de versões.
  • Usa um token de cliente para tudo o que é executado num dispositivo; nunca uses uma chave de API.
  • Atribui a cada integração o âmbito mínimo necessário (uma chave READ para dashboards, WRITE para acumular/resgatar pontos e ADMIN apenas para reembolsos/correções).
  • Se uma chave puder ter sido exposta, troca-a de imediato no admin — a revogação é imediata.
  • Cria tokens de cliente com o valor mais curto possível de ttlSeconds.

Para veres a lista completa de tipos, consultas, mutações e campos, consulta a referência da API.