Documentação

Receitas

Servidor Node/Express completo

Token, eventos e recebedor de webhook com verificação de assinatura.

js
import crypto from "node:crypto";
import express from "express";

const API = "https://api.iclubing.com";
const KEY = process.env.ICLUBING_KEY;
const SEGREDO = process.env.ICLUBING_WEBHOOK_SECRET;
const app = express();

async function iclubing(path, body, extra = {}) {
  const r = await fetch(API + path, {
    method: body ? "POST" : "GET",
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json", ...extra },
    body: body ? JSON.stringify(body) : undefined,
  });
  if (!r.ok) throw new Error(`iClubing ${r.status}: ${await r.text()}`);
  return r.json();
}

// 1) token para o clube abrir logado
app.post("/clube/token", async (req, res) => {
  res.json(await iclubing(`/v1/participants/${encodeURIComponent(req.user.id)}/token`, {}));
});

// 2) evento a partir do seu fluxo de negócio
export async function depositoConfirmado(deposito) {
  await iclubing("/v1/events", {
    id: `dep:${deposito.id}`,
    participant: deposito.userId,
    type: "deposit.confirmed",
    amount: deposito.valor,
  });
}

// 3) webhook: corpo CRU para conferir a assinatura
app.post("/webhooks/iclubing", express.raw({ type: "application/json" }), async (req, res) => {
  const corpo = req.body.toString("utf8");
  const p = Object.fromEntries(String(req.get("X-Clube-Signature")).split(",").map((x) => x.split("=")));
  const esperado = crypto.createHmac("sha256", SEGREDO).update(`${p.t}.${corpo}`).digest("hex");
  if (esperado !== p.v1 || Math.abs(Date.now() / 1000 - p.t) > 300) return res.sendStatus(401);

  const ev = JSON.parse(corpo);
  if (ev.type === "reward.delivery_requested") {
    const { delivery, reward, participant } = ev.data;
    try {
      await carteira.creditarPremio(participant.external_id, reward.payload, { ref: delivery.id });
      await iclubing(`/v1/deliveries/${delivery.id}/confirm`, { external_ref: delivery.id });
    } catch (e) {
      await iclubing(`/v1/deliveries/${delivery.id}/fail`, { reason: String(e.message).slice(0, 500) });
    }
  }
  res.sendStatus(200);
});