torann/geoip
GeoIP for Laravel detects visitors' geographical location and currency from their IP addresses. Supports multiple geo services and requires publishing the config to set up a service.
Build vs. buy the "where is this visitor?" primitive. Every region-aware feature starts with the same question and the same hidden work: provider selection, HTTP client, timeouts, retries, caching, IPv6 and private-IP edge cases, and a sane fallback when a lookup fails. torann/geoip collapses that into a published config file plus GeoIP::getLocation($ip), with a driver layer that normalizes MaxMind, ipdata.co, ipfinder.io, IP-API, and ipgeolocation.io behind one Location value object (country, region, city, postal code, coordinates, timezone, currency). The decision it unlocks: stop funding bespoke plumbing, and spend that capacity on the features that consume location.
Sequencing the localization roadmap. Because the driver is swappable and the API is one call, geo stops being a one-way-door architecture decision and becomes a staged rollout. That supports shipping in risk order: default currency display and analytics tagging first (cosmetic, read-only), then regional content variants and consent-banner behavior, then availability messaging — and only last, region-based access gating or differential pricing. Those last two are revenue- and legal-exposed and are precisely the features that punish a noisy segment.
Cost architecture as a product lever. Per-call providers meter usage; MaxMind's local database trades per-call cost for a recurring distribution-and-refresh obligation. With caching keyed on IP and a TTL matched to how slowly geo actually changes, the provider sees a small fraction of traffic. That is a decision the PM should make deliberately against forecast volume — not discover after the invoice.
Compliance scoping. IP addresses are personal data in several jurisdictions, and the default path forwards visitor IPs to a third party. Whether the DPO has approved outbound provider calls and a retention policy for cached results is a product-gating question, not an engineering detail. This package makes that decision visible and swappable; it does not make it go away.
Good fit when:
LocationResolver interface with the package as the sole implementation today.Look elsewhere when:
For executives. This is a small, low-cost dependency that removes a recurring build-versus-rebuild tax on every location-aware feature. It is BSD-2-Clause licensed, actively tracking current Laravel releases, and deliberately provider-agnostic — so our commercial and data-residency options stay open rather than being locked to whichever vendor we wire in first. The risk is contained and cheap to contain: one maintainer, no SLA, and a demonstrated history of upstream providers changing terms. We mitigate with a thin internal interface, a pinned version, and a reviewed upgrade cadence — roughly a sprint of work, most of it on fallbacks, caching, and observability rather than installation. We should not ship region-based gating or differential pricing on it until we have proven the primitive on cosmetic, read-only consumers and cleared the privacy review.
For engineering. Publish the config, pin the minor, wire credentials through the existing secret store, and put the facade behind a LocationResolver interface from day one — that interface is the condition of adoption, not a follow-up ticket. Build the fallback contract explicitly: cache-miss → lookup → null-safe default, with private, loopback, and malformed IPs returning a null location rather than throwing. Turn on caching with a TTL tuned long, add counters for cache hit rate and lookup failures, configure trusted proxies so X-Forwarded-For is resolved behind the load balancer, and add a CI smoke test that resolves a known IP through the configured driver so a silent post-upgrade misconfiguration fails loudly.
For legal and finance. Two questions before commit: which driver, and does it satisfy our sub-processor and residency constraints? And what volume, and does it justify per-call pricing versus a local database with a scheduled refresh? Both answers are reversible behind the same interface — which is exactly why we are adopting the abstraction now and deferring the provider commitment.
How can I help you explore Laravel packages today?