Workflows típicos

Receitas práticas pra integrações comuns na PaysClub. Cada workflow tem o fluxo de eventos, código de exemplo e os pontos de atenção.

1. Sincronizar membros com seu banco de dados

Toda vez que um novo membro recebe acesso a um produto, o eventomember.enrolled é disparado. Use isso pra manter seu DB de usuários em sincronia.

Receiver Node.js (Express)
import crypto from "crypto";
import express from "express";

const app = express();
app.use(express.raw({ type: "application/json" }));

app.post("/webhooks/paysclub", async (req, res) => {
  // 1. Valida assinatura HMAC
  const signature = req.headers["x-paysclub-signature"];
  const expected = "sha256=" + crypto
    .createHmac("sha256", process.env.PAYSCLUB_WEBHOOK_SECRET)
    .update(req.body)
    .digest("hex");

  if (signature !== expected) return res.status(401).send("Invalid signature");

  const { event, data } = JSON.parse(req.body.toString());

  // 2. Roteia por evento
  if (event === "member.enrolled") {
    await db.users.upsert({
      id: data.buyer_id,
      product_ids: { push: data.product_id },
      enrolled_at: new Date(),
    });
  }

  if (event === "access.revoked") {
    await db.users.update({
      where: { id: data.buyer_id },
      data: { product_ids: { remove: data.product_id } },
    });
  }

  // 3. Responde 200 RAPIDAMENTE — gateway tem timeout 10s
  res.status(200).send("ok");
});
Idempotency: webhooks podem ser entregues múltiplas vezes em falha de rede. Use o order_id + event como chave única pra deduplicar. PaysClub tenta a entrega até 5 vezes com backoff exponencial.

2. Vender via API key (sem checkout hospedado)

Para integrações headless onde você quer total controle do UX, crie a sessão de checkout via API e renderize sua própria UI. Use o session_idretornado para gerar o link único de pagamento.

Server-side: criar sessão
const res = await fetch("https://app.paysclub.com/api/checkout/sessions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer pk_live_seu_api_key",
  },
  body: JSON.stringify({
    product_id: "550e8400-...",
    plan_id: "660e8400-...",
    email: "comprador@email.com",
  }),
});

const { session_id, session_token } = await res.json();

// Redirect pro checkout com a sessão pré-criada
const checkoutUrl = `https://compra.paysclub.com/checkout/seu-slug?session=${session_id}`;
return Response.redirect(checkoutUrl, 302);

3. Processar reembolso programaticamente

Reembolsos podem ser disparados via API quando o cliente entra em contato com seu suporte. O fluxo é assíncrono — você dispara o refund e aguarda o webhook confirmar.

POST /api/v1/orders/{id}/refund
curl -X POST https://app.paysclub.com/api/v1/orders/ORDER_ID/refund \
  -H "Authorization: Bearer pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Cliente solicitou reembolso por insatisfação" }'

# Resposta imediata (status: pending)
{
  "id": "...",
  "status": "pending",
  "created_at": "2026-04-20T..."
}

Após processamento, você recebe o webhook:

Webhook refund.requested
{
  "event": "refund.requested",
  "data": {
    "refund_id": "...",
    "order_id": "...",
    "amount": 97.00,
    "reason": "Cliente solicitou reembolso..."
  }
}

4. Rastrear comissões de afiliados

Quando uma venda vem por link de afiliado, o eventoaffiliate.commission_earned é disparado após confirmação de pagamento. Use pra dashboards externos ou notificações.

Payload
{
  "event": "affiliate.commission_earned",
  "data": {
    "affiliate_id": "...",
    "order_id": "...",
    "product_id": "...",
    "commission_amount": 9.70
  }
}
Comissão snapshot: a porcentagem é capturada no momento da criação da order. Se você alterar a comissão de um afiliado depois, vendas em andamento ainda usarão a porcentagem antiga.

Boas práticas gerais

Checklist
✓ Sempre validar HMAC antes de processar payload
✓ Responder 200 em ≤2s (mesmo que processamento seja assíncrono)
✓ Deduplicar por (event_id + order_id) com TTL 24h
✓ Retry assíncrono no SEU lado se DB falhar (não retornar 500)
✓ Logs por correlation_id (order_id) pra debug
✗ Não fazer chamadas síncronas a APIs externas no handler
✗ Não confiar no .data sem revalidar via GET na nossa API
✗ Não fazer trabalho pesado no handler — use queue se >2s