Atender a quien oculta su teléfono en WhatsApp (BSUID)

Meta desplegó los nombres de usuario: quien oculta su teléfono llega al
webhook sin `from` ni `wa_id`, identificado solo por su BSUID con la forma
"CO.1761088155094242". El webhook exigía teléfono y descartaba el mensaje
en silencio, sin guardarlo ni dejar rastro: 221 mensajes de 65 personas
ignorados por completo, ya el 3-6% de lo que entra cada día y subiendo.

- webhook.php lee `from_user_id` cuando no llega el teléfono, y rescata el
  nombre del contacto indexando también por `user_id`.
- WhatsAppService responde por BSUID: Meta exige el campo `recipient` en
  lugar de `to`. Se traduce en sendMessage(), el punto por donde pasan todos
  los envíos, en vez de en los diez métodos que arman payloads.
- formatPhoneNumber() devuelve el BSUID intacto; antes le quitaba el punto
  y las letras y lo dejaba en un destinatario inexistente.
- Las cuatro columnas del flujo pasan a varchar(32): los BSUID de Meta
  llegan a 23 caracteres y se truncaban en silencio en varchar(20).

De estas personas no tendremos el teléfono; si un flujo lo necesita, hay
que identificarlas por documento.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-11 19:41:10 -05:00
co-authored by Claude Opus 5
parent 381c2ca04e
commit 92179af56a
3 changed files with 53 additions and 6 deletions
+11 -6
View File
@@ -113,21 +113,26 @@ class WhatsAppWebhook {
return;
}
// Extraer nombres de contactos del payload (contacts[].profile.name)
// Extraer nombres de contactos del payload (contacts[].profile.name).
// Se indexa por wa_id y también por user_id (BSUID): desde que Meta desplegó
// los nombres de usuario, quien oculta su teléfono llega sin wa_id.
$contactNames = [];
if (isset($value['contacts']) && is_array($value['contacts'])) {
foreach ($value['contacts'] as $contact) {
$waId = $contact['wa_id'] ?? null;
$name = $contact['profile']['name'] ?? null;
if ($waId && $name) {
$contactNames[$waId] = $name;
if (!$name) continue;
foreach ([$contact['wa_id'] ?? null, $contact['user_id'] ?? null] as $clave) {
if ($clave) $contactNames[$clave] = $name;
}
}
}
foreach ($items as $message) {
// En algunos payloads la estructura key es 'from' y 'id' (mensajes), en otros puede venir distinta; normalizamos
$phoneNumber = $message['from'] ?? ($message['wa_id'] ?? null);
// En algunos payloads la estructura key es 'from' y 'id' (mensajes), en otros puede venir distinta; normalizamos.
// `from`/`wa_id` dejaron de ser obligatorios: si la persona oculta su teléfono
// solo llega `from_user_id` (BSUID, ej. "CO.1761088155094242"). Ese identificador
// ocupa el lugar del teléfono en todo el flujo y sirve para responderle.
$phoneNumber = $message['from'] ?? ($message['wa_id'] ?? ($message['from_user_id'] ?? null));
$messageId = $message['id'] ?? ($message['message_id'] ?? null);
$timestamp = $message['timestamp'] ?? null;