Fluxo 2 - Checkout Hotmart → Keycloak: Gerenciamento de Acesso
Gerenciar o ciclo de vida de acesso de assinantes da Hotmart à comunidade Circle, criando/atualizando usuários no Keycloak e adicionando/removendo do grupo "Assinantes Circle" baseado em eventos de compra aprovada ou cancelamento.
Objetivo e Contexto
Controle de Acesso via Grupos no Keycloak
O Keycloak é nosso provedor de identidade e autorização. Ele controla quem pode acessar a comunidade Circle através de:
- Grupos: Agregações de usuários com permissões comuns
- Grupo "Assinantes Circle": Aqueles com acesso ativo à comunidade
- SSO (Single Sign-On): Autenticação centralizada com token JWT
Fluxo de autorização:
Usuário loga via SSO Keycloak
↓
Keycloak verifica: está no grupo "Assinantes Circle"?
├─ SIM → Gera JWT com acesso
└─ NÃO → Nega acesso ao Circle
Este fluxo garante: Quando alguém compra na Hotmart, ele é automaticamente adicionado ao grupo (ativação de acesso). Quando cancela, é removido (revogação de acesso).
Eventos Suportados
| Evento Hotmart | Ação | Resultado |
|---|---|---|
PURCHASE_APPROVED |
Criar usuário OU conceder acesso | Usuário adicionado ao grupo Assinantes Circle |
SUBSCRIPTION_CANCELLATION |
Remover acesso | Usuário removido do grupo Assinantes Circle |
Macro Arquitetura
| Componente | Responsabilidade | Ferramenta |
|---|---|---|
| Webhook | Receber eventos de checkout | Hotmart (POST → N8N) |
| Extração de dados | Normalizar email e evento | N8N (Code) |
| Autenticação | Obter token de acesso | Keycloak API |
| Busca de usuário | Verificar se usuário já existe | Keycloak API |
| Roteamento | Direcionar para ação correta | N8N (Switch) |
| Gerenciamento de grupo | Adicionar/remover do grupo | Keycloak API |
| Convite de acesso | Enviar primeiro acesso | Keycloak Magic Link |
Fluxo Técnico Detalhado
1. Webhook: Receber Eventos Hotmart
URL de entrada:
POST https://n8n.bonde.org/webhook/checkout/auth
Eventos capturados:
{
"event": "PURCHASE_APPROVED" | "SUBSCRIPTION_CANCELLATION",
"data": {
"buyer": {
"email": "cliente@example.com",
"first_name": "João",
"last_name": "Silva",
"checkout_phone": "...",
"address": { ... }
},
"subscriber": {
"email": "cliente@example.com"
},
"subscription": {
"status": "ACTIVE"
},
"purchase": {
"recurrence_number": 1,
"approved_date": 1712345678000,
"status": "APPROVED"
}
}
}
2. Normalização: Extrair Dados Relevantes
O que faz: Padroniza o payload para facilitar processamento nos próximos nós
Lógica (JavaScript):
const event = webhook.body.event;
const data = webhook.body.data;
if (event === 'SUBSCRIPTION_CANCELLATION') {
email = data.subscriber.email; // Cancelamento usa subscriber
} else if (event === 'PURCHASE_APPROVED') {
email = data.buyer.email; // Compra usa buyer
}
return {
email: email,
event: event
};
Saída:
{
"email": "cliente@example.com",
"event": "PURCHASE_APPROVED"
}
3. Autenticação: Obter Token Keycloak
Endpoint:
POST https://auth.bonde.org/realms/bonde/protocol/openid-connect/token
Credenciais (Client Credentials Flow):
{
"grant_type": "client_credentials",
"client_id": "n8n",
"client_secret": "GZIT95PlJdCe61iV1yivmvMmhqfCtHV9"
}
Resposta:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldWIiwia2lkIiA6ICJ...",
"expires_in": 300,
"token_type": "Bearer"
}
Por quê: Token permite que N8N execute ações no Keycloak como autoridade administrativa.
4. Buscar Usuário: Verificar Existência
Endpoint:
GET https://auth.bonde.org/admin/realms/bonde/users?email={{email}}&exact=true
Headers:
Authorization: Bearer {{ access_token }}
Resposta (usuário existe):
[
{
"id": "17896b50-8e7a-4715-b217-3caf12f090de",
"email": "cliente@example.com",
"firstName": "João",
"lastName": "Silva",
"enabled": true,
"groups": ["/Assinantes Circle"]
}
]
Resposta (usuário não existe):
[]
5. Roteamento: Pipeline com 5 Saídas
Lógica do Switch:
IF event == "SUBSCRIPTION_CANCELLATION" AND usuário existe
→ Output 0: Remover Acesso
ELSE IF event == "SUBSCRIPTION_CANCELLATION" AND usuário NÃO existe
→ Output 1: Ignorar (usuário já removido ou nunca existiu)
ELSE IF event == "PURCHASE_APPROVED" AND usuário existe
→ Output 2: Conceder Acesso (adicionar a grupo)
ELSE IF event == "PURCHASE_APPROVED" AND usuário NÃO existe
→ Output 3: Criar Usuário (novo cliente)
ELSE
→ Output 4: Evento inválido (ignorar)
6A. Caminho: Remover Acesso (Output 0)
Quando disparado: Cliente cancelou assinatura e já tinha usuário no Keycloak
Endpoint:
DELETE https://auth.bonde.org/admin/realms/bonde/users/{{ user_id }}/groups/f8a91e89-f8dc-459d-9c59-3b1aaf945b28
Parâmetros:
| Parâmetro | Valor |
|----|----|
| user_id | ID do usuário Keycloak |
| group_id | f8a91e89-f8dc-459d-9c59-3b1aaf945b28 (Assinantes Circle) |
| Authorization | Bearer {{ access_token }} |
Resultado:
Status: 204 No Content
O que acontece: Usuário é removido do grupo "Assinantes Circle" → próximo SSO nega acesso ao Circle
6B. Caminho: Conceder Acesso (Output 2)
Quando disparado: Cliente tem assinatura ativa e já tinha usuário no Keycloak
Endpoint:
PUT https://auth.bonde.org/admin/realms/bonde/users/{{ user_id }}/groups/f8a91e89-f8dc-459d-9c59-3b1aaf945b28
Parâmetros:
| Parâmetro | Valor |
|----|----|
| user_id | ID do usuário Keycloak |
| group_id | f8a91e89-f8dc-459d-9c59-3b1aaf945b28 (Assinantes Circle) |
| Authorization | Bearer {{ access_token }} |
Resultado:
Status: 204 No Content
O que acontece: Usuário é garantidamente membro do grupo → próximo SSO permite acesso ao Circle
6C. Caminho: Criar Usuário (Output 3)
Quando disparado: Cliente comprou pela primeira vez e não tem usuário no Keycloak
Passo 1: Criar usuário
Endpoint:
POST https://auth.bonde.org/admin/realms/bonde/users
Payload:
{
"email": "{{ buyer.email }}",
"username": "{{ buyer.email }}",
"emailVerified": true,
"enabled": true,
"firstName": "{{ buyer.first_name }}",
"lastName": "{{ buyer.last_name }}",
"groups": ["/Assinantes Circle"],
"requiredActions": ["UPDATE_PASSWORD"]
}
Campos:
| Campo | Descrição |
|----|----|
| email | Email do cliente Hotmart |
| username | Mesmo que email (identificador único) |
| emailVerified | Marca como verificado (sem confirmar por email) |
| enabled | Ativa o usuário imediatamente |
| firstName, lastName | Dados do perfil |
| groups | Adiciona ao grupo Assinantes Circle já na criação |
| requiredActions | Obriga mudança de senha no primeiro acesso |
Resposta (sucesso):
Status: 201 Created
Location: /admin/realms/bonde/users/{{ user_id }}
Passo 2: Enviar Magic Link
Endpoint:
POST https://auth.bonde.org/realms/bonde/magic-link
Payload:
{
"email": "{{ buyer.email }}",
"client_id": "circle",
"expiration_seconds": 3600,
"redirect_uri": "https://comunidade.bonde.org",
"send_email": true
}
Campos:
| Campo | Descrição |
|----|----|
| email | Destinatário do email com link |
| client_id | Aplicação (Circle) para qual gerar link |
| expiration_seconds | Link válido por 1 hora |
| redirect_uri | Para onde redirecionar após login |
| send_email | Envia email com link automaticamente |
Email recebido pelo cliente:
Bem-vindo à Comunidade BONDE!
Clique aqui para acessar sua comunidade:
https://auth.bonde.org/magic-link/eyJhbGc...
Resultado: Cliente recebe email com link de primeiro acesso, clica e é redirecionado para Circle
Resultado Esperado
Estado Antes do Fluxo (Compra)
- ✗ Compra confirmada na Hotmart
- ✗ Sem usuário no Keycloak
- ✗ Sem acesso à comunidade Circle
Estado Depois do Fluxo (Compra - Novo Usuário)
- ✅ Usuário criado no Keycloak
- ✅ Adicionado ao grupo "Assinantes Circle"
- ✅ Email recebido com Magic Link
- ✅ Primeiro acesso requer mudar senha
- ✅ Após login, tem acesso total ao Circle
Estado Antes do Fluxo (Cancelamento)
- ✗ Assinatura cancelada na Hotmart
- ✓ Usuário ainda existe no Keycloak
- ✓ Usuário ainda está no grupo Assinantes Circle
Estado Depois do Fluxo (Cancelamento)
- ✅ Usuário removido do grupo "Assinantes Circle"
- ✅ Próximo login SSO → Keycloak nega acesso
- ✅ Usuário vê erro ao tentar acessar Circle
- ℹ️ Conta continue existindo (para histórico)
Mapeamento de Groups no Keycloak
| Nome do Grupo | ID | Propósito |
|---|---|---|
| Assinantes Circle | f8a91e89-f8dc-459d-9c59-3b1aaf945b28 |
Autorização para acessar Circle |
| (outros grupos) | ... | Para expansão futura |
Note: Esses IDs são específicos da configuração Keycloak. Se estrutura de grupos mudar, esses IDs precisarão ser atualizados no fluxo.
Possíveis Cenários de Erro
| Erro | Causa | Resolução |
|---|---|---|
| Email inválido | Hotmart enviou email malformado | Webhook rejeita |
| Usuário duplicado | Email já existe no Keycloak | N8N trata como "usuário existe" → concede acesso |
| Falha ao criar usuário | Senha requisitada ou erro de permissão | Verificar credenciais N8N no Keycloak |
| Magic Link não chega | Email configurado incorretamente em Keycloak | Usuário pode fazer reset de senha manualmente |
| Evento não reconhecido | Hotmart enviou evento inesperado | Fluxo ignora (Output 4) |
| Falha ao remover do grupo | Usuário já não está no grupo | Operação é idempotente (sucesso/silenciosa) |
Conceitos-Chave
Client Credentials Flow
Tipo de autenticação OAuth2 para aplicações servidor-a-servidor:
N8N enviar credenciais (client_id + client_secret)
↓
Keycloak valida
↓
Keycloak emite token temporal
↓
N8N usa token em requisições subsequentes
Vantagem: Não requer interação do usuário, ideal para integrações.
Grupos como Autorização
Em vez de usar roles (papéis), usamos grupos porque: - Mais fácil gerenciar dinamicamente - Keycloak sincroniza grupos → Circle via SSO - Um usuário pode estar em múltiplos grupos no futuro
Email como Chave Primária
Email é único e imutável → garantia de identificação consistente: - Ao buscar usuário, sempre usa email - Ao criar usuário, email é username também - Se cliente trocasse de email, criaria duplicata (cuidado!)
Magic Link vs Enviar Senha
Magic Link (implementado): - ✅ Não expõe senha por email - ✅ First-time setup seguro - ✅ Redireção direta para Circle
Enviar Senha (não implementado): - ✗ Menos seguro - ✗ Cliente precisa fazer reset mesmo assim
RequiredActions
Campo requiredActions: ["UPDATE_PASSWORD"] força:
- Na primeira login, Keycloak redireciona para trocar senha
- Garante que cliente tem senha única e forte
- Previne que N8N armazene senhas
Fluxos Relacionados
- ✅ Fluxo 1 (Enriquecimento de Base AC): Atualiza dados do cliente em paralelo
- ✅ Fluxo 3 (Ativação de Conta Keycloak + Circle): O que acontece DEPOIS que usuário está no grupo
- ⏳ Fluxo 4 (Desativação): Remove acesso quando cancelamento
- ⏳ Fluxo 5 (Sincronização de Perfil): Atualiza profilename/avatar Circle ↔ Keycloak
Checklist de Implementação
- [ ] Webhook URL configurada na Hotmart
- [ ] Credenciais N8N registradas no Keycloak (client_id + client_secret)
- [ ] Group ID verificado (
f8a91e89-f8dc-459d-9c59-3b1aaf945b28) - [ ] Email de Magic Link configurado no Keycloak
- [ ] Teste: Nova compra → usuário criado e recebe magic link
- [ ] Teste: Cancelamento → usuário removido do grupo
- [ ] Monitoramento de erros no N8N ativado