Detecta el país del visitante
De dónde sale el país de un visitante, dónde se guarda y qué lo lee.
El kit deduce el país de cada visitante a partir de su IP, lo recuerda y se lo entrega a las funciones que ponen precio, dan formato o ubican algo por país. Esta guía cubre el servicio que lo hace, las dos variables de entorno que lee, dónde se guarda el resultado para un visitante y para una cuenta, qué lo usa, y qué pasa en tu máquina, donde no hay IP pública.
El servicio
App\Services\CountryDetectionService es una clase de métodos estáticos y el único punto de entrada. Llámala desde cualquier lugar:
use App\Services\CountryDetectionService;
$country = CountryDetectionService::detect(); // 'MX', o null
detect()
Devuelve un código ISO de dos letras, o null cuando nada respondió. Intenta, en orden:
- El
country_codede la cuenta con sesión iniciada, cuando lo tiene. - Un código ya encontrado para este visitante, guardado en la sesión bajo
current_country_code. - La IP de la petición, a través de
detectFromIp(). Un código encontrado así se escribe en la sesión, para que la consulta ocurra a lo más una vez por visita.
detectFromIp(?string $ip)
La consulta de bajo nivel, usable también por sí sola:
- Sin IP devuelve
null. - Una IP privada o reservada (
127.0.0.1,::1,10.x,192.168.xy similares) devuelveconfig('app.country_code_fallback'), onullcuando está vacía. No se hace ninguna petición. - Cualquier otra IP se envía a
https://ipapi.co/{ip}/country/, sin llave y con un tiempo de espera de dos segundos. Una respuesta de dos letras se devuelve en mayúsculas; una falla, un tiempo agotado o cualquier otra cosa devuelvenull.
La aplicación confía en todos los proxies, así que detrás de un balanceador la IP es la de X-Forwarded-For y no la del balanceador.
getWorldId()
Resuelve el código detectado al id numérico del país en Weblabor World, a través de GET https://world.weblabor.mx/api/country/{codigo} con el token como encabezado Bearer y un tiempo de espera de dos segundos. Sin WEBLABOR_WORLD_TOKEN no devuelve nada y no hace petición; ante cualquier error falla en silencio.
detectTimezone() y worldTimezone()
La misma clase encuentra la zona horaria del visitante, desde la sesión, desde https://ipapi.co/{ip}/timezone/, y por último desde la zona horaria del país en Weblabor World cuando el token está puesto. Ese flujo está en Zonas horarias y fechas.
Las dos variables de entorno
| Variable | Clave de configuración | Valor por omisión | Qué hace |
|---|---|---|---|
COUNTRY_CODE_FALLBACK |
app.country_code_fallback |
US |
El país que se asume cuando la IP es privada. Ponla en tu mercado para el desarrollo local, o en un valor vacío para obtener null y ver lo que ve un visitante sin país. |
WEBLABOR_WORLD_TOKEN |
services.weblabor.world.token |
vacío | El token de la API de Weblabor World. Con él, una cuenta guarda además su país como división de World, los selects de país, estado y ciudad del perfil cargan sus opciones, y el respaldo de zona horaria por país está disponible. Sin él, la detección sigue funcionando sólo con la IP. |
.env.example lista WEBLABOR_WORLD_TOKEN= y no COUNTRY_CODE_FALLBACK; agrega esta última cuando US no sea el país para el que desarrollas.
El paquete World UI que dibuja los selects lee el mismo token, más WEBLABOR_WORLD_ENDPOINT, cuyo valor por omisión es https://world.weblabor.mx/api y sólo cambia cuando Weblabor te lo indique.
Dónde se guarda el país
Para un visitante, en la sesión, bajo current_country_code. Se escribe la primera vez que detect() llega a la consulta por IP y se lee en cada llamada posterior, así que un visitante que se mueve entre páginas se consulta una sola vez.
Para una cuenta, en users.country_code, una columna anulable de dos caracteres. Se llena cuando se crea la cuenta: App\Observers\UserObserver llama a detect() después de la inserción, que en ese momento lee la sesión con la que la persona se registró. Nunca se vuelve a detectar después; una cuenta conserva su país hasta que lo cambia.
Con WEBLABOR_WORLD_TOKEN puesto, el observer guarda además el id de división de World en extra_data.country. Ese atributo tiene el cast DivisionCast, así que leerlo devuelve un objeto Division cuyo ->country es el código ISO. El "Selecciona país" del perfil está ligado a ese atributo; cuando la persona elige otro país ahí, el observer copia el nuevo código ISO de vuelta a country_code, para que los dos nunca discrepen. Sin el token, extra_data.country no se toca y sólo se escribe country_code.
Qué lee el país
| Función | Cómo |
|---|---|
| Precios por país | El paquete de cobro le pregunta a CountryDetectionService a través del country_resolver de config/billing-core.php. El bloque de precios de la landing, la página de planes y la suscripción automática al plan gratuito al registrarse muestran la fila de precio del país del visitante y caen a la fila default. Una fila de precio lleva su propia moneda, así que el país decide en qué moneda se cobra a la persona. Revisa Crea y da precio a un plan. |
| Números de teléfono | normalize_phone_number() convierte un número escrito sin + a E.164 asumiendo el country_code de la cuenta, el país detectado o COUNTRY_CODE_FALLBACK, en ese orden. User::phoneExists() lo usa para encontrar un número guardado con otra forma. |
| El dashboard de administración | La métrica "Usuarios por país" agrupa las cuentas por country_code, con unknown para las que no tienen. |
| Dispositivos conectados | La lista de dispositivos en /account llama a detectFromIp() con la IP de cada sesión y nombra el país a través de la lista de países de World. |
| La zona horaria de la cuenta | Cuando la IP no da zona horaria, se usa la del país desde Weblabor World, como se describe arriba. |
El campo de teléfono en sí no preselecciona un prefijo a partir del país: la persona escribe el número con su código de país, y la normalización de arriba es lo que interpreta un número escrito sin él.
Desarrollo local
En tu máquina toda petición llega desde 127.0.0.1 o ::1, que es una IP reservada, así que detectFromIp() nunca llama a ipapi.co y devuelve COUNTRY_CODE_FALLBACK en su lugar. Con el valor por omisión eres un visitante de Estados Unidos; pon COUNTRY_CODE_FALLBACK=MX para ver precios mexicanos, o déjala vacía para obtener null y la fila de precio default. El valor se lee en el primer detect() de una visita y se guarda en la sesión, así que después de cambiarlo cierra sesión o abre una ventana privada.
Todo lo que le pregunta a Weblabor World, el id de división, los selects de país, estado y ciudad del perfil, los nombres de país en la lista de dispositivos y la zona horaria por país, necesita WEBLABOR_WORLD_TOKEN también en tu máquina. Los métodos del servicio fallan en silencio sin él; los selects no tienen API de dónde cargar sus opciones. La detección por IP en sí no necesita el token.