ipinfo/ipinfolaravel
Laravel integration for IPinfo's IP data API, adding geolocation, ASN, company, carrier, and privacy detection to your app. Includes configuration, middleware, caching, and a simple facade for looking up visitor IP addresses.
composer require ipinfo/ipinfolaravel
php artisan vendor:publish --provider="ipinfo\ipinfolaravel\ipinfolaravelServiceProvider" --tag=config
Add your token to .env (the package reads it from config/ipinfo.php):
IPINFO_TOKEN=your_token_here
IPINFO_COUNTRY=US # fallback country used for private/bogon IPs
The provider and Ipinfo facade are auto-discovered. First use case — resolve a visitor's country in a controller or middleware:
use ipinfo\ipinfolaravel\facades\Ipinfo;
$details = Ipinfo::getDetails($request->ip());
$country = $details->country ?? config('ipinfo.country');
$currency = $country === 'GB' ? 'GBP' : 'USD';
Where to look first
config/ipinfo.php after publishing — this is the entire surface area of the wrapper.vendor/ipinfo/ipinfolaravel/src/ — it is a very thin service provider + facade; you'll see it just builds an ipinfo\ipinfo\IPinfo client from config.ipinfo/ipinfo README — that, not the Laravel repo, is the real API reference for getDetails(), getBatchDetails(), and the Details object.Before anything else, check installability on your stack. The last release was January 2021, so its composer constraints target Laravel 6–8 era. Run composer show ipinfo/ipinfolaravel --all and confirm your Laravel/PHP versions are allowed; on Laravel 10/11 you will likely need to skip the wrapper and bind ipinfo/ipinfo yourself (see Gotchas).
Treat it as a container binding, not a framework feature. Day-to-day, the pattern is: inject or resolve ipinfo\ipinfo\IPinfo (bound as ipinfo), call one of two methods, read the Details object.
Resolve once per request via middleware. Avoid scattering lookups through controllers; geolocation is request-scoped context.
class ResolveGeo
{
public function handle($request, Closure $next)
{
$ip = $request->ip();
$request->attributes->set('geo', Cache::remember(
"ipinfo:{$ip}",
now()->addDay(),
fn () => Ipinfo::getDetails($ip)->all // cache the array, not the object
));
return $next($request);
}
}
Then anywhere downstream: $request->attributes->get('geo')['country'].
Cache per IP, explicitly. Even if the package config exposes cache settings, wrap calls in Cache::remember with a key like ipinfo:{ip} and a 24h–7d TTL. Geolocation is stable; this is the single biggest lever on latency, rate limits, and cost. Cache the ->all array — the Details object serializes awkwardly into Redis/file caches and queues.
Batch enrichment in jobs. For imports, order backfills, or log analysis, never loop getDetails(); call the batch endpoint with deduped IPs, chunked to the API limit (~1000 per request):
$results = Ipinfo::getBatchDetails($ips); // ['8.8.8.8' => Details, ...]
foreach ($results as $ip => $details) {
// $details->country, $details->org, ...
}
Do this in a queued job, not a web request — batches can be slow and the payload is large.
Fraud / security scoring. Store country, asn, org, and (paid plan) privacy/company on login events or orders. Typical checks: country mismatch vs. billing country, hosting/VPN ASN, datacenter org strings. Keep the raw lookup result alongside the decision so you can re-evaluate rules later without re-querying.
Localization and pricing. country, timezone, and loc drive default locale, currency, and date formatting. Always layer a config fallback for bogon results rather than branching on null everywhere.
Testing. Mock the facade or swap the binding — never hit the network in tests:
Ipinfo::shouldReceive('getDetails')->andReturn((object) ['country' => 'DE']);
// or
$this->app->instance('ipinfo', $fakeClient);
getDetails() with no argument looks up the server's IP, not the visitor's — the API resolves the caller when no IP is supplied. In a Laravel app that means your web node's egress IP. Always pass $request->ip().127.0.0.1, ::1, 10.x, 172.16–31.x, 192.168.x, and Docker/CI addresses are bogons: $details->bogon is true and country, city, etc. are empty. Set the fallback country option and test locally with a real public IP; don't conclude the package is broken when local dev returns blanks.$request->ip() is the proxy. Configure TrustProxies (or read CF-Connecting-IP/X-Forwarded-For deliberately) or every user will geolocate to your datacenter.Http::fake() will not intercept these calls. The underlying library uses cURL directly, so Laravel's HTTP client fakes, preventStrayRequests(), and Telescope's HTTP watcher won't see them. Mock the container binding or the facade instead.privacy, company, carrier, domains, and abuse data require paid plans; free/anonymous usage returns a reduced field set and stricter rate limits. Guard with ?? and null-safe operators rather than assuming ->city exists.''; you get 401/429 responses later. Add a startup check (config('ipinfo.token') empty → log a warning) and run php artisan config:clear after editing .env, since config:cache will otherwise freeze the old token.composer require ipinfo/ipinfo and in AppServiceProvider::register() do $this->app->singleton(IPinfo::class, fn () => new IPinfo(config('ipinfo.token'), config('ipinfo'))). Same client, same Details object, no facade.ipinfo\ipinfolaravel\facades\Ipinfo (lowercase segments), which breaks IDE autocompletion and PSR-4 conventions. Alias it in config/app.php or import with as Geo in files you touch often.GeoService with lookup(string $ip): GeoData; that one indirection lets you swap the wrapper for the raw client, change providers, add circuit breaking, or fake responses in tests without touching call sites.IPinfo client's options, so configure it there rather than expecting the Laravel package to grow features.How can I help you explore Laravel packages today?