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>
Cuando se usaban rawComponents, el parámetro $meta no se agregaba
al payload, causando que los mensajes del turnero se guardaran con
canal='bot' y aparecieran en conversations.php.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
WhatsApp API returns "(#100) Invalid parameter — Parameter name is
missing or empty" for templates that use named variables.
- chat.php sendTemplate(): collect params as [{name, value}] objects
using data-var-index as the parameter name
- WhatsAppService::sendTemplateMessage(): handle {name, value} objects
in the indexed-array path by emitting {type, parameter_name, text}
(backward-compatible — plain strings still work as positional params)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- WhatsAppService::makeRequest: include error_subcode and error_data.details
in the thrown exception message to surface the real reason for #100 errors
- chat_send_message: log template name/lang/params before sending;
pass canal+operator_id meta so outgoing templates are tagged correctly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
saveOutgoingMessage() now reads __app_meta.canal and stores it in conversations.
sendMediaById() accepts a $meta param and forwards it to saveOutgoingMessage.
chat_upload_media.php passes $extra (canal=turnero) to sendMediaById.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- WhatsAppService($canal): when 'turnero', swaps phoneNumberId to whatsapp_phone_number_id_turnero
so outgoing messages actually go from the turnero number (not the main bot)
- webhook.php: reads metadata.phone_number_id from the payload and compares it to
whatsapp_phone_number_id_turnero; if it matches, saves canal='turnero' and skips bot processing
- Previously all incoming messages had canal=NULL/bot so Chat Turnero never saw them,
and all outgoing messages from Chat Turnero were sent via the wrong (main bot) number
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>