Documentação / Arquitetura de Pareamento Remoto de Conexão (Remote Pairing)

Arquitetura de Pareamento Remoto de Conexão (Remote Pairing)

Entrar

Arquitetura de Pareamento Remoto de Conexão (Remote Pairing)

📌 Contexto

Para facilitar a vida dos clientes da Pilot Status que operam como "plataforma" (vendendo para outros clientes finais), e que atualmente precisam integrar/desenhar do zero toda a parte visual do QRCode e Pairing Code, estamos introduzindo uma forma de gerar um link de pareamento.

1. Fluxo de Usuário

  1. O Cliente Pilot Status (via API) chama: POST /api/v1/numbers/remote-pairing enviando o número (number) e nome (name).
  2. A API retorna uma URL: https://[pilotstatus.com]/connect/abc-123-xyz.
  3. O Cliente Pilot Status envia ou exibe essa URL para o seu cliente final em um iframe ou modal simples.
  4. Ao abrir, o Cliente Final acessa uma página hospedada pela Pilot Status com a interface de leitura de QR Code.
  5. O Cliente final faz a leitura do QR e a conexão é efetuada no projeto original do Cliente Pilot Status.
  6. O Cliente Pilot Status é notificado via Webhook (number.created / status change).

Como as instâncias são provisionadas no momento da geração do link? Para garantir que a rastreabilidade e amarrações existam de imediato no banco, o endpoint da API já cria fisicamente a WhatsAppInstance no banco de dados e nos provedores upstream. No entanto, ela nasce com o status desconectado (CLOSE), ignorando a leitura de QR inicial que os clientes do dashboard passam. O QR Code (e seu TTL de 40s) não é ativado na API; ele só é de fato puxado (via trigger de conexão) quando o usuário final acessa a página pública e clica para parear.


🛠️ Modificações no Backend

1. Endpoint V1: Geração de Sessão de Pareamento

Caminho: apps/fullstack/src/app/api/v1/remote-pairing/route.ts

  • Autenticação e Validação: Reutiliza a função getPublicApiKeyInfo de src/lib/public-api-auth.ts para capturar e validar as intenções de requests externos.
  • Armazenamento e Criação Imediata: A WhatsAppInstance é efetivamente criada no banco de dados e no provedor conectado com o Pilot Status, mas gerada de maneira assíncrona sem acionar a exibição imediata do QR Code ao cliente da API.
    import { WhatsAppInstanceService } from "@/services/whatsapp-instance.service";
    import { randomUUID } from "crypto";
    
    const remotePairingToken = randomUUID(); // Usado na URL
    
    // Cria a instância inteira já associada ao proxy e Evolution (porém desconectada initialmente)
    const created = await WhatsAppInstanceService.create({
        tenantId: auth.keyInfo.tenantId,
        displayName: parsed.data.name,
        number: parsed.data.number,
        linkMode: parsed.data.linkMode || "SINGLE",
        state: "CLOSE", // Nasce desconectada
        remotePairingToken // Salva o token rastreável no banco
    });
    

2. Endpoints Internos para a UI Pública

Caminho: apps/fullstack/src/app/api/remote-pairing-ui/[token]/route.ts

  • GET: Busca a instância que contém o token de rastreio (remotePairingToken). Retorna dados básicos para a UI mascarar: {"valid": true, "name": "Cliente João", "maskedNumber": "+551199999****" }.

  • POST: Ação engatilhada quando o usuário clica em "Gerar QR Code". Já com a instância criada, ele força uma chamada de connect no provedor para de fato trazer o QR Code / Pairing Code vigente para aquela sessão, alterando o status interno.

    import { getPrimaryWhatsAppProvider, getSecondaryWhatsAppProvider } from "@pilot-status/whatsapp-provider";
    
    // Encontra a instância via token
    const instance = await WhatsAppInstanceService.getByRemoteToken(params.token);
    
    // Solicita o connect passando a API Key gerada/evolution instanceId 
    const connectResult = await primaryProvider.instance.connect(instance.instanceName, { ... });
    
    // Atualiza banco para CONNECTING 
    // No caso de Dual Link, repete com secondaryProvider se aplicável
    

💻 Modificações no Frontend

1. Página de Pareamento Pública (Next.js Application Router)

Caminho: apps/fullstack/src/app/connect/[token]/page.tsx

  • Uma rota externa pura no App Router, sem dependências massivas de contextos como o <AppProvider> autenticado.
  • Micro-estados reaproveitados do Flow Numbers.tsx (SPA):
    • Componentização das lidas para QR Code de expiração, bem semelhantes aos tratamentos em apps/fullstack/src/spa/pages/Numbers.tsx (onde é pego e tickado os 40s de QR_PAIRING_TTL_SECONDS).
    • Componente: Pode reaproveitar visivelmente chamando a função handleGenerateQR / ou advanceToDualSecondaryStep se adaptados da estrutura existente que englobe os payloads:
      // Semelhante ao payload usado em: whatsAppConnectHasDisplayMaterial(res)
      { qrcodeBase64: string | null; pairingCode: string | null }
      
  • Fluxo do Componente UI:
    1. Aguardando: Status inicial, mostra o nome/número e um botão "Conectar Dispositivo".
    2. QR / Pareamento: O React vai fazer chamadas via fetch padrão pro novo backend (/api/remote-pairing-ui/[token]).
      • Dual Link (Conexão Dupla): O payload que inicializou a sessão via API possuirá suporte a um parâmetro linkMode: "DUAL". Caso ativado na intenção inicial no banco, a UI automaticamente pedirá para o cliente final parear duas vezes nativamente (Evolution GO + V2), gerando confiabilidade máxima na ponta.
    3. Polling: Dá um fetch secundário em um setInterval em /api/remote-pairing-ui/[token]/status pra identificar quando state == "OPEN".
    4. Sucesso: Animação de sucesso.

🔄 Fluxo de Confirmação (Webhook / Integração da Ponta)

Quando o pareamento remoto na página /connect/:token for finalizado, a WhatsAppInstanceService criará de fato a instância.

Novo Evento de Webhook

Para que o sistema seja perfeitamente passivo, a infraestrutura ganhará suporte ao novo evento de Webhook number.status:

  • Caminho: Será adicionado nos checkboxes de controle (modais de Criação/Edição) em apps/fullstack/src/spa/pages/Webhooks.tsx e apps/fullstack/src/spa/pages/webhooks/webhook-events.ts.
  • Efeito: Sempre que uma mudança de estado ocorrer (por exemplo de CONNECTING para OPEN ou de OPEN para CLOSE), o cliente final ou o painel principal do sistema será alertado que a conexão foi concretizada ou perdida.
  • Em conjunto com o evento padrão number.created, o cliente da Pilot Status armazenará as amarrações do link de pareamento sem realizar polling exaustivo.

✅ Conclusão de Migração do Fluxo

Reutilizando grande parte da infra de criação WhatsAppInstance e os webhooks transacionais do pacote @pilot-status/shared, removemos completamente do cliente a exigência de lidar com WebSockets de QR, ou timers front-end com Next.js via proxy, resolvendo a fricção de entrada e garantindo até estabilidade dual (Double Scan) sem estresse visual adicional.