whatsappbackend
Nono dígito e WhatsApp: quando o wa_id não bate com o telefone
· 5 min de leitura
Se o seu produto faz match de usuário por número de telefone e você tem usuários brasileiros, existe um bug esperando por você em produção. A gente sabe porque colocou esse bug no ar.
O Lima tem um bot de WhatsApp. Você manda mensagem, ele registra seus gastos, completa seus hábitos, responde perguntas. Para saber quem está falando, comparamos o número do remetente com o número cadastrado na conta do usuário. Um lookup simples, que funcionou em todos os testes. Aí os usuários reais chegaram, e para alguns deles o bot respondia como um estranho: “não reconheço esse número”. Mesma pessoa, mesmo telefone, cadastro feito minutos antes.
Uma aula rápida de história
No começo dos anos 2010 o Brasil estava ficando sem números de celular, então a ANATEL adicionou um nono dígito, um 9 na frente, a todos os números móveis, com rollout região por região. Um celular de São Paulo que era +55 11 8765-4321 virou +55 11 9 8765-4321.
Todo número emitido desde então tem nove dígitos. O usuário digita nove dígitos. As operadoras cobram nove dígitos. Para qualquer pessoa discando um telefone no Brasil, a forma de oito dígitos simplesmente não existe mais.
O WhatsApp, porém, identifica as contas pelo número usado no registro. Uma conta criada antes da migração, e existem dezenas de milhões delas, ainda carrega internamente a identidade de oito dígitos. Quando esse usuário manda mensagem para o seu número comercial, o wa_id chega no webhook como 551187654321 (12 dígitos), enquanto o seu banco guarda +5511987654321 (13 dígitos). A comparação de string falha. Para o seu bot, aquela pessoa nunca existiu.
A parte cruel é a distribuição da falha. Ela só atinge quem tem conta antiga de WhatsApp, um recorte que pende exatamente para os usuários fiéis e menos técnicos, justo quem você menos quer receber com um “quem é você?”.
O que colocamos no ar
Duas mudanças, uma na leitura e outra na escrita.
Na leitura (o webhook), nunca confiamos na forma que chega. Para qualquer wa_id brasileiro, derivamos as duas variantes e buscamos por qualquer uma delas:
// 55 + DDD (2) + subscriber: 8 digits => also try with the 9; 9 digits => also try without
static List<String> candidates(String waId) {
if (!waId.startsWith("55") || waId.length() < 12) return List.of(waId);
String ddd = waId.substring(2, 4), rest = waId.substring(4);
if (rest.length() == 8) return List.of(waId, "55" + ddd + "9" + rest);
if (rest.length() == 9 && rest.startsWith("9")) return List.of(waId, "55" + ddd + rest.substring(1));
return List.of(waId);
}
Na escrita (o banco), rodamos uma migration normalizando todos os celulares brasileiros armazenados para a forma canônica E.164 de nove dígitos. Assim existe exatamente um formato em repouso, e a expansão de candidatos só acontece na borda. Também validamos números novos com a libphonenumber no cadastro. Ela conhece as regras do nono dígito por região, então número malformado nem chega a entrar no sistema.
Uma sutileza: faça a expansão de candidatos apenas para o código de país 55. A Argentina, por exemplo, tem suas próprias esquisitices de prefixo (um 9 depois do código do país para celulares), e uma heurística genérica “prestativa” de inserção de dígito vai corromper alegremente os números de outros países.
Lições
- E.164 é um formato, não uma identidade. Duas strings válidas em E.164 podem ser o mesmo telefone. Normalize para uma única forma canônica na escrita e trate identificadores de entrada como aproximados.
- O
wa_iddo WhatsApp reflete o histórico de registro, não o plano de numeração atual. Teste com uma conta antiga, não só com a sua. - Tolerância na borda, forma canônica em repouso. Fazer match das duas variantes espalhado pelo codebase é a receita para terminar com quatro cópias do mesmo bug.
Se você está construindo fluxos de OTP ou de bot para o Brasil, assuma que os fantasmas de oito dígitos estão por aí. Eles estão, e são seus melhores usuários.