Search by

silvershop-suite / address-lookup-postnl

RVXD

PostNL Adrescheck provider for silvershop-suite/address-lookup (CMS 6): a satellite connector that resolves NL addresses structurally (postcode + house number -> street + city via PostNL's Dutch addresscheck) and every other country via free-text international autocomplete, over PostNL's unified Adr

Package info

github.com/silvershop-suite/silvershop-address-lookup-postnl

Type:silverstripe-vendormodule

pkg:composer/silvershop-suite/address-lookup-postnl

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-10-20 15:50 UTC

This package is not auto-updated.

Last update: 2026-10-21 04:44:31 UTC


README

A satellite provider for silvershop-suite/address-lookup (CMS 6) that resolves addresses through PostNL's Adrescheck Autocomplete (ACI) API — the carrier-aligned option for shops that already ship with PostNL (or MyParcel).

It registers one provider (postnl) in the base module's per-country registry. PostNL's unified autocomplete endpoint serves both the structured Dutch postcode + house-number lookup and free-text international autocomplete, so a single connector covers NL precisely and the rest of the world as typeahead.

Capabilities

PostNL exposes one endpoint — GET https://api.postnl.nl/v1/address/autocomplete/search — authenticated with an apikey request header. There is no autocomplete→retrieve two-step: /search returns full addresses directly. The only optional first step is /token for a session uuid; this provider self-generates that uuid (RFC 4122 v4) and skips the extra round-trip.

Country Mode Request Resolves
NL postcode_number /search?countryIso=NL&postalCode=&houseNumber=(&houseNumberAddition=) full structured address: street + city + region
every other country autocomplete /search?countryIso=&q=(&postalCode=) candidate addresses from a free-text term
Method Behaviour
lookup(AddressQuery) NL: structured postcode + house number → one ResolvedAddress (picks the typed house-number addition when several exist). Other: free-text q (+ postalCode filter) → the first candidate
suggest(term, iso) free-text /search → candidate ResolvedAddress rows, capped to result_limit (ACI has no limit param, so the cap is client-side)
validate(ResolvedAddress) a best-effort lookup-match (ACI has no boolean verify endpoint): a hit returns the resolved address as the canonical form (score 1.0); a miss or any fault soft-passes

Coverage. ACI advertises "all countries of the world" and exposes no country-enumeration endpoint; the actual per-account entitlement is enforced server-side (HTTP 403 for an unsupported entry-point country, which degrades to a soft miss). The provider therefore accepts any well-formed ISO 3166-1 alpha-2 code and lets a genuine miss fall back to the manual form.

Field mapping. ACI returns streetName → street, houseNumber (a JSON number) → house number, houseNumberAddition → suffix, postalCode, cityName → city, region → state, lowercase countryIso → upper-cased country code. ACI returns no coordinates, so lat/lng stay null.

Installation

composer require silvershop-suite/address-lookup-postnl

Then run dev/build?flush=all.

API key

The provider authenticates with an apikey header and reads the key from the environment variable SS_POSTNL_API_KEY:

# .env
SS_POSTNL_API_KEY="your-postnl-api-key"

Without a key the provider is inert: isConfigured() is false, the registry skips it, and the checkout form stays manual — so the module is safe to install before credentials are supplied. The key is sent only in the server-side request header; it is never put in a URL/query string or exposed to the browser.

Configuration

Registration is applied automatically by _config/address-lookup-postnl.yml, which only registers the provider as available — it deliberately does not set by_country or default, so the base defaults (NL → pdok, DE → openplz, BE → myparcel) and any other installed satellite's routing (e.g. the Loqate satellite's global default) are left untouched.

SilverShop\AddressLookup\Provider\AddressLookupProviderRegistry:
  providers:
    postnl: '%$SilverShop\AddressLookup\PostNL\PostNLAddressProvider'

Opt PostNL in where you want it:

# Carrier-aligned NL: use PostNL for the Dutch postcode + house-number lookup instead of the free government PDOK.
SilverShop\AddressLookup\Provider\AddressLookupProviderRegistry:
  by_country:
    NL: postnl
# Or route specific international countries to PostNL's autocomplete.
SilverShop\AddressLookup\Provider\AddressLookupProviderRegistry:
  by_country:
    GB: postnl
    FR: postnl
# Or prefer PostNL globally (becomes the fallback for every country without a by_country entry).
SilverShop\AddressLookup\Provider\AddressLookupProviderRegistry:
  default: postnl

Provider options

SilverShop\AddressLookup\PostNL\PostNLAddressProvider:
  endpoint: 'https://api.postnl.nl/v1/address/autocomplete'  # override to pin a sandbox / mirror host
  api_key_env: 'SS_POSTNL_API_KEY'                           # the env var holding the key
  result_limit: 10                                           # max suggest candidates (trimmed client-side)
  timeout: 5                                                 # upstream timeout, seconds (inherited from the HTTP base)

Consent

PostNL is an EU carrier used to fulfil the order (own-logistics-adjacent), so the provider is functional/necessary and declares no consent category — it runs without a cookie gate (same posture as the base module's bundled providers and the MyParcel satellite, and unlike the consent-gated global Loqate satellite).

Hardening

Inherited from the base module's AbstractHttpProvider: SSL verify ON, a short upstream timeout, and a graceful empty answer on any miss or fault, so a slow or failing address API never blocks checkout. The lookup endpoint itself (POST-only, same-origin + CSRF, rate-limited, PII in the request body not the URL) lives in the base module.

Requirements

  • PHP ^8.3
  • silverstripe/framework ^6
  • silvershop/core ^6
  • silvershop-suite/address-lookup ^1
  • A PostNL API key (SS_POSTNL_API_KEY)

Pairs well with

License

BSD-3-Clause.