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
+23
View File
@@ -767,6 +767,14 @@ class WhatsAppService
unset($payload['__app_meta']);
}
// A quien oculta su teléfono se le responde por BSUID, y Meta exige que vaya
// en `recipient` en lugar de `to`. Se corrige aquí, que es por donde pasan
// todos los envíos, en vez de en cada método que arma un payload.
if (isset($payload['to']) && self::esBsuid($payload['to'])) {
$payload['recipient'] = $payload['to'];
unset($payload['to']);
}
// Debug log (payload sent to WhatsApp)
error_log("WhatsApp API URL: " . $url);
error_log("WhatsApp Token length: " . strlen($this->token));
@@ -881,11 +889,26 @@ class WhatsAppService
return $decoded;
}
/**
* ¿El destinatario es un BSUID (identificador de usuario por empresa) y no un teléfono?
* Formato de Meta: código de país, punto y dígitos. Ej.: "CO.1761088155094242".
*/
public static function esBsuid($destinatario)
{
return (bool) preg_match('/^[A-Z]{2}\.\d+$/', (string) $destinatario);
}
/**
* Formatear número de teléfono
*/
private function formatPhoneNumber($phone)
{
// Un BSUID no es un teléfono: se devuelve intacto, porque quitarle
// el punto y las letras lo convertiría en un destinatario inexistente.
if (self::esBsuid($phone)) {
return $phone;
}
// Remover caracteres especiales
$phone = preg_replace('/[^0-9]/', '', $phone);