--- name: WhatsApp LID Privacy Mode (2026-05-05) description: Mensagens recebidas com @lid escondem o numero real do remetente. Estrategia em 2 camadas pra recuperar. type: project originSessionId: 86576285-fa7a-44f0-8ed3-9a88bcd27a8e --- # WhatsApp @lid (Linked ID) — Privacy Mode ## Problema WhatsApp introduziu privacidade onde o numero do remetente vem como `XXX@lid` em vez de `numero@s.whatsapp.net`. O numero REAL nao vem no payload — by design, e privacidade. **Bug critico que tinhamos**: estavamos usando `payload['jid']` como "numero real do remetente". Mas esse campo na verdade e o numero do CONECTOR Baileys (a conta WhatsApp da escola), nao do remetente. Por isso TODOS os LIDs eram convertidos pro mesmo numero (553189049626), zerando todas as confirmacoes. **Why**: detectado quando "Mirtilos Moreira" respondeu "1" mas foi atribuido ao admin. Logs: ``` LID 131219891171533 → 553189049626 (Mirtilos) LID 143499185922126 → 553189049626 (Jesiel) LID 230218920902658 → 553189049626 (?) ``` **How to apply**: NUNCA usar `payload['jid']`, `payload['participantPn']`, `payload['senderPn']` etc pra resolver LID — esses campos sao do receptor, nao do remetente. ## Solucao em 2 camadas ### Camada 1 (PRIMARIO, 100% confiavel): LID destino capturado no envio `WhatsAppService::sendText` captura `$result['response']->key->remoteJid` (LID que o Baileys retorna ao enviar) e salva em `whatsapp_mensagens.lid_destino`. Quando recebe resposta com `@lid`, busca em `whatsapp_mensagens` onde `lid_destino` bate, recupera o `telefone_destino` original. Implementado em: - `WhatsappMensagemModel::registrarMensagemEnviada($..., ?string $lidDestino = null)` - `WhatsappMensagemModel::getUltimaMensagemPorLid($lidNumerico)` - `LembreteService::enviarMensagemWhatsApp` — passa `$resultado['recipient_lid']` - `WhatsappWebhookController::resolverTelefonePorLid` ### Camada 2 (FALLBACK): pushName lookup Se nao tem LID salvo (ex: lembretes antigos antes da correcao), usa `payload['pushName']` (nome de exibicao do contato no WhatsApp) pra buscar: 1. `usuarios.nome` exato (alunos) 2. `alunos.responsavel1_nome`/`responsavel2_nome` exato 3. LIKE no nome do aluno (apelido/nome incompleto) Se houver match unico → usa telefone. Se multiplos ou nenhum → ignora pra nao processar mensagem errada. Implementado em: - `WhatsappWebhookController::resolverTelefonePorPushName` - `WhatsappWebhookController::normalizarParaBR` ## Migration Tabela `whatsapp_mensagens`: ```sql ALTER TABLE whatsapp_mensagens ADD COLUMN lid_destino VARCHAR(64) NULL AFTER telefone_formatado, ADD INDEX idx_whatsapp_lid_destino (lid_destino); ``` Migration: `2026-05-05-220000_AddLidDestinoToWhatsappMensagens.php` ## Limitacoes do fallback pushName - Aluno sem pushName configurado → nao funciona - pushName diferente do nome no DB ("João" vs "João Silva Santos") → match aproximado pode pegar - 2+ alunos com mesmo nome → ignora (retorna null) - Responsavel respondendo pelo aluno → so funciona se nome do responsavel bater A camada 1 (LID capturado no envio) elimina TODOS esses casos. O fallback pushName e so pra mensagens de quem ainda nao recebeu lembrete novo apos a correcao.